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).
- 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'scaasca.pem(the Visualize tab shows it to copy). Comparefingerprintwithopenssl x509 -in ca.pem -noout -fingerprint -sha256.
- in the IGH app: Settings → Integrations → the API integration → Download CA (
- Postman → Settings → Certificates → CA certificates: turn it on and select the file.
- Select the IGH local environment and set
serial(the gateway id, lower case). Postman reachesigh-<serial>.localthrough mDNS; where that doesn't resolve, put the gateway's LAN address inbaseUrlandwsUrl(https://192.168.1.20:8490/v1): the certificate names it too. - After the CA is regenerated in the app, repeat from 1.
2. Import
- Postman → Import → select the collection and the three environments (drag them in together).
- For the WebSocket requests: see WEBSOCKETS.md (the v3 folder, or a request saved by hand).
- 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
- 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. - Smoke run (read-only) → Run folder: every read, nothing switched or created.
- 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.
- 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)
- New → WebSocket. URL:
{{wsUrl}}. - Headers:
Authorization=Bearer {{token}}(or the URL{{wsUrl}}?token={{token}}). - 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).
- 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. - 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
- A refused upgrade is the HTTP status with the error body (401 without or with a bad credential, 403, 429 past 20 sockets per home through the cloud, 503
gateway_offline). Postman shows it as the handshake's response. - A message that is refused (bad JSON shape, 413 over 16 KiB, 429
rate_limitedwithretryAfterthrough the cloud) is answered with an error frame or{ id, status, body }; the socket stays open. - A text that is not JSON (
ping) closes the socket with 4400invalid_request. - A credential that stops passing (revoked, Agent access or Cloud access turned off, the integration disabled) gets an
{ error }frame, then the close code 4000 + the HTTP status with the error code as the reason: 4401unauthorizedorsession_expired, 4403agent_access_disabled,cloud_access_disabledorrecord_disabled, 4404, 4429, and 4503gateway_offlinewhen the gateway leaves the cloud. - On AWS (api.igh.app, api.staging.igh.app) the close carries no code: the error frame is the only signal.
- Reconnect with a backoff, then send "Request: state now" to catch up.
IGH home API 1.1.0. Generated from the doc source; do not edit.