IGH developershome API v1 · 1.1.0

Postman

The Postman package

To try the API by hand, import the Postman package from the public repository's postman/ folder (or download it below): the HTTP collection with every operation, the authorization flows and the refusals; an environment each for the home network and the cloud; and the WebSocket requests with their saved ClientMessage messages. It is generated with these pages, so it always matches the reference. On the home network, pin the gateway's CA first (GET /v1/gateway/tls, TLS and CA pinning).

IGH home API in Postman

Version 1.1.0. Generated from the API's OpenAPI and AsyncAPI documents with the rest of https://developers.igh.app: nothing here is edited by hand.

File What
igh-api.postman_collection.json The HTTP collection (Postman Collection v2.1): every operation, the authorization flows, errors, a read-only smoke run.
igh-local.postman_environment.json Environment: IGH local (home network).
igh-staging-cloud.postman_environment.json Environment: IGH staging cloud.
igh-cloud.postman_environment.json Environment: IGH cloud.
collections/IGH WebSocket/ The WebSocket requests with their saved messages (Postman Collection Format v3, YAML).
WEBSOCKETS.md The WebSocket guide: the URLs, the messages to save, the events and close codes.

1. Trust the gateway's CA (local environment)

On the home network the gateway answers on port 8490 with HTTPS signed by its own CA. Pin that CA in Postman; never turn off SSL certificate verification (Settings → General → SSL certificate verification stays on).

  1. Get ca.pem, either:
    • in the IGH app: Settings → Integrations → the API integration → Download CA (igh-<serial>-ca.pem; it shows the SHA-256 fingerprint too);
    • or through the cloud: select a cloud environment, set {{token}} (step 4), send TLS → Gateway CA (to pin), and save the answer's ca as ca.pem (the Visualize tab shows it to copy). Compare fingerprint with openssl x509 -in ca.pem -noout -fingerprint -sha256.
  2. Postman → Settings → Certificates → CA certificates: turn it on and select the file.
  3. Select the IGH local environment and set serial (the gateway id, lower case). Postman reaches igh-<serial>.local through mDNS; where that doesn't resolve, put the gateway's LAN address in baseUrl and wsUrl (https://192.168.1.20:8490/v1): the certificate names it too.
  4. After the CA is regenerated in the app, repeat from 1.

2. Import

  1. Postman → Import → select the collection and the three environments (drag them in together).
  2. For the WebSocket requests: see WEBSOCKETS.md (the v3 folder, or a request saved by hand).
  3. In the environment selector (top right) pick one.

3. The environments

Switching the environment is the only change between the home network and the cloud: every request uses {{baseUrl}} and {{token}}. The device flow, OAuth and MCP always go to a cloud (pairBase, recordUrl).

File Name baseUrl Notes
igh-local.postman_environment.json IGH local (home network) https://igh-{{serial}}.local:8490/v1 The gateway on the home network, port 8490, HTTPS with the gateway's own CA (pin it in Postman: Settings → Certificates → CA certificates). serial is the gateway id in lower case. Without mDNS, put the gateway's LAN address in baseUrl and wsUrl instead (https://192.168.1.20:8490/v1): the certificate names the LAN addresses too. Pairing, MCP and OAuth go to the production cloud; for a gateway on staging change pairBase to https://api.staging.igh.app/v1 and mcpUrl to https://mcp.staging.igh.app.
igh-staging-cloud.postman_environment.json IGH staging cloud https://api.staging.igh.app/v1 The staging cloud's relay (api.staging.igh.app), MCP server (mcp.staging.igh.app) and OAuth server (cloud.staging.igh.app). The home's gateway must use the staging cloud, and the integration needs Cloud access on.
igh-cloud.postman_environment.json IGH cloud https://api.igh.app/v1 The production cloud's relay (api.igh.app), MCP server (mcp.igh.app) and OAuth server (cloud.igh.app). The integration needs Cloud access on.
Variable What
transport local or cloud: requests marked (local only) or (cloud only) skip themselves in the other.
baseUrl The API, ending in /v1.
wsUrl The WebSocket /v1/stream.
serial The gateway id, lower case (on the integration's page and in mDNS _igh-api._tcp). The local environment's addresses use it.
recordUrl The integration's own MCP address https://mcp.igh.app/r/<id>, from its page in the app (Agent access on): the device flow, OAuth and MCP use it.
pairBase The cloud API that runs the device flow (/v1/pair).
mcpUrl The cloud's MCP server; an integration's own address (recordUrl) is <mcpUrl>/r/<id>.
token (secret) The credential every request sends (Authorization: Bearer {{token}}). The Authorization folders set it.
apiKey (secret) An API key to paste (folder (a)).
oauthRefreshToken (secret) OAuth's refresh token, copied from Manage Tokens (folder (d)).
revokedToken (secret) A revoked credential, for a negative case.
otherRecordToken (secret) Another integration's credential, for a negative case.
deviceId, lightId, sceneId, automationId, scheduleId, roomId, intentId, sensorId An id, empty: Getting started → House and the list requests fill it (only while empty); set it to choose.

Secret variables keep their current value on this computer only. No environment holds a credential or an id: fill them in Postman.

4. Get a credential

Each folder under Authorization runs top to bottom (send each request, or Run folder in the Collection Runner) and ends with {{token}} set.

Use Folder Credential
Scripts, your own programs (a) API key: an administrator creates it on the integration's page (Settings → Integrations → API integration → Create key); paste it into apiKey igh_key_… until deleted
Agent-like tools, terminals, anything without a browser (b) Device flow: POST /v1/pair with recordUrl, the administrator compares the matching code and accepts in the IGH app, POST /v1/pair/token until approved igh_agent_… until revoked
The same, for one session (c) Device flow, the administrator picks Accept for this session igh_session_… (ends with the session)
Hosted platforms (Claude, ChatGPT) (d) OAuth 2.1 with PKCE: discovery, dynamic registration, Postman's OAuth 2.0 helper in the browser an access token of about an hour, and a refresh token
MCP clients (e) MCP over JSON-RPC: request_access, then check_access until accepted igh_agent_… or igh_session_…

(f) holds the refusals: no token, a bad or revoked one, Agent access or Cloud access off, another integration's credential on its address.

5. Run

  1. Getting started → House fills the id variables (deviceId, lightId, sceneId, roomId, …) with the house's first of each kind, only while they are empty: clear one to pick again, or set it to the device you want.
  2. Smoke run (read-only) → Run folder: every read, nothing switched or created.
  3. The other folders top to bottom. Requests named [switches a device] operate real units. Scenes, Automations and Schedules create one test object each (a scene of one delay, a disabled automation, a paused schedule): the API never deletes, so remove them in the app.
  4. Errors and edge cases provokes each refusal; some need a setting of the integration changed first (the request says which).

Every request checks its status. A refusal is checked for { error: { code, message } }, and the console (View → Show Postman Console) prints its appAction (the app screen that does what the API won't) and retryAfter. Through the cloud, 50 requests a second and 600 a minute per home: past that, 429 with retryAfter.

Guides: https://developers.igh.app · Reference: https://developers.igh.app/reference.html

The WebSocket in Postman

/v1/stream carries requests as messages, subscriptions and pushed events (AsyncAPI: https://developers.igh.app/asyncapi.html).

Postman sends WebSocket requests, but its Collection v2.1 file format (the collection .json) can't hold them: that is a limit of the file format, not of Postman. Postman v12's Collection Format v3 can, so the WebSocket requests come as a v3 collection beside it; the same requests can be saved by hand in any version.

The URLs

Environment wsUrl
IGH local (home network) wss://igh-{{serial}}.local:8490/v1/stream
IGH staging cloud wss://api.staging.igh.app/v1/stream
IGH cloud wss://api.igh.app/v1/stream

Authenticate the upgrade with Authorization: Bearer {{token}}, or ?token={{token}} when a client can't set headers: the gateway and the cloud both accept the query token on the upgrade (the cloud strips it before relaying; on HTTPS requests only the header counts). On the home network the CA pinned in README step 1 applies; keep SSL verification on.

Option A: the v3 collection (Postman v12)

collections/IGH WebSocket/ is a Collection Format v3 collection: one YAML file per request (Stream (Authorization header), Stream (token in the query)), each with every message below saved. Bring it in with Import → drag the IGH WebSocket folder, or open a folder holding postman/collections/IGH WebSocket in Local View (Native Git). Select an IGH environment, Connect, pick a saved message, Send.

Option B: by hand (any Postman version)

  1. New → WebSocket. URL: {{wsUrl}}.
  2. Headers: Authorization = Bearer {{token}} (or the URL {{wsUrl}}?token={{token}}).
  3. Save it into a collection of its own, "IGH WebSocket" (a WebSocket request in the HTTP collection makes it a multi-protocol collection, with fewer features).
  4. For each message below: paste it into the message box, set the type to JSON, Save message with its name. Variables ({{lightId}}) are replaced when sent.
  5. Connect, then send a saved message.

The messages to save

A request message { id, method, path, query?, body? } (path with /v1, as on HTTPS) is answered { id, status, body }; { id, subscribe: [...] } chooses the topics (state, sensor, scene, exposure; all four until a client subscribes).

Subscribe: state

Only state events from now on (answered { id, status: 200, body: { subscribed } }).

{
  "id": "subscribe-state",
  "subscribe": [
    "state"
  ]
}

Subscribe: sensors

Only sensor events from now on (answered { id, status: 200, body: { subscribed } }).

{
  "id": "subscribe-sensor",
  "subscribe": [
    "sensor"
  ]
}

Subscribe: scenes

Only scene events from now on (answered { id, status: 200, body: { subscribed } }).

{
  "id": "subscribe-scene",
  "subscribe": [
    "scene"
  ]
}

Subscribe: exposure

Only exposure events from now on (answered { id, status: 200, body: { subscribed } }).

{
  "id": "subscribe-exposure",
  "subscribe": [
    "exposure"
  ]
}

Subscribe: everything

All four topics (what a new socket gets until it subscribes).

{
  "id": "subscribe-all",
  "subscribe": [
    "state",
    "sensor",
    "scene",
    "exposure"
  ]
}

Request: state now

GET /v1/state over the socket: read it once after (re)connecting to catch up.

{
  "id": "state",
  "method": "GET",
  "path": "/v1/state"
}

Command: light on [switches a device]

A request over the socket, answered { id, status, body } as over HTTPS. Switches a real device.

{
  "id": "light-on",
  "method": "POST",
  "path": "/v1/devices/{{lightId}}/control",
  "body": {
    "op": "set_power",
    "args": {
      "on": true
    }
  }
}

Command: light off [switches a device]

A request over the socket, answered { id, status, body } as over HTTPS. Switches a real device.

{
  "id": "light-off",
  "method": "POST",
  "path": "/v1/devices/{{lightId}}/control",
  "body": {
    "op": "set_power",
    "args": {
      "on": false
    }
  }
}

Keep-alive (no ping message: a cheap request)

The stream has no ping message, and a text that is not JSON (ping) closes it (4400 invalid_request). WebSocket ping frames are answered by the server; to see traffic, send this cheap request.

{
  "id": "keep-alive",
  "method": "GET",
  "path": "/v1/about"
}

What arrives

state

A unit's or gang's new state.

{
  "event": "state",
  "house": "Cohen Home",
  "device": "8F3K2M9Q4T7W:5b0e6c1a-2f4d-4c3b-9a8e-7d6f5e4c3b2a:u-kitchen-light",
  "class": "light_dimmer",
  "state": {
    "on": true,
    "bri": 40
  },
  "at": 1760780000000
}

sensor

A sensor reading or detection.

{
  "event": "sensor",
  "house": "Cohen Home",
  "device": "8F3K2M9Q4T7W:5b0e6c1a-2f4d-4c3b-9a8e-7d6f5e4c3b2a:u-hall-sensor",
  "class": "sensor",
  "state": {
    "motion": true,
    "lux": 120
  },
  "at": 1760780000000
}

scene

A scene runs (running true, then false).

{
  "event": "scene",
  "house": "Cohen Home",
  "device": "8F3K2M9Q4T7W:5b0e6c1a-2f4d-4c3b-9a8e-7d6f5e4c3b2a:scene:s-evening",
  "class": "scene",
  "state": {
    "running": true
  },
  "at": 1760780000000
}

exposure

What the integration shows changed: read GET /v1/house again.

{
  "event": "exposure",
  "house": "Cohen Home",
  "at": 1760780000000
}

answer

The answer to a request message.

{
  "id": "light-on",
  "status": 200,
  "body": {
    "house": "Cohen Home",
    "device": "8F3K2M9Q4T7W:5b0e6c1a-2f4d-4c3b-9a8e-7d6f5e4c3b2a:u-kitchen-light",
    "state": {
      "on": true,
      "bri": 40
    }
  }
}

error (rate limited, the socket stays open)

Through the cloud, a message past the limit.

{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests for this home. Wait and try again.",
    "retryAfter": 1
  }
}

error (before a close)

A credential that stopped passing: this frame, then close 4403 agent_access_disabled.

{
  "error": {
    "code": "agent_access_disabled",
    "message": "Agent access is off for this API integration.",
    "appAction": "integrations.record"
  }
}

Errors and close codes


IGH home API 1.1.0. Generated from the doc source; do not edit.