{
  "openapi": "3.0.3",
  "info": {
    "title": "H.E.A.T. Cloud API",
    "version": "1.0.0"
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT"
      }
    },
    "schemas": {
      "ErrorResponse": {
        "additionalProperties": false,
        "example": {
          "error": "not_found",
          "message": "The requested resource was not found"
        },
        "type": "object",
        "required": [
          "error",
          "message"
        ],
        "properties": {
          "error": {
            "type": "string"
          },
          "message": {
            "type": "string"
          }
        }
      },
      "ServerError": {
        "additionalProperties": false,
        "example": {
          "error": "internal_server_error",
          "message": "An unexpected error occurred"
        },
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string"
          },
          "message": {
            "type": "string"
          }
        }
      },
      "Site": {
        "additionalProperties": false,
        "example": {
          "siteId": "123e4567-e89b-12d3-a456-426614174000",
          "siteName": "Site 1",
          "isOnline": true,
          "reportedAt": "2025-06-01T08:00:00Z",
          "schedules": [
            {
              "asset": "grid",
              "type": "upperLimitkW",
              "entries": [
                {
                  "startAt": "2026-06-01T08:00:00Z",
                  "endAt": "2026-06-01T10:00:00Z",
                  "value": 50
                }
              ]
            },
            {
              "asset": "grid",
              "type": "lowerLimitkW",
              "entries": [
                {
                  "startAt": "2026-06-01T08:00:00Z",
                  "endAt": "2026-06-01T10:00:00Z",
                  "value": -20
                }
              ]
            },
            {
              "asset": "grid",
              "type": "activePowerkW",
              "entries": [
                {
                  "startAt": "2026-06-01T10:00:00Z",
                  "endAt": "2026-06-01T12:00:00Z",
                  "value": 30
                }
              ]
            },
            {
              "asset": "bess",
              "type": "activePowerkW",
              "entries": [
                {
                  "startAt": "2026-06-01T08:00:00Z",
                  "endAt": "2026-06-01T10:00:00Z",
                  "value": -50
                }
              ]
            },
            {
              "asset": "bess",
              "type": "optimalSocPcnt",
              "entries": [
                {
                  "startAt": "2026-06-01T10:00:00Z",
                  "endAt": "2026-06-01T12:00:00Z",
                  "value": 80
                }
              ]
            },
            {
              "asset": "solar",
              "type": "solarCurtailmentPcnt",
              "entries": [
                {
                  "startAt": "2026-06-01T08:00:00Z",
                  "endAt": "2026-06-01T12:00:00Z",
                  "value": 10
                }
              ]
            }
          ]
        },
        "type": "object",
        "required": [
          "siteId",
          "siteName",
          "isOnline",
          "schedules",
          "reportedAt"
        ],
        "properties": {
          "siteId": {
            "format": "uuid",
            "description": "Unique site identifier in UUID format",
            "type": "string"
          },
          "siteName": {
            "description": "Human-readable name of the site",
            "type": "string"
          },
          "isOnline": {
            "description": "Connectivity status",
            "type": "boolean"
          },
          "schedules": {
            "description": "List of schedules currently active on site",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AssetScheduleCommand"
            }
          },
          "reportedAt": {
            "type": "string",
            "nullable": true,
            "format": "date-time",
            "description": "Timestamp of the last healthcheck received from the controller, or null if it has never reported"
          }
        }
      },
      "BessAssetGroup": {
        "additionalProperties": false,
        "type": "object",
        "required": [
          "id",
          "ratedPowerUpperLimitkW",
          "ratedPowerLowerLimitkW",
          "type",
          "availableStorageCapacitykWh",
          "soc"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "ratedPowerUpperLimitkW": {
            "description": "Available rated power upper limit",
            "type": "number"
          },
          "ratedPowerLowerLimitkW": {
            "description": "Available rated power lower limit",
            "type": "number"
          },
          "activePowerkW": {
            "description": "Asset group active power",
            "type": "number"
          },
          "type": {
            "type": "string",
            "enum": [
              "bess"
            ]
          },
          "availableStorageCapacitykWh": {
            "type": "number",
            "nullable": true,
            "description": "Available storage capacity in kWh"
          },
          "soc": {
            "type": "number",
            "nullable": true,
            "description": "State of charge as a percentage (0–100)"
          }
        }
      },
      "BaseAssetGroup": {
        "additionalProperties": false,
        "type": "object",
        "required": [
          "id",
          "ratedPowerUpperLimitkW",
          "ratedPowerLowerLimitkW",
          "type"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "ratedPowerUpperLimitkW": {
            "description": "Available rated power upper limit",
            "type": "number"
          },
          "ratedPowerLowerLimitkW": {
            "description": "Available rated power lower limit",
            "type": "number"
          },
          "activePowerkW": {
            "description": "Asset group active power",
            "type": "number"
          },
          "type": {
            "type": "string",
            "enum": [
              "load",
              "heatpump",
              "genset",
              "ev",
              "grid",
              "solar",
              "wind"
            ],
            "description": "Type of asset group"
          }
        }
      },
      "Assets": {
        "additionalProperties": false,
        "type": "object",
        "required": [
          "grid",
          "bess",
          "load",
          "solar",
          "ev",
          "heatpump",
          "genset",
          "wind"
        ],
        "properties": {
          "grid": {
            "nullable": true,
            "allOf": [
              {
                "$ref": "#/components/schemas/BaseAssetGroup"
              }
            ]
          },
          "bess": {
            "nullable": true,
            "allOf": [
              {
                "$ref": "#/components/schemas/BessAssetGroup"
              }
            ]
          },
          "load": {
            "nullable": true,
            "allOf": [
              {
                "$ref": "#/components/schemas/BaseAssetGroup"
              }
            ]
          },
          "solar": {
            "nullable": true,
            "allOf": [
              {
                "$ref": "#/components/schemas/BaseAssetGroup"
              }
            ]
          },
          "ev": {
            "nullable": true,
            "allOf": [
              {
                "$ref": "#/components/schemas/BaseAssetGroup"
              }
            ]
          },
          "heatpump": {
            "nullable": true,
            "allOf": [
              {
                "$ref": "#/components/schemas/BaseAssetGroup"
              }
            ]
          },
          "genset": {
            "nullable": true,
            "allOf": [
              {
                "$ref": "#/components/schemas/BaseAssetGroup"
              }
            ]
          },
          "wind": {
            "nullable": true,
            "allOf": [
              {
                "$ref": "#/components/schemas/BaseAssetGroup"
              }
            ]
          }
        }
      },
      "ScheduleEntry": {
        "additionalProperties": false,
        "type": "object",
        "required": [
          "startAt",
          "endAt",
          "value"
        ],
        "properties": {
          "startAt": {
            "format": "date-time",
            "description": "Entry start time in ISO 8601 format",
            "type": "string"
          },
          "endAt": {
            "format": "date-time",
            "description": "Entry end time in ISO 8601 format",
            "type": "string"
          },
          "value": {
            "description": "Numeric value; unit and sign depend on asset and schedule type",
            "type": "number"
          }
        }
      },
      "AssetScheduleCommand": {
        "additionalProperties": false,
        "example": {
          "asset": "grid",
          "type": "upperLimitkW",
          "entries": [
            {
              "startAt": "2026-06-01T08:00:00Z",
              "endAt": "2026-06-01T10:00:00Z",
              "value": 50
            }
          ]
        },
        "type": "object",
        "required": [
          "asset",
          "type",
          "entries"
        ],
        "properties": {
          "asset": {
            "type": "string",
            "enum": [
              "load",
              "heatpump",
              "genset",
              "ev",
              "grid",
              "solar",
              "bess",
              "wind"
            ],
            "description": "Type of asset group"
          },
          "type": {
            "type": "string",
            "enum": [
              "upperLimitkW",
              "lowerLimitkW",
              "activePowerkW",
              "optimalSocPcnt",
              "solarCurtailmentPcnt"
            ],
            "description": "Schedule command type. `upperLimitkW` and `lowerLimitkW` are signed: positive = import, negative = export. For BESS assets, `activePowerkW` and `optimalSocPcnt` are mutually exclusive."
          },
          "entries": {
            "description": "Ordered list of time-range entries for this asset/type pair",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ScheduleEntry"
            }
          }
        }
      },
      "CreateSchedulesBatchResponse": {
        "additionalProperties": false,
        "type": "object",
        "required": [
          "batchId",
          "accepted"
        ],
        "properties": {
          "batchId": {
            "format": "uuid",
            "description": "Unique identifier for the created schedule batch",
            "type": "string"
          },
          "accepted": {
            "description": "Number of schedule commands accepted into the batch",
            "type": "number"
          }
        }
      },
      "DeviceTreeNode": {
        "type": "object",
        "required": [
          "uid",
          "node_type",
          "device_type",
          "children"
        ],
        "properties": {
          "uid": {
            "type": "string"
          },
          "node_type": {
            "type": "string",
            "enum": [
              "Dispatcher",
              "DeviceGroup",
              "Device",
              "DeviceElement",
              "SubGrid"
            ]
          },
          "name": {
            "type": "string"
          },
          "device_type": {
            "type": "string"
          },
          "data_source": {
            "type": "string"
          },
          "rated_power_upper_limit_kW": {
            "type": "number"
          },
          "rated_power_lower_limit_kW": {
            "type": "number"
          },
          "available_storage_capacity_kWh": {
            "type": "number",
            "nullable": true
          },
          "soc": {
            "type": "number",
            "nullable": true
          },
          "active_power_kw": {
            "type": "number"
          },
          "rated_energy": {
            "type": "number"
          },
          "children": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DeviceTreeNode"
            }
          }
        }
      }
    }
  },
  "paths": {
    "/api/v1/public/sites": {
      "get": {
        "operationId": "listSites",
        "summary": "Get all sites",
        "tags": [
          "Sites"
        ],
        "description": "Get all available sites with some information on status included.",
        "responses": {
          "200": {
            "description": "Default Response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "sites"
                  ],
                  "properties": {
                    "sites": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Site"
                      }
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "Default Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServerError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/public/sites/{siteId}": {
      "get": {
        "operationId": "getSite",
        "summary": "Get site details",
        "tags": [
          "Sites"
        ],
        "description": "Get all availabe details for a given site.",
        "parameters": [
          {
            "schema": {
              "format": "uuid",
              "type": "string"
            },
            "in": "path",
            "name": "siteId",
            "required": true,
            "description": "UUID of the site to retrieve"
          }
        ],
        "responses": {
          "200": {
            "description": "Default Response",
            "content": {
              "application/json": {
                "schema": {
                  "additionalProperties": false,
                  "example": {
                    "siteId": "123e4567-e89b-12d3-a456-426614174000",
                    "siteName": "Site 1",
                    "isOnline": true,
                    "reportedAt": "2025-06-01T08:00:00Z",
                    "schedules": [
                      {
                        "asset": "grid",
                        "type": "upperLimitkW",
                        "entries": [
                          {
                            "startAt": "2026-06-01T08:00:00Z",
                            "endAt": "2026-06-01T10:00:00Z",
                            "value": 50
                          }
                        ]
                      },
                      {
                        "asset": "grid",
                        "type": "lowerLimitkW",
                        "entries": [
                          {
                            "startAt": "2026-06-01T08:00:00Z",
                            "endAt": "2026-06-01T10:00:00Z",
                            "value": -20
                          }
                        ]
                      },
                      {
                        "asset": "grid",
                        "type": "activePowerkW",
                        "entries": [
                          {
                            "startAt": "2026-06-01T10:00:00Z",
                            "endAt": "2026-06-01T12:00:00Z",
                            "value": 30
                          }
                        ]
                      },
                      {
                        "asset": "bess",
                        "type": "activePowerkW",
                        "entries": [
                          {
                            "startAt": "2026-06-01T08:00:00Z",
                            "endAt": "2026-06-01T10:00:00Z",
                            "value": -50
                          }
                        ]
                      },
                      {
                        "asset": "bess",
                        "type": "optimalSocPcnt",
                        "entries": [
                          {
                            "startAt": "2026-06-01T10:00:00Z",
                            "endAt": "2026-06-01T12:00:00Z",
                            "value": 80
                          }
                        ]
                      },
                      {
                        "asset": "solar",
                        "type": "solarCurtailmentPcnt",
                        "entries": [
                          {
                            "startAt": "2026-06-01T08:00:00Z",
                            "endAt": "2026-06-01T12:00:00Z",
                            "value": 10
                          }
                        ]
                      }
                    ],
                    "assets": {
                      "grid": {
                        "id": "0-grid",
                        "type": "grid",
                        "ratedPowerUpperLimitkW": 500,
                        "ratedPowerLowerLimitkW": -500,
                        "activePowerkW": 120
                      },
                      "solar": {
                        "id": "0-solar",
                        "type": "solar",
                        "ratedPowerUpperLimitkW": 200,
                        "ratedPowerLowerLimitkW": 0,
                        "activePowerkW": 150
                      },
                      "bess": {
                        "id": "0-bess",
                        "type": "bess",
                        "ratedPowerUpperLimitkW": 100,
                        "ratedPowerLowerLimitkW": -100,
                        "activePowerkW": 30,
                        "availableStorageCapacitykWh": 500,
                        "soc": 75
                      },
                      "ev": {
                        "id": "0-ev",
                        "type": "ev",
                        "ratedPowerUpperLimitkW": 50,
                        "ratedPowerLowerLimitkW": 0,
                        "activePowerkW": 22
                      },
                      "heatpump": {
                        "id": "0-heatpump",
                        "type": "heatpump",
                        "ratedPowerUpperLimitkW": 20,
                        "ratedPowerLowerLimitkW": 0,
                        "activePowerkW": 10
                      },
                      "genset": {
                        "id": "0-genset",
                        "type": "genset",
                        "ratedPowerUpperLimitkW": 300,
                        "ratedPowerLowerLimitkW": 0,
                        "activePowerkW": 0
                      },
                      "load": {
                        "id": "0-load",
                        "type": "load",
                        "ratedPowerUpperLimitkW": 400,
                        "ratedPowerLowerLimitkW": 0,
                        "activePowerkW": 250
                      }
                    }
                  },
                  "type": "object",
                  "required": [
                    "siteId",
                    "siteName",
                    "isOnline",
                    "schedules",
                    "reportedAt",
                    "assets"
                  ],
                  "properties": {
                    "siteId": {
                      "format": "uuid",
                      "description": "Unique site identifier in UUID format",
                      "type": "string"
                    },
                    "siteName": {
                      "description": "Human-readable name of the site",
                      "type": "string"
                    },
                    "isOnline": {
                      "description": "Connectivity status",
                      "type": "boolean"
                    },
                    "schedules": {
                      "description": "List of schedules currently active on site",
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AssetScheduleCommand"
                      }
                    },
                    "reportedAt": {
                      "type": "string",
                      "nullable": true,
                      "format": "date-time",
                      "description": "Timestamp of the last healthcheck received from the controller, or null if it has never reported"
                    },
                    "assets": {
                      "$ref": "#/components/schemas/Assets"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Default Response",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    }
                  ],
                  "example": {
                    "error": "not_found",
                    "message": "Site not found"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Default Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServerError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/public/sites/{siteId}/schedules": {
      "post": {
        "operationId": "createSchedules",
        "summary": "Set schedules",
        "tags": [
          "Sites"
        ],
        "description": "Set all scheduled commands for a site. See the schedules guide for detailed documentation.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "additionalProperties": false,
                "example": {
                  "schedules": [
                    {
                      "asset": "grid",
                      "type": "upperLimitkW",
                      "entries": [
                        {
                          "startAt": "2026-06-01T08:00:00Z",
                          "endAt": "2026-06-01T10:00:00Z",
                          "value": 50
                        }
                      ]
                    },
                    {
                      "asset": "grid",
                      "type": "lowerLimitkW",
                      "entries": [
                        {
                          "startAt": "2026-06-01T08:00:00Z",
                          "endAt": "2026-06-01T10:00:00Z",
                          "value": -20
                        }
                      ]
                    },
                    {
                      "asset": "grid",
                      "type": "activePowerkW",
                      "entries": [
                        {
                          "startAt": "2026-06-01T10:00:00Z",
                          "endAt": "2026-06-01T12:00:00Z",
                          "value": 30
                        }
                      ]
                    },
                    {
                      "asset": "bess",
                      "type": "activePowerkW",
                      "entries": [
                        {
                          "startAt": "2026-06-01T08:00:00Z",
                          "endAt": "2026-06-01T10:00:00Z",
                          "value": -50
                        }
                      ]
                    },
                    {
                      "asset": "bess",
                      "type": "optimalSocPcnt",
                      "entries": [
                        {
                          "startAt": "2026-06-01T10:00:00Z",
                          "endAt": "2026-06-01T12:00:00Z",
                          "value": 80
                        }
                      ]
                    },
                    {
                      "asset": "solar",
                      "type": "solarCurtailmentPcnt",
                      "entries": [
                        {
                          "startAt": "2026-06-01T08:00:00Z",
                          "endAt": "2026-06-01T12:00:00Z",
                          "value": 10
                        }
                      ]
                    }
                  ]
                },
                "type": "object",
                "required": [
                  "schedules"
                ],
                "properties": {
                  "schedules": {
                    "minItems": 1,
                    "maxItems": 10,
                    "description": "List of asset schedule commands. Each asset/type combination must appear at most once.",
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/AssetScheduleCommand"
                    }
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "schema": {
              "format": "uuid",
              "type": "string"
            },
            "in": "path",
            "name": "siteId",
            "required": true,
            "description": "UUID of the target site"
          }
        ],
        "responses": {
          "201": {
            "description": "Default Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateSchedulesBatchResponse"
                }
              }
            }
          },
          "400": {
            "description": "Default Response",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    }
                  ],
                  "example": {
                    "error": "bad_request",
                    "message": "Duplicate asset/type combination in schedule batch"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Default Response",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    }
                  ],
                  "example": {
                    "error": "unauthorized",
                    "message": "Missing or invalid API key"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Default Response",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    }
                  ],
                  "example": {
                    "error": "forbidden",
                    "message": "API key does not have access to this site"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Default Response",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    }
                  ],
                  "example": {
                    "error": "not_found",
                    "message": "Site not found"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Default Response",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ServerError"
                    }
                  ],
                  "example": {
                    "error": "internal_server_error",
                    "message": "Failed to create schedule batch"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/public/health": {
      "get": {
        "operationId": "getHealth",
        "summary": "Get API health status",
        "tags": [
          "Health"
        ],
        "description": "Returns 204 when the service is operational, 503 when unavailable. Request timeouts should also be treated as service unavailability.",
        "security": [],
        "responses": {
          "204": {
            "description": "Service is operational"
          },
          "503": {
            "description": "Default Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServerError"
                }
              }
            }
          }
        }
      }
    }
  },
  "servers": [
    {
      "url": "https://cloud.heat-solutions.com"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ]
}
