> ## Documentation Index
> Fetch the complete documentation index at: https://docs.solargenix.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Roof Layout API

This document describes the roof layout API used to generate a roof layout based on latitude and longitude coordinates. The API analyzes the roof geometry and returns data required to design and visualize solar panel installations and even more.

The API returns structured data that includes:

* Detected roof segments with boundary polygons
* Roof orientation data, including tilt and azimuth
* Candidate solar panel placements in portrait and landscape orientations
* Rooftop obstacle detection to avoid blocked areas
* Optional solar production metrics (flux values) for panel placement evaluation
* Normalized coordinates for rendering the layout on top of the provided map imagery

## Base URL

`https://api.solargenix.ai`

## Authentication

All requests to the roof layout API require an access token.

Send the token in the `Token` header:

```http theme={null}
Token: <your_access_token>
```

## Access Token Generation

Access tokens can be generated from the Developer page on the SolarGenix application website.

Token issuance is managed in the SolarGenix interface and is not exposed as a public API endpoint.

### Token generation flow

1. Sign in to the SolarGenix interface.
2. Open the **Developer** page.
3. Select **Generate Token**.
4. Copy the generated token and store it securely.
5. Use the token in the `Token` request header for all public API calls.

### Token handling requirements

* Store tokens on your server side only.
* Do not expose tokens in client-side JavaScript, mobile bundles, or public repositories.
* Rotate the token immediately if it is exposed.
* Use a different token for each environment when possible.

## Endpoint

### `POST /v1/layout/`

Generates a roof layout and candidate panel placements for the requested property.

Full URL:

```text theme={null}
https://api.solargenix.ai/v1/layout/
```

## Request Headers

| Header         | Required | Value              | Description                                                        |
| -------------- | -------- | ------------------ | ------------------------------------------------------------------ |
| `Token`        | Yes      | `string`           | Public API access token generated in the SolarGenix Developer page |
| `Content-Type` | Yes      | `application/json` | Request payload format                                             |

## Request Body

| Field                  | Type      | Required | Description                                                                        |
| ---------------------- | --------- | -------- | ---------------------------------------------------------------------------------- |
| `lat`                  | `number`  | Yes      | Latitude of the property                                                           |
| `lng`                  | `number`  | Yes      | Longitude of the property                                                          |
| `measurement_system`   | `string`  | Yes      | Unit system for all dimensional input values. Allowed values: `metric`, `imperial` |
| `panel_size_w`         | `number`  | Yes      | Panel width in the selected measurement system                                     |
| `panel_size_h`         | `number`  | Yes      | Panel height in the selected measurement system                                    |
| `setback`              | `object`  | Yes      | Required roof-edge setback configuration                                           |
| `setback.eave`         | `number`  | Yes      | Setback from eave edges                                                            |
| `setback.ridge`        | `number`  | Yes      | Setback from ridge edges                                                           |
| `setback.hipvalley`    | `number`  | Yes      | Setback from hip and valley edges                                                  |
| `setback.rake`         | `number`  | Yes      | Setback from rake edges                                                            |
| `panel_spacing`        | `number`  | Yes      | Spacing between adjacent panels in the selected measurement system                 |
| `calculate_production` | `boolean` | Yes      | When `true`, panel candidates include production-related values such as `flux`     |

## Measurement Rules

* When `measurement_system` is `metric`, all dimensional request values must be sent in meters.
* When `measurement_system` is `imperial`, all dimensional request values must be sent in feet.
* Use one system consistently for `panel_size_w`, `panel_size_h`, all `setback` values, and `panel_spacing`.

## Request Example

```bash theme={null}
curl --request POST "https://api.solargenix.ai/v1/layout/" \
  --header "Token: sgx_live_xxxxxxxxxxxxxxxxxxxx" \
  --header "Content-Type: application/json" \
  --data '{
    "lat": 33.1399,
    "lng": -117.1661,
    "measurement_system": "metric",
    "panel_size_w": 1.72,
    "panel_size_h": 1.13,
    "setback": {
      "eave": 0.3,
      "ridge": 0.45,
      "hipvalley": 0.45,
      "rake": 0.9
    },
    "panel_spacing": 0.04,
    "calculate_production": true
  }'
```

## JavaScript Example

```js theme={null}
const response = await fetch("https://api.solargenix.ai/v1/layout/", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Token: "sgx_live_xxxxxxxxxxxxxxxxxxxx",
  },
  body: JSON.stringify({
    lat: 33.1399,
    lng: -117.1661,
    measurement_system: "metric",
    panel_size_w: 1.72,
    panel_size_h: 1.13,
    setback: {
      eave: 0.3,
      ridge: 0.45,
      hipvalley: 0.45,
      rake: 0.9,
    },
    panel_spacing: 0.04,
    calculate_production: true,
  }),
});

const layout = await response.json();
```

## Success Response

The API returns a JSON object containing the source image and all detected roof geometry required to render candidate panel placements.

```json theme={null}
{
  "map_image": "https://app.solargenix.ai/v1/geoTiff:get?id=OTgyOTliMjhlMTNjN2RhYWQwYjI0N2Q1NDBmZGY3MTgtNmYzMTZmYTgzN2I4NmYxZGNhYzlmYzZiNDBlMzJkNTA6UkdCOkxPVw&key=AIzaSyB0wHitfUdbRICV2MG-_Az0SV1L-DRZwHU",
  "obstacles": [
    {
      "y": 0.6622347235679626,
      "x": 0.6092343330383301,
      "size": 0.002371065318584442
    },
    {
      "y": 0.5262547731399536,
      "x": 0.4840320944786072,
      "size": 0.005819257814437151
    }
  ],
  "panel_size": [
    0.04540977758705369,
    0.02983316783335504
  ],
  "roof_segments": [
    {
      "cycle": [
        { "y": 0.213, "x": 0.308 },
        { "y": 0.401, "x": 0.349 },
        { "y": 0.547, "x": 0.589 },
        { "y": 0.505, "x": 0.787 },
        { "y": 0.391, "x": 0.589 },
        { "y": 0.411, "x": 0.568 },
        { "y": 0.422, "x": 0.495 },
        { "y": 0.390, "x": 0.453 },
        { "y": 0.275, "x": 0.412 }
      ],
      "angle": -148.6864567298103,
      "azimuth": 31.925055,
      "tilt": 20.933521,
      "panels_portrait": [
        {
          "y": 0.4809831936078284,
          "x": 0.6885589416769791,
          "flux": 2758.5617769042965
        },
        {
          "y": 0.4649294207721041,
          "x": 0.6621691781114049,
          "flux": 2758.9307097656247
        }
      ],
      "panels_landscape": [
        {
          "y": 0.47785912967572686,
          "x": 0.7048290301834498,
          "flux": 2711.016651708984
        },
        {
          "y": 0.478417337821471,
          "x": 0.650101228497473,
          "flux": 2802.8145025390622
        }
      ]
    }
  ]
}
```

## Response Field Reference

### Root Object

| Field           | Type                    | Description                                                                            |
| --------------- | ----------------------- | -------------------------------------------------------------------------------------- |
| `map_image`     | `string`                | URL of the source map or imagery used for roof layout rendering                        |
| `obstacles`     | `array<object>`         | Detected rooftop obstructions that should be avoided during placement                  |
| `panel_size`    | `array<number, number>` | Normalized panel width and height used for rendering over `map_image`                  |
| `roof_segments` | `array<object>`         | Detected roof planes with boundaries, orientation data, and candidate panel placements |

### `obstacles[]`

| Field  | Type     | Description                                                         |
| ------ | -------- | ------------------------------------------------------------------- |
| `x`    | `number` | Horizontal position of the obstacle in normalized image coordinates |
| `y`    | `number` | Vertical position of the obstacle in normalized image coordinates   |
| `size` | `number` | Relative obstacle footprint size in normalized image coordinates    |

### `roof_segments[]`

| Field              | Type            | Description                                                          |
| ------------------ | --------------- | -------------------------------------------------------------------- |
| `cycle`            | `array<object>` | Closed polygon describing the roof segment boundary                  |
| `angle`            | `number`        | Rotation angle used for segment-aligned panel placement calculations |
| `azimuth`          | `number`        | Compass direction of the roof surface in degrees                     |
| `tilt`             | `number`        | Roof tilt in degrees                                                 |
| `panels_portrait`  | `array<object>` | Candidate panel centers for portrait orientation                     |
| `panels_landscape` | `array<object>` | Candidate panel centers for landscape orientation                    |

### `roof_segments[].cycle[]`

| Field | Type     | Description                                                             |
| ----- | -------- | ----------------------------------------------------------------------- |
| `x`   | `number` | Horizontal position of a boundary point in normalized image coordinates |
| `y`   | `number` | Vertical position of a boundary point in normalized image coordinates   |

### `roof_segments[].panels_portrait[]`

| Field  | Type     | Description                                                                                                 |
| ------ | -------- | ----------------------------------------------------------------------------------------------------------- |
| `x`    | `number` | Horizontal center position of the candidate panel in normalized image coordinates                           |
| `y`    | `number` | Vertical center position of the candidate panel in normalized image coordinates                             |
| `flux` | `number` | Production-related solar flux value for the panel candidate. Present when `calculate_production` is enabled |

### `roof_segments[].panels_landscape[]`

| Field  | Type     | Description                                                                                                 |
| ------ | -------- | ----------------------------------------------------------------------------------------------------------- |
| `x`    | `number` | Horizontal center position of the candidate panel in normalized image coordinates                           |
| `y`    | `number` | Vertical center position of the candidate panel in normalized image coordinates                             |
| `flux` | `number` | Production-related solar flux value for the panel candidate. Present when `calculate_production` is enabled |

## Coordinate System and Rendering Rules

The response uses normalized image coordinates for roof layout rendering.

* `x` and `y` values are relative to the image and typically fall in the range `0.0` to `1.0`.
* `panel_size` is also normalized and must be converted before rendering.
* `cycle`, `obstacles`, `panels_portrait`, and `panels_landscape` all use the same normalized coordinate system.

### Pixel conversion

To draw the result on top of the returned `map_image`, convert coordinates using the final rendered image dimensions:

```text theme={null}
x_px = x * image_width
y_px = y * image_height
panel_width_px = panel_size[0] * image_width
panel_height_px = panel_size[1] * image_height
```

### Important note

Do not treat response placement coordinates as raw pixels. They are normalized placement values intended to be scaled against the final image size.

## Common Implementation Notes

* Use `panels_portrait` when you want portrait panel candidate placement.
* Use `panels_landscape` when you want landscape panel candidate placement.
* Use `flux` to rank panel candidate placements when production scoring is enabled.
* Use `cycle` to draw roof segments.
* Use `obstacles` to block placements or visually mark non-usable rooftop areas.

## HTTP Status Expectations

| Status                      | Meaning                                                       |
| --------------------------- | ------------------------------------------------------------- |
| `200 OK`                    | Layout generated successfully                                 |
| `400 Bad Request`           | Request body is invalid or required values are missing        |
| `401 Unauthorized`          | `Token` header is missing or invalid                          |
| `422 Unprocessable Entity`  | The property could not be processed into a usable roof layout |
| `500 Internal Server Error` | Unexpected processing failure                                 |

## Example Error Responses

### Missing or invalid token

```json theme={null}
{
  "error": {
    "code": "invalid_token",
    "message": "The provided API token is missing, invalid, or expired."
  }
}
```

### Invalid request body

```json theme={null}
{
  "error": {
    "code": "validation_error",
    "message": "measurement_system must be one of: metric, imperial."
  }
}
```

### Layout could not be generated

```json theme={null}
{
  "error": {
    "code": "layout_unavailable",
    "message": "A usable roof layout could not be generated for the requested location."
  }
}
```
