API reference
Reading the reference
Every operation and schema below is generated from the gateway's own types, so it is exactly what the gateway serves. The documents themselves: api/openapi.json (OpenAPI 3.1, HTTPS) and api/asyncapi.json (AsyncAPI 3.0, the WebSocket), both with JSON Schema 2020-12. Every operation can also answer a refusal in the Error shape (Errors), and every successful answer names the house in house.
Operations marked "No credential needed" answer without Authorization; all others need a key or an agent credential (Authentication and Accept agent).
Operations
GET /v1/about
about — How to connect, and the app steps (static text).
No credential needed. Guide: Agents.
GET /v1/activity
activity — The activity log of exposed items (?device&from&to&limit).
Guide: Sensors, energy and activity.
GET /v1/automations
automations — Automations whose references are all exposed (automations: on).
Guide: Automations and schedules.
POST /v1/automations
createAutomation — Create an automation (the app's schema).
Body: Object.
Guide: Automations and schedules.
GET /v1/automations/{id}
automation — One automation.
PATCH /v1/automations/{id}
editAutomation — Edit an automation (ifRevision; PUT does the same).
Body: Object.
Guide: Automations and schedules.
GET /v1/devices/{id}
device — One unit, module, gang or scene.
Guide: The house.
PATCH /v1/devices/{id}
renameDevice — Rename a unit or gang (structural: on).
Body: Rename.
Guide: Renaming rooms and units.
POST /v1/devices/{id}/control
control — One operation: set state, unit mode (set_mode), run a scene.
POST /v1/devices/{id}/control { op, args } (POST /v1/devices/{id}/control) runs one operation. op must be one of the device's operations from GET /v1/house; args depends on it (Control):
| Operation | Args |
|---|---|
set_power |
{ "on": true } |
set_brightness |
{ "bri": 0–100 } |
set_position |
{ "pos": 0–100 } (shutters, curtains) |
open, close, stop |
none |
set_mode |
{ "mode": "auto" | "eco" | "manual" } (the unit's mode; sleep is not available) |
set_temp, set_ac_mode, set_fan |
{ "temp": 24 }, { "mode": "cool" }, { "fan": "auto" } |
key |
{ "key": "<remote key>" } (IR remotes) |
run |
none (a scene; also POST /v1/scenes/{id}/run, POST /v1/scenes/{id}/run) |
- Values out of range are
422validation. - Locks and gates: unlocking or opening needs
allow_unlock: on(else403unlock_not_allowed) and, for an agent, the user's explicit confirmation every time. - A unit that doesn't answer over IGHC is
504unit_unreachable; when the gateway's IGHC adapter can't carry frames,503ighc_unavailable. - Every change is logged with the credential's name (see
GET /v1/activity).
Body: Control.
Guide: Controlling devices.
GET /v1/devices/{id}/explain
explainDevice — Why the device did what it did (?from&to).
Guide: Explain.
GET /v1/devices/{id}/intents
intents — The device's configuration intents with their cached values.
A unit's configuration is offered as intents: what a user wants ("one touch turns it on and off", "turn off after no motion"), never the unit's menus. Each intent (IntentDef) has an id, a meaning, when (not) to use it, prerequisites, side effects, the errors a change can get, examples, its value shape, and its access:
- read_write — changed with
PUT /v1/devices/{id}/intents/{intent} { value }(PUT /v1/devices/{id}/intents/{intent}) when the integration hasadvanced_config: read_write. The gateway sends the frames, and the new value is reported only after the unit confirmed it. - read_only — shown from the cache with
guidanceand anappActionnaming the app screen that changes it.
GET /v1/devices/{id}/intents lists the intents of that unit's model (or of one gang) with each value as { value, source: "cache", asOf }; GET /v1/intents the whole table.
advanced_config: off: every intent call is403config_not_allowed;read: every change is.- A read-only intent, and resetting a unit, are
403not_offered. - A setting packed with others in one register needs a cached value to change: without one it is
409not_cachedwithappAction: unit.menus(unit.menus): the user reads the unit's menu in the app once. - Unit mode (Auto, Eco, Manual) is a control operation,
set_mode(seePOST /v1/devices/{id}/control), not an intent.
The intents, one by one, follow.
Guide: Cached unit data.
PUT /v1/devices/{id}/intents/{intent}
setIntent — Change a writable intent (advanced_config: read_write).
Body: IntentValue.
Guide: Intents.
GET /v1/devices/{id}/schedules
unitSchedules — The unit's own schedules as last read (read-only).
Guide: Automations and schedules.
GET /v1/energy
energy — Energy and cost (?scope=house|room:<id>|floor:<id>|device:<id>&period|from&to).
Guide: Sensors, energy and activity.
POST /v1/explain
explain — Why a device did what it did.
GET /v1/devices/{id}/explain and POST /v1/explain { device, from?, to? } (POST /v1/explain) answer the same: everything that may explain what a device did between from and to (epoch milliseconds; the last 24 hours by default):
- activity log entries for the device, each with its cause (a person, an automation, a schedule, an integration);
- automations and gateway schedules that target it;
- the unit's own (cached) schedules, its auto or eco mode and touch settings;
- sensors in the same room that triggered;
- integrations (the API, Google, Alexa, Matter, HomeKit, Home Assistant) that sent it commands.
Every item says whether it is live (the gateway's own log and objects) or cached (what the unit last confirmed). Only exposed items appear. Word cached items as "based on the last available data".
Body: Explain.
Guide: Explain.
GET /v1/gateway/tls
tls — The gateway's local CA (PEM) and its SHA-256 fingerprint, to pin.
On the home network the gateway serves HTTPS (TLS 1.3 only) with a server certificate signed by its own certificate authority, made once on the gateway (valid about 20 years; its private key never leaves the gateway). The server certificate names igh-<serial>.local, igh-<serial> and the gateway's current LAN addresses, lasts about a year and is renewed by itself. Clients trust only that CA for that gateway: pin it.
Never turn off certificate verification (curl -k, verify=False, rejectUnauthorized: false, InsecureSkipVerify). There is no plain-HTTP fallback.
Getting the CA. Either:
- from the integration's page in the app: the CA's fingerprint and a download of its file; or
GET /v1/gateway/tlsthroughhttps://api.igh.app(publicly trusted TLS):{ ca, fingerprint, algorithm: "SHA-256", createdAt, house }.cais the PEM certificate;fingerprintis the SHA-256 of its DER bytes, upper-case hex pairs joined by colons.
Check that the fingerprint you computed from ca equals fingerprint, store the PEM next to the credential, and use it as the only trust anchor for that gateway.
On a certificate error (unknown issuer, wrong name): fetch the CA again the same way. If its fingerprint differs from the one you pinned, tell the user before trusting it: an administrator may have regenerated the CA on the integration's page (integrations.record), or something is impersonating the gateway. Never accept a changed CA silently. A name mismatch after the gateway moved to another address heals by itself within minutes (the certificate is reissued); use igh-<serial>.local meanwhile.
Guide: Getting started.
GET /v1/house
house — Floors, rooms, units (modules with their gangs), scenes; live state and operations.
GET /v1/house answers the home as the integration shows it: Floors → Rooms → units, plus rooms on no floor, units in no room and the scenes. Each unit and gang carries its id, name, class, kind, model, room, floor, operations and its current state (live). A module carries no state of its own; its gangs do.
GET /v1/state: every exposed unit's and gang's state now, as{ devices: [{ id, name, class, state }] }; the cheap way to poll (but prefer Events (WebSocket)).GET /v1/devices/{id}: one unit, module, gang or scene.
Call GET /v1/house first and keep the ids: every other call takes them. Something not exposed is simply absent, and asking for it is 404 not_found.
Answer: House.
Guide: Getting started.
GET /v1/integration
integration — The integration this credential belongs to, the house, and its settings.
Guide: Concepts.
GET /v1/intents
intentTable — Every intent's definition.
Answer: IntentDef.
Guide: Intents.
PATCH /v1/rooms/{id}
renameRoom — Rename a room inside the exposure (structural: on).
Body: Rename.
Guide: Renaming rooms and units.
GET /v1/scenes
scenes — The exposed scenes, their members and whether they run.
GET /v1/scenes: the exposed scenes, their members (as device ids, plus how many members this integration doesn't show) and whether they run.POST /v1/scenes/{id}/runruns one.POST /v1/scenescreates one in the app's scene schema (SceneBody): itsstepsname only exposed devices. On a Selected integration the new scene joins the selection. It is marked as created by this integration.PATCH /v1/scenes/{id}changes one; send therevisionyou read asifRevision, or a change made meanwhile is refused with409revision_mismatch: read it again and redo the edit.
Deleting a scene is done in the app.
Guide: Scenes.
POST /v1/scenes
createScene — Create a scene (it joins a Selected integration's selection).
Body: SceneBody.
Guide: Scenes.
GET /v1/scenes/{id}
scene — One scene.
Guide: Scenes.
PATCH /v1/scenes/{id}
editScene — Edit a scene (ifRevision).
Body: SceneBody.
Guide: Scenes.
POST /v1/scenes/{id}/run
runScene — Run a scene.
Guide: Controlling devices.
GET /v1/schedules
schedules — The gateway's schedules whose references are all exposed (schedules: on).
Guide: Automations and schedules.
POST /v1/schedules
createSchedule — Create a gateway schedule.
Body: Object.
Guide: Automations and schedules.
GET /v1/schedules/{id}
schedule — One gateway schedule.
PATCH /v1/schedules/{id}
editSchedule — Edit a gateway schedule (ifRevision; PUT does the same).
Body: Object.
Guide: Automations and schedules.
GET /v1/sensors
sensors — Every exposed sensor's readings.
Guide: Sensors, energy and activity.
GET /v1/sensors/{id}/detections
detections — A sensor's detections (?cursor&limit).
Guide: Sensors, energy and activity.
GET /v1/state
state — Every exposed unit's and gang's state now.
Guide: The house.
Schemas
Access
AgentRequest
The args of api_request: one agent asking for access.
| Field | Type | Required | Description |
|---|---|---|---|
code |
string | yes | The matching code the agent shows ("KQ7-4M"). |
expiresAt |
integer | yes | When the cloud lets it go (epoch ms), not counting pauses. |
hosted |
boolean | ||
name |
string | yes | |
platform |
string | yes | |
record |
string or null | Fixed by a per-record URL. | |
request |
string | yes | |
suggested |
any | The scope the agent suggests (exposure and knob values). |
ClientMessage
One client message: a request (method and path, answered { id, status, body })
or subscribe (the topics to receive from now on).
| Field | Type | Required | Description |
|---|---|---|---|
body |
any | ||
id |
string or null | Echoed in the answer. | |
method |
string or null | ||
path |
string or null | From /v1, as on HTTPS. |
|
query |
object | ||
subscribe |
array or null |
Control
POST /v1/devices/{id}/control: one operation and its arguments.
| Field | Type | Required | Description |
|---|---|---|---|
args |
any | The operation's arguments: set_power {on}, set_brightness {bri: 0–100}, set_position {pos: 0–100}, set_mode {mode: auto|eco|manual|sleep}, set_temp {temp}, set_ac_mode {mode}, set_fan {fan}, key {key}, … |
|
op |
string | yes | One of the device's operations (set_power, set_brightness, open, set_mode, run, …). |
Error
Every refusal, on the home network, the relay, the WebSocket and MCP alike, has one shape (Error):
{ "error": { "code": "agent_access_disabled", "message": "…", "appAction": "integrations.record", "retryAfter": 2 } }
codeis stable;messageis plain English for the user and the agent, and never contains device paths, system errors or radio frames.appAction(when set) names the IGH app screen that does what the API won't. Tell the user to go there.retryAfter(seconds, when set) also comes as aRetry-Afterheader on429and503.- Anything not exposed, or not existing, is
404not_found, never "forbidden".
| Field | Type | Required | Description |
|---|---|---|---|
error |
object | yes |
Event
One pushed event.
| Field | Type | Required | Description |
|---|---|---|---|
at |
integer | yes | Epoch ms. |
class |
string or null | ||
device |
string or null | The device id (not for exposure). |
|
event |
string | yes | state, sensor, scene or exposure. |
house |
string | yes | |
state |
any | The device's state now: a unit's or gang's state, a sensor's readings, { running } for a scene. |
Example
A request in words and the exact call that does it.
| Field | Type | Required | Description |
|---|---|---|---|
ask |
string | yes | |
call |
string | yes |
Explain
{ device, from?, to? } (epoch ms; the last 24 h by default).
| Field | Type | Required | Description |
|---|---|---|---|
device |
string | yes | |
from |
integer or null | ||
to |
integer or null |
Floor
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | yes | |
name |
string | yes | |
rooms |
array of Room |
yes |
Gang
One gang of a multi-gang module.
| Field | Type | Required | Description |
|---|---|---|---|
floor |
string or null | ||
gang |
integer | yes | The gang's position on the module, from 0. |
id |
string | yes | The device id: {gateway}:{integration}:{unit}:{gang}. |
name |
string | yes | |
operations |
array of string | yes | |
room |
string or null | A gang has its own room, which may differ from its module's. | |
state |
any |
House
Everything one API integration shows, in the house's own layout.
| Field | Type | Required | Description |
|---|---|---|---|
floors |
array of Floor |
yes | |
gateway |
string | yes | The gateway's id (its serial), as the integration's page and the mDNS announcement show it. |
house |
string | yes | The house's name. Every answer names it, so an agent connected to two houses never mixes them up. |
rooms |
array of Room |
yes | Rooms on no floor. |
scenes |
array of Scene |
yes | |
units |
array of Unit |
yes | Units and modules in no room. |
IntentDef
| Field | Type | Required | Description |
|---|---|---|---|
access |
Access |
yes | |
appAction |
string or null | ||
errors |
array of string | yes | Error codes a change can get. |
examples |
array of Example |
yes | |
guidance |
string or null | A read-only intent: what to do instead. | |
id |
string | yes | |
meaning |
string | yes | |
notWhen |
string | yes | |
prerequisites |
array of string | yes | |
sideEffects |
string | yes | |
title |
string | yes | |
value |
Shape |
yes | |
when |
string | yes |
IntentValue
{ value }: an intent's new value, in the intent's value shape.
| Field | Type | Required | Description |
|---|---|---|---|
value |
any | yes |
Object
An automation or schedule in the app's own schema, with ifRevision on an edit.
| Field | Type | Required | Description |
|---|---|---|---|
ifRevision |
integer or null |
Origin
Which API integration and credential created an object. Provenance, not ownership: the app shows "Created via API: {name}", "(deleted)" once the record is gone.
| Field | Type | Required | Description |
|---|---|---|---|
integration |
string | yes | |
link |
string | yes |
Rename
{ name }: a rename (rooms, units, gangs).
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | yes |
Room
| Field | Type | Required | Description |
|---|---|---|---|
floor |
string or null | The floor's id; null for a room on no floor. |
|
id |
string | yes | |
name |
string | yes | |
units |
array of Unit |
yes |
Scene
| Field | Type | Required | Description |
|---|---|---|---|
floor |
string or null | ||
id |
string | yes | The device id: {gateway}:{integration}:scene:{scene}. |
name |
string | yes | |
room |
string or null | Where the app shows it (display only). |
SceneBody
A scene, in the app's scene schema: steps are its actions.
| Field | Type | Required | Description |
|---|---|---|---|
floorId |
string or null | ||
icon |
string or null | ||
ifRevision |
integer or null | The revision read; another one now is 409 revision_mismatch. |
|
name |
string or null | ||
roomId |
string or null | ||
steps |
array or null |
Shape
The shape of an intent's value.
Unit
A unit, or a multi-gang module with the gangs this integration shows inside.
| Field | Type | Required | Description |
|---|---|---|---|
class |
string | yes | The class (light_dimmer, shutter, gang_module, …): what it is and what it can do. |
floor |
string or null | ||
gangs |
array of Gang |
||
id |
string | yes | The device id: {gateway}:{integration}:{unit}. |
kind |
string | yes | unit, or module for a multi-gang module (controlled gang by gang). |
model |
string or null | The hardware model (IGHH, IGHHX3, …) when known: it decides which intents apply. |
|
name |
string | yes | |
operations |
array of string | yes | The operations POST /v1/devices/{id}/control takes for this class. |
room |
string or null | ||
state |
any | The current state (live); absent on a module, whose state is in its gangs. |
The WebSocket /v1/stream uses ClientMessage, Event and Error (AsyncAPI document: api/asyncapi.json).
IGH home API 1.1.0. Generated from the doc source; do not edit.