Download OpenAPI specification:
Last updated: 2026-03-25
The HeatingControl External API allows external developers to integrate with the HeatingControl ecosystem. Through this API you can list homes, manage devices, read device state, update settings, and send commands.
This API is currently in DRAFT status. While in draft, breaking changes may still occur as the API design is being finalized. The draft period will end with the first official release.
After the first official release, this API may be extended at any time with non-breaking changes. Your client code must be designed to handle these gracefully and not break when encountering unknown data.
Non-breaking changes include but are not limited to:
- New fields added to response objects
- New values for existing string/enum fields
- New optional query parameters or request body fields
- New endpoints
- Relaxed validation on existing fields (e.g. accepting a wider range of values)
Breaking changes (removed fields, changed types, altered semantics) will only be introduced in a new API version (e.g.
/v2).As a best practice, ignore unknown fields rather than treating them as errors, and avoid strict schema validation that rejects unexpected properties.
The API uses OAuth2 client credentials flow.
POST /v1/oauth/token with your client_id and client_secret to receive an access token.Authorization header:Authorization: Bearer <access_token>
POST /v1/oauth/token again to obtain a new one before expiry.A typical integration follows this sequence:
GET /v1/homes to discover available homes.GET /v1/devices to enumerate devices (optionally filter by homeId).GET /v1/devices/{deviceId}/state to read current state.GET /v1/devices/{deviceId}/settings to read current settings.POST /v1/devices/{deviceId}/commands/{key} to control a device.This walkthrough shows a complete integration flow for managing electric heaters — from obtaining a token to reading state and sending commands. Replace YOUR_CLIENT_ID, YOUR_CLIENT_SECRET, and the base URL with your actual values.
curl -X POST https://api.example.com/v1/oauth/token \
-H "Content-Type: application/json" \
-d '{
"grant_type": "client_credentials",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET"
}'
Response:
{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"refresh_token": "dGhpcyBpcyBhIHJlZnJl...",
"expires_in": 3600,
"token_type": "Bearer"
}
Save the access_token — you will include it in every subsequent request as Authorization: Bearer <access_token>.
Discover the devices available in your account:
curl https://api.example.com/v1/devices \
-H "Authorization: Bearer <access_token>"
Response:
{
"items": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "Living Room Heater",
"type": "heater",
"subtype": "wifi_heater",
"homeId": "h_abc123",
"online": true,
"fwVersion": "2.1.0",
"hwVersion": "1.0",
"gatewayId": null
},
{
"id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"name": "Bedroom Heater",
"type": "heater",
"subtype": "wifi_heater",
"homeId": "h_abc123",
"online": true,
"fwVersion": "2.1.0",
"hwVersion": "1.0",
"gatewayId": null
}
],
"pagination": {
"limit": 50,
"cursor": null,
"nextCursor": null,
"hasMore": false
}
}
Note the id of each device — you will use it in the next steps.
Get the current state of the Living Room Heater, including temperature and power:
curl https://api.example.com/v1/devices/a1b2c3d4-e5f6-7890-abcd-ef1234567890/state \
-H "Authorization: Bearer <access_token>"
Response:
{
"type": "heater",
"online": true,
"mode": "auto",
"indicators": ["active"],
"currentTemperature": 20.5,
"targetTemperature": 22.0,
"temperatureUnits": "C",
"power": 1500,
"maxPower": 1500
}
Key fields:
mode — the current operating mode (e.g. auto, manual, off).power — current power consumption in watts (0 when the heater is not actively heating).maxPower — the rated maximum power of the heater in watts.Switch the Living Room Heater to manual mode at 21 °C. First, check which commands are available:
curl https://api.example.com/v1/devices/a1b2c3d4-e5f6-7890-abcd-ef1234567890/commands \
-H "Authorization: Bearer <access_token>"
Then execute the setMode command:
curl -X POST https://api.example.com/v1/devices/a1b2c3d4-e5f6-7890-abcd-ef1234567890/commands/setMode \
-H "Authorization: Bearer <access_token>" \
-H "Content-Type: application/json" \
-d '{
"params": {
"mode": "manual",
"temperature": 21.0
}
}'
Response:
{
"success": true,
"message": "Command executed successfully"
}
Check the Bedroom Heater to see if it is consuming power:
curl https://api.example.com/v1/devices/b2c3d4e5-f6a7-8901-bcde-f12345678901/state \
-H "Authorization: Bearer <access_token>"
Response:
{
"type": "heater",
"online": true,
"mode": "auto",
"indicators": [],
"currentTemperature": 22.1,
"targetTemperature": 22.0,
"temperatureUnits": "C",
"power": 0,
"maxPower": 2000
}
Here power is 0 because the room has already reached the target temperature and the heater is not actively heating. The maxPower of 2000 tells you this is a 2 kW heater.
View the configurable settings for a device:
curl https://api.example.com/v1/devices/a1b2c3d4-e5f6-7890-abcd-ef1234567890/settings \
-H "Authorization: Bearer <access_token>"
Response (abbreviated):
{
"temperature_units": {
"key": "temperature_units",
"type": "enum",
"label": "Temperature Units",
"value": "C",
"editable": true,
"options": ["C", "F"]
},
"keyboard_locked": {
"key": "keyboard_locked",
"type": "boolean",
"label": "Keyboard Lock",
"value": false,
"editable": true
}
}
You can update a specific setting with a PATCH request:
curl -X PATCH https://api.example.com/v1/devices/a1b2c3d4-e5f6-7890-abcd-ef1234567890/settings/keyboard_locked \
-H "Authorization: Bearer <access_token>" \
-H "Content-Type: application/json" \
-d '{ "value": true }'
List endpoints support cursor-based pagination:
| Parameter | Description |
|---|---|
limit |
Maximum number of items to return (default varies by endpoint). |
cursor |
Opaque cursor returned in the previous response to fetch the next page. |
When more results are available the response includes a cursor field. Pass it as a query parameter to retrieve the next page. When cursor is absent or null, you have reached the last page.
All error responses follow a standard envelope:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Human-readable description of the problem",
"requestId": "abc-123",
"details": {}
}
}
| Field | Description |
|---|---|
code |
Machine-readable error code (e.g. VALIDATION_ERROR, UNAUTHORIZED, NOT_FOUND). |
message |
Human-readable explanation. |
requestId |
Unique identifier for the request — useful when contacting support. |
details |
Optional object with additional context (e.g. field-level validation errors). |
| authorization required | string |
| grant_type required | string Enum: "client_credentials" "refresh_token" OAuth2 grant type |
| client_id | string Client identifier (required for client_credentials grant) |
| client_secret | string Client secret (required for client_credentials grant) |
| refresh_token | string Refresh token (required for refresh_token grant) |
| scope | string Requested permission scopes |
{- "grant_type": "client_credentials",
- "client_id": "string",
- "client_secret": "string",
- "refresh_token": "string",
- "scope": "/user:W /devs/*:W"
}{- "access_token": "eyJhbGciOiJIUzI1NiIs...",
- "refresh_token": "dGhpcyBpcyBhIHJlZnJl...",
- "expires_in": 3600,
- "token_type": "Bearer"
}| limit | number [ 1 .. 100 ] Default: 20 Maximum number of homes to return |
| cursor | string Cursor from a previous response to fetch the next page |
{- "items": [
- {
- "id": "aabbccdd-eeff-0011-2233-445500000000",
- "name": "My Home",
- "deviceCount": 3,
- "location": {
- "country": "ES",
- "state": "Catalonia",
- "city": "Barcelona",
- "timeZone": "Europe/Madrid",
- "latitude": 41.3851,
- "longitude": 2.1734,
- "geofenceRadius": 200
}
}
], - "pagination": {
- "limit": 20,
- "cursor": null,
- "nextCursor": null,
- "hasMore": false
}
}{- "id": "aabbccdd-eeff-0011-2233-445500000000",
- "name": "My Home",
- "deviceCount": 3,
- "location": {
- "country": "ES",
- "state": "Catalonia",
- "city": "Barcelona",
- "timeZone": "Europe/Madrid",
- "latitude": 41.3851,
- "longitude": 2.1734,
- "geofenceRadius": 200
}
}| limit | number [ 1 .. 100 ] Default: 20 Maximum number of devices to return |
| cursor | string Cursor from a previous response to fetch the next page |
| homeId | string Filter by home UUID |
| gatewayId | string Filter devices by gateway UUID. Returns only devices connected through this gateway. |
| includeGateways | boolean Default: false Include gateway devices in the response |
{- "items": [
- {
- "id": "0123456789abcdef01-0001-0000-0000-000000000000",
- "name": "Living Room Heater",
- "type": "heater",
- "homeId": "aabbccdd-eeff-0011-2233-445500000000",
- "online": true,
- "subtype": "wifi_heater",
- "fwVersion": "1.3",
- "hwVersion": "3.0",
- "gatewayId": null
}
], - "pagination": {
- "limit": 20,
- "cursor": null,
- "nextCursor": null,
- "hasMore": false
}
}{- "id": "0123456789abcdef01-0001-0000-0000-000000000000",
- "name": "Living Room Heater",
- "type": "heater",
- "homeId": "aabbccdd-eeff-0011-2233-445500000000",
- "online": true,
- "subtype": "wifi_heater",
- "fwVersion": "1.3",
- "hwVersion": "3.0",
- "gatewayId": null
}| deviceId required | string <uuid> |
| name | string New display name for the device |
{- "name": "Bedroom Heater"
}{- "id": "0123456789abcdef01-0001-0000-0000-000000000000",
- "name": "Living Room Heater",
- "type": "heater",
- "homeId": "aabbccdd-eeff-0011-2233-445500000000",
- "online": true,
- "subtype": "wifi_heater",
- "fwVersion": "1.3",
- "hwVersion": "3.0",
- "gatewayId": null
}{- "type": "heater",
- "online": true,
- "mode": "auto",
- "indicators": [
- "active"
], - "currentTemperature": 21.5,
- "targetTemperature": 22,
- "temperatureUnits": "C",
- "chargeLevel": 75,
- "power": 1500,
- "maxPower": 2000
}{- "property1": {
- "type": "boolean",
- "key": "locked",
- "label": "Keyboard Lock Enabled",
- "value": false
}, - "property2": {
- "type": "boolean",
- "key": "locked",
- "label": "Keyboard Lock Enabled",
- "value": false
}
}| deviceId required | string <uuid> |
| key required | string |
| value required | boolean New boolean value |
{- "value": false
}{- "type": "boolean",
- "key": "locked",
- "label": "Keyboard Lock Enabled",
- "value": false
}{- "property1": {
- "params": {
- "property1": {
- "type": "number",
- "required": true,
- "constraints": {
- "min": 5,
- "max": 35,
- "step": 0.5
}
}, - "property2": {
- "type": "number",
- "required": true,
- "constraints": {
- "min": 5,
- "max": 35,
- "step": 0.5
}
}
}
}, - "property2": {
- "params": {
- "property1": {
- "type": "number",
- "required": true,
- "constraints": {
- "min": 5,
- "max": 35,
- "step": 0.5
}
}, - "property2": {
- "type": "number",
- "required": true,
- "constraints": {
- "min": 5,
- "max": 35,
- "step": 0.5
}
}
}
}
}| deviceId required | string <uuid> |
| key required | string |
object Command parameters. Use GET /devices/:id/commands to discover the expected parameter schema for each command. |
{- "params": {
- "mode": "auto"
}
}{- "command": "setMode",
- "state": "APPLIED",
- "appliedAt": "2026-03-18T12:00:00.000Z"
}