{
  "v": 12,
  "_ref_id": "coll_97ab96976e071407e9e2f769",
  "name": "IGH home API v1",
  "description": "The IGH home API v1 (1.1.0), generated from its OpenAPI document. Guides: https://developers.igh.app · Hoppscotch guide: https://developers.igh.app/hoppscotch.html\n\n**Start here**\n\n1. Pick an environment: *IGH local (home network)* for the gateway's port 8490 (pin its CA first: README), *IGH cloud* or *IGH staging cloud* for the relay. Switching the environment is the only change between them.\n\n2. Get a credential: one of the **Authorization** folders sets `<<token>>` (an API key, the device flow, OAuth or MCP).\n\n3. Send **Getting started → House**: it fills `deviceId`, `lightId`, `sceneId`, `roomId` and the other ids.\n\n4. **Smoke run (read-only)** → Run Collection, or the folders top to bottom.\n\nRequests named **[switches a device]** operate real units. Requests marked *(cloud only)* talk to the cloud from every environment (or, when they need the relay, aren't sent on the home network); *(local only)* ones aren't sent in a cloud environment. A request that can't work in the selected environment, or needs an empty variable, stops before it is sent and says why.\n\nEvery request checks its status (its Variables tab says what it expects); refusals are checked for the one error shape `{ error: { code, message } }`, and the Console prints `appAction` (the app screen that does what the API won't) and `retryAfter`. The scripts use Hoppscotch's `hopp` API (Settings → Experiments → Experimental scripting sandbox, on by default).\n\nThe WebSocket `/v1/stream` is not in this file (a Hoppscotch collection holds HTTP and GraphQL requests only): see WEBSOCKETS.md for the Realtime tab.",
  "folders": [
    {
      "v": 12,
      "_ref_id": "coll_4d6f946726e0512abcb23c84",
      "name": "Authorization",
      "description": "Six folders, one per way in. Each runs top to bottom and ends with `<<token>>` set, which every other request sends (`Authorization: Bearer <<token>>`).\n- Scripts and your own tools: **(a) API key**.\n- Agent-like tools and terminals without a browser: **(b)** or **(c) device flow**, or **(e)** over MCP.\n- Hosted platforms (Claude, ChatGPT): **(d) OAuth 2.1 with PKCE**.\nThe device flow, OAuth and MCP always talk to the cloud (`pairBase`, `<<recordUrl>>`), whichever environment is selected; the credential they give works on the home network too.",
      "folders": [
        {
          "v": 12,
          "_ref_id": "coll_d4bbe3b17ce281b34ec571dc",
          "name": "(a) API key",
          "description": "For scripts and your own tools. An administrator creates the key in the IGH app: Settings → Integrations → the API integration → Create key. It is shown once: copy it.\n\nPaste it into the environment's `apiKey` (current value; the variable is secret and stays on this computer), then send the request: when the key works it becomes `<<token>>`.",
          "folders": [],
          "requests": [
            {
              "v": "17",
              "_ref_id": "req_be35965e53648e4b3420a2d2",
              "name": "Use the key as <<token>>",
              "method": "GET",
              "endpoint": "<<baseUrl>>/integration",
              "params": [],
              "headers": [],
              "auth": {
                "authType": "bearer",
                "authActive": true,
                "token": "<<apiKey>>"
              },
              "body": {
                "contentType": null,
                "body": null
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "200",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "api",
                  "active": true
                }
              ],
              "responses": {},
              "description": "**`GET /v1/integration`** — The integration this credential belongs to, the house, and its settings.\n\n[Reference](https://developers.igh.app/reference.html#op-integration) · [Concepts](https://developers.igh.app/overview.html)",
              "preRequestScript": "if (!hopp.env.get(\"apiKey\")) {\n  throw new Error(\"Not sent: paste the key into the environment's apiKey first.\");\n}",
              "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar body = answer();\nif (body && body.integration) {\n  var credential = body.integration.credential || {};\n  console.log(\"House \" + body.house + \", integration \" + body.integration.name + \" (\" + body.integration.id + \"), credential \" + credential.kind + \".\");\n  console.log(\"Settings: \" + JSON.stringify(body.integration.settings));\n  fill(\"serial\", String(body.gateway || \"\").toLowerCase());\n}\n\nif (hopp.response.statusCode === 200) {\n  save(\"token\", hopp.env.get(\"apiKey\"));\n  save(\"tokenKind\", \"key\");\n  hopp.test(\"token is the key\", function () { hopp.expect(hopp.env.get(\"token\")).to.match(/^igh_key_/); });\n}"
            }
          ],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "headers": [],
          "variables": [],
          "preRequestScript": "",
          "testScript": ""
        },
        {
          "v": 12,
          "_ref_id": "coll_b938f246dd58cc10febc195b",
          "name": "(b) Device flow: agent credential",
          "description": "How agent-like tools without a browser connect: ask by the integration's address, show the matching code, an administrator accepts in the IGH app, poll for the credential. Needs `recordUrl` (the MCP address on the integration's page, Agent access and Cloud access on).",
          "folders": [],
          "requests": [
            {
              "v": "17",
              "_ref_id": "req_9d38044d7e980fcc53132623",
              "name": "1. Ask for access (cloud only)",
              "method": "POST",
              "endpoint": "<<pairBase>>/pair",
              "params": [],
              "headers": [],
              "auth": {
                "authType": "none",
                "authActive": true
              },
              "body": {
                "contentType": "application/json",
                "body": "{\n  \"record\": \"<<recordUrl>>\",\n  \"name\": \"Hoppscotch\",\n  \"platform\": \"Hoppscotch\"\n}"
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "200",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "plain",
                  "active": true
                },
                {
                  "key": "igh.record",
                  "value": "true",
                  "active": true
                }
              ],
              "responses": {},
              "description": "`POST /v1/pair { record, name, platform }` on the cloud, whatever the environment (`pairBase`). `record` is the integration's own MCP address, from its page in the IGH app (Agent access on). The answer's `userCode` is the matching code: the Console and the test result show it. Errors: 404 `not_found` (no IGH home takes agents at that address), 403 `agent_access_disabled` or `cloud_access_disabled`, 429 `rate_limited` (5 a minute, 30 an hour per home).\n\n[Authentication and Accept agent](https://developers.igh.app/authentication.html) · [Getting started](https://developers.igh.app/getting-started.html)",
              "preRequestScript": "",
              "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar body = answer() || {};\nif (hopp.response.statusCode === 200) {\n  save(\"pairCode\", body.code);\n  save(\"pairUserCode\", body.userCode);\n  save(\"pairInterval\", body.interval || 5);\n  save(\"pairPolls\", 0);\n  console.log(\"Matching code: \" + body.userCode + \". Tell the administrator: the IGH app shows the same code; Accept (or Accept for this session) it there. Then send the next request.\");\n  hopp.test(\"matching code to compare in the IGH app: \" + body.userCode, function () {\n    hopp.expect(body.userCode).to.match(/^[2-9A-Z]{3}-[2-9A-Z]{2}$/);\n  });\n}"
            },
            {
              "v": "17",
              "_ref_id": "req_f36e0bf2758e3ecdd3620847",
              "name": "2. Wait for approval (send again until approved) (cloud only)",
              "method": "POST",
              "endpoint": "<<pairBase>>/pair/token",
              "params": [],
              "headers": [],
              "auth": {
                "authType": "none",
                "authActive": true
              },
              "body": {
                "contentType": "application/json",
                "body": "{\n  \"code\": \"<<pairCode>>\"\n}"
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "200,400",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "oauth",
                  "active": true
                },
                {
                  "key": "igh.credential",
                  "value": "agent",
                  "active": true
                }
              ],
              "responses": {},
              "description": "The administrator compares the matching code in the IGH app and taps **Accept**: the answer is an agent credential `igh_agent_…`, kept until revoked.\n\nUntil then it answers 400 `authorization_pending` (or `slow_down`: polled too soon; the interval grows by 5 s). On approval it sets `<<token>>`; the credential is handed out once.\n\nHoppscotch has no request loop, so send it again until the answer is 200: from the second poll on, its pre-request script first waits `pairInterval` seconds. To poll hands-free: this folder → Run Collection, select only this request, Iterations 120, Delay 0, **Keep variable values** on and **Stop run if an error occurs** on; after approval, denial (`access_denied`) or expiry (`expired_token`, after 10 minutes) the next poll stops (no `pairCode`), which ends the run.\n\n[Authentication and Accept agent](https://developers.igh.app/authentication.html) · [Getting started](https://developers.igh.app/getting-started.html)",
              "preRequestScript": "if (!hopp.env.get(\"pairCode\")) {\n  throw new Error(\"Not sent: no pairCode. Send 1 (Ask for access) first; after an answer (approved, denied or expired) start again from 1.\");\n}\nif (Number(hopp.env.get(\"pairPolls\") || 0) > 0) {\n  var seconds = Number(hopp.env.get(\"pairInterval\") || 5);\n  await new Promise(function (resolve) { setTimeout(resolve, seconds * 1000); });\n}",
              "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar body = answer() || {};\nvar polls = Number(hopp.env.get(\"pairPolls\") || 0) + 1;\nsave(\"pairPolls\", polls);\nif (hopp.response.statusCode === 200 && body.access_token) {\n  var wanted = hopp.request.variables.get(\"igh.credential\");\n  save(\"token\", body.access_token);\n  save(\"tokenKind\", body.kind);\n  save(\"pairCode\", \"\");\n  save(\"pairPolls\", 0);\n  hopp.test(\"approved: token is set (\" + body.kind + \")\", function () {\n    hopp.expect(body.access_token).to.match(/^igh_(agent|session)_/);\n  });\n  hopp.test(\"the credential kind is \" + wanted, function () { hopp.expect(body.kind).to.equal(wanted); });\n} else if (body.error === \"authorization_pending\" || body.error === \"slow_down\") {\n  if (body.error === \"slow_down\") { save(\"pairInterval\", Number(hopp.env.get(\"pairInterval\") || 5) + 5); }\n  console.log(\"Waiting for an administrator to answer in the IGH app (poll \" + polls + \"). Matching code: \" + (hopp.env.get(\"pairUserCode\") || \"?\") + \". Send this request again: it waits pairInterval seconds first.\");\n} else {\n  console.log(\"The request ended: \" + (body.error || hopp.response.statusCode) + (body.error_description ? \" (\" + body.error_description + \")\" : \"\") + \". Start again from 1.\");\n  save(\"pairCode\", \"\");\n}"
            },
            {
              "v": "17",
              "_ref_id": "req_221d7abc921f03632b7b66e1",
              "name": "3. Check the agent credential",
              "method": "GET",
              "endpoint": "<<baseUrl>>/integration",
              "params": [],
              "headers": [],
              "auth": {
                "authType": "inherit",
                "authActive": true
              },
              "body": {
                "contentType": null,
                "body": null
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "200",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "api",
                  "active": true
                }
              ],
              "responses": {},
              "description": "Uses `<<token>>` like every other request.\n\n**`GET /v1/integration`** — The integration this credential belongs to, the house, and its settings.\n\n[Reference](https://developers.igh.app/reference.html#op-integration) · [Concepts](https://developers.igh.app/overview.html)",
              "preRequestScript": "",
              "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar body = answer();\nif (body && body.integration) {\n  var credential = body.integration.credential || {};\n  console.log(\"House \" + body.house + \", integration \" + body.integration.name + \" (\" + body.integration.id + \"), credential \" + credential.kind + \".\");\n  console.log(\"Settings: \" + JSON.stringify(body.integration.settings));\n  fill(\"serial\", String(body.gateway || \"\").toLowerCase());\n}"
            }
          ],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "headers": [],
          "variables": [],
          "preRequestScript": "",
          "testScript": ""
        },
        {
          "v": 12,
          "_ref_id": "coll_de727e19359d17eea79aac85",
          "name": "(c) Device flow: Accept for this session",
          "description": "The same flow; the administrator picks **Accept for this session** instead of Accept. The credential is `igh_session_…` and lasts as long as that session.",
          "folders": [],
          "requests": [
            {
              "v": "17",
              "_ref_id": "req_c9cd0bd4bc43c7f7fe280d5a",
              "name": "1. Ask for access (cloud only)",
              "method": "POST",
              "endpoint": "<<pairBase>>/pair",
              "params": [],
              "headers": [],
              "auth": {
                "authType": "none",
                "authActive": true
              },
              "body": {
                "contentType": "application/json",
                "body": "{\n  \"record\": \"<<recordUrl>>\",\n  \"name\": \"Hoppscotch (session)\",\n  \"platform\": \"Hoppscotch\"\n}"
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "200",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "plain",
                  "active": true
                },
                {
                  "key": "igh.record",
                  "value": "true",
                  "active": true
                }
              ],
              "responses": {},
              "description": "`POST /v1/pair { record, name, platform }` on the cloud, whatever the environment (`pairBase`). `record` is the integration's own MCP address, from its page in the IGH app (Agent access on). The answer's `userCode` is the matching code: the Console and the test result show it. Errors: 404 `not_found` (no IGH home takes agents at that address), 403 `agent_access_disabled` or `cloud_access_disabled`, 429 `rate_limited` (5 a minute, 30 an hour per home).\n\n[Authentication and Accept agent](https://developers.igh.app/authentication.html) · [Getting started](https://developers.igh.app/getting-started.html)",
              "preRequestScript": "",
              "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar body = answer() || {};\nif (hopp.response.statusCode === 200) {\n  save(\"pairCode\", body.code);\n  save(\"pairUserCode\", body.userCode);\n  save(\"pairInterval\", body.interval || 5);\n  save(\"pairPolls\", 0);\n  console.log(\"Matching code: \" + body.userCode + \". Tell the administrator: the IGH app shows the same code; Accept (or Accept for this session) it there. Then send the next request.\");\n  hopp.test(\"matching code to compare in the IGH app: \" + body.userCode, function () {\n    hopp.expect(body.userCode).to.match(/^[2-9A-Z]{3}-[2-9A-Z]{2}$/);\n  });\n}"
            },
            {
              "v": "17",
              "_ref_id": "req_d67d1635d1decdec8366e780",
              "name": "2. Wait for approval (send again until approved) (cloud only)",
              "method": "POST",
              "endpoint": "<<pairBase>>/pair/token",
              "params": [],
              "headers": [],
              "auth": {
                "authType": "none",
                "authActive": true
              },
              "body": {
                "contentType": "application/json",
                "body": "{\n  \"code\": \"<<pairCode>>\"\n}"
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "200,400",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "oauth",
                  "active": true
                },
                {
                  "key": "igh.credential",
                  "value": "session",
                  "active": true
                }
              ],
              "responses": {},
              "description": "The administrator compares the matching code in the IGH app and taps **Accept for this session**: the answer is a session credential `igh_session_…`, which ends with the session (then 401 `session_expired`).\n\nUntil then it answers 400 `authorization_pending` (or `slow_down`: polled too soon; the interval grows by 5 s). On approval it sets `<<token>>`; the credential is handed out once.\n\nHoppscotch has no request loop, so send it again until the answer is 200: from the second poll on, its pre-request script first waits `pairInterval` seconds. To poll hands-free: this folder → Run Collection, select only this request, Iterations 120, Delay 0, **Keep variable values** on and **Stop run if an error occurs** on; after approval, denial (`access_denied`) or expiry (`expired_token`, after 10 minutes) the next poll stops (no `pairCode`), which ends the run.\n\n[Authentication and Accept agent](https://developers.igh.app/authentication.html) · [Getting started](https://developers.igh.app/getting-started.html)",
              "preRequestScript": "if (!hopp.env.get(\"pairCode\")) {\n  throw new Error(\"Not sent: no pairCode. Send 1 (Ask for access) first; after an answer (approved, denied or expired) start again from 1.\");\n}\nif (Number(hopp.env.get(\"pairPolls\") || 0) > 0) {\n  var seconds = Number(hopp.env.get(\"pairInterval\") || 5);\n  await new Promise(function (resolve) { setTimeout(resolve, seconds * 1000); });\n}",
              "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar body = answer() || {};\nvar polls = Number(hopp.env.get(\"pairPolls\") || 0) + 1;\nsave(\"pairPolls\", polls);\nif (hopp.response.statusCode === 200 && body.access_token) {\n  var wanted = hopp.request.variables.get(\"igh.credential\");\n  save(\"token\", body.access_token);\n  save(\"tokenKind\", body.kind);\n  save(\"pairCode\", \"\");\n  save(\"pairPolls\", 0);\n  hopp.test(\"approved: token is set (\" + body.kind + \")\", function () {\n    hopp.expect(body.access_token).to.match(/^igh_(agent|session)_/);\n  });\n  hopp.test(\"the credential kind is \" + wanted, function () { hopp.expect(body.kind).to.equal(wanted); });\n} else if (body.error === \"authorization_pending\" || body.error === \"slow_down\") {\n  if (body.error === \"slow_down\") { save(\"pairInterval\", Number(hopp.env.get(\"pairInterval\") || 5) + 5); }\n  console.log(\"Waiting for an administrator to answer in the IGH app (poll \" + polls + \"). Matching code: \" + (hopp.env.get(\"pairUserCode\") || \"?\") + \". Send this request again: it waits pairInterval seconds first.\");\n} else {\n  console.log(\"The request ended: \" + (body.error || hopp.response.statusCode) + (body.error_description ? \" (\" + body.error_description + \")\" : \"\") + \". Start again from 1.\");\n  save(\"pairCode\", \"\");\n}"
            },
            {
              "v": "17",
              "_ref_id": "req_c3585a71ef63be787251a5f7",
              "name": "3. Check the session credential",
              "method": "GET",
              "endpoint": "<<baseUrl>>/integration",
              "params": [],
              "headers": [],
              "auth": {
                "authType": "inherit",
                "authActive": true
              },
              "body": {
                "contentType": null,
                "body": null
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "200",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "api",
                  "active": true
                }
              ],
              "responses": {},
              "description": "Uses `<<token>>` like every other request.\n\n**`GET /v1/integration`** — The integration this credential belongs to, the house, and its settings.\n\n[Reference](https://developers.igh.app/reference.html#op-integration) · [Concepts](https://developers.igh.app/overview.html)",
              "preRequestScript": "",
              "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar body = answer();\nif (body && body.integration) {\n  var credential = body.integration.credential || {};\n  console.log(\"House \" + body.house + \", integration \" + body.integration.name + \" (\" + body.integration.id + \"), credential \" + credential.kind + \".\");\n  console.log(\"Settings: \" + JSON.stringify(body.integration.settings));\n  fill(\"serial\", String(body.gateway || \"\").toLowerCase());\n}"
            }
          ],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "headers": [],
          "variables": [],
          "preRequestScript": "",
          "testScript": ""
        },
        {
          "v": 12,
          "_ref_id": "coll_7358b2fa47b745527e42c372",
          "name": "(d) OAuth 2.1 with PKCE (hosted MCP clients) (cloud only)",
          "description": "How Claude, ChatGPT and other hosted platforms connect: discovery (RFC 9728, RFC 8414), dynamic client registration (RFC 7591), then the authorization code with PKCE (S256) for the resource `<<recordUrl>>`.\n\nIn the desktop app: send 1 to 4; 4 makes the authorization address (a new PKCE verifier each time) and prints it. Open it in a browser: IGH's approval page (no sign-in for the integration's own address), an administrator approves in the IGH app, and the browser lands on `<<oauthRedirectUri>>` (a loopback address nothing answers: the code is in the address bar). Copy that whole address into the environment's `oauthCode` and send 5: `<<token>>` and `oauthRefreshToken` are set. 7 refreshes, 8 revokes.\n\nHoppscotch's own OAuth 2.0 authorization (the last request) redirects to the app's own page: `https://hoppscotch.io/oauth` in the web app, which IGH accepts, but `app://hoppscotch/oauth` in the desktop app, which IGH refuses (redirects are https, or http on a loopback address). So in the desktop app use 3, 4 and 5; they are the same flow.",
          "folders": [],
          "requests": [
            {
              "v": "17",
              "_ref_id": "req_1204335bee39049cdbe812a4",
              "name": "1. Protected-resource metadata",
              "method": "GET",
              "endpoint": "<<oauthResourceMetadata>>",
              "params": [],
              "headers": [],
              "auth": {
                "authType": "none",
                "authActive": true
              },
              "body": {
                "contentType": null,
                "body": null
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "200",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "plain",
                  "active": true
                },
                {
                  "key": "igh.record",
                  "value": "true",
                  "active": true
                }
              ],
              "responses": {},
              "description": "`GET /.well-known/oauth-protected-resource/r/{id}` on the MCP host (the pre-request script makes the URL from `<<recordUrl>>`). Saves the authorization server as `oauthIssuer`.\n\n[Authentication and Accept agent](https://developers.igh.app/authentication.html) · [Getting started](https://developers.igh.app/getting-started.html)",
              "preRequestScript": "var record = String(hopp.env.get(\"recordUrl\") || \"\").replace(/\\/+$/, \"\");\nvar parts = /^(https?:\\/\\/[^/]+)(\\/.*)?$/.exec(record);\nif (!parts) { throw new Error(\"Not sent: recordUrl is not an https address.\"); }\nhopp.env.active.set(\"oauthResourceMetadata\", parts[1] + \"/.well-known/oauth-protected-resource\" + (parts[2] || \"\"));",
              "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar body = answer() || {};\nsave(\"oauthIssuer\", (body.authorization_servers || [])[0]);\nvar record = String(hopp.env.get(\"recordUrl\") || \"\").replace(/\\/+$/, \"\");\nhopp.test(\"the resource is recordUrl\", function () { hopp.expect(body.resource).to.equal(record); });\nhopp.test(\"bearer in the header\", function () { hopp.expect(body.bearer_methods_supported).to.include(\"header\"); });"
            },
            {
              "v": "17",
              "_ref_id": "req_3eb5a6efec2e732c1c80df1f",
              "name": "2. Authorization-server metadata",
              "method": "GET",
              "endpoint": "<<oauthIssuer>>/.well-known/oauth-authorization-server",
              "params": [],
              "headers": [],
              "auth": {
                "authType": "none",
                "authActive": true
              },
              "body": {
                "contentType": null,
                "body": null
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "200",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "plain",
                  "active": true
                }
              ],
              "responses": {},
              "description": "RFC 8414 metadata of the issuer (`cloud.<site>`). Saves the authorize, token, registration and revocation endpoints and the scope (`igh`, plus `offline_access` when listed, for a refresh token).\n\n[Authentication and Accept agent](https://developers.igh.app/authentication.html) · [Getting started](https://developers.igh.app/getting-started.html)",
              "preRequestScript": "",
              "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar body = answer() || {};\nsave(\"oauthAuthUrl\", body.authorization_endpoint);\nsave(\"oauthTokenUrl\", body.token_endpoint);\nsave(\"oauthRegisterUrl\", body.registration_endpoint);\nsave(\"oauthRevokeUrl\", body.revocation_endpoint);\nvar scopes = body.scopes_supported || [];\nsave(\"oauthScope\", scopes.indexOf(\"offline_access\") === -1 ? \"igh\" : \"igh offline_access\");\nhopp.test(\"PKCE with S256\", function () { hopp.expect(body.code_challenge_methods_supported).to.include(\"S256\"); });\nhopp.test(\"public clients (none)\", function () { hopp.expect(body.token_endpoint_auth_methods_supported).to.include(\"none\"); });"
            },
            {
              "v": "17",
              "_ref_id": "req_c22fb886caba0650737b8970",
              "name": "3. Register Hoppscotch (dynamic client registration)",
              "method": "POST",
              "endpoint": "<<oauthRegisterUrl>>",
              "params": [],
              "headers": [],
              "auth": {
                "authType": "none",
                "authActive": true
              },
              "body": {
                "contentType": "application/json",
                "body": "{\n  \"client_name\": \"Hoppscotch\",\n  \"redirect_uris\": [\n    \"<<oauthRedirectUri>>\",\n    \"https://hoppscotch.io/oauth\"\n  ],\n  \"token_endpoint_auth_method\": \"none\",\n  \"grant_types\": [\n    \"authorization_code\",\n    \"refresh_token\"\n  ],\n  \"response_types\": [\n    \"code\"\n  ]\n}"
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "201",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "oauth",
                  "active": true
                }
              ],
              "responses": {},
              "description": "Registers the loopback callback `<<oauthRedirectUri>>` (requests 4 and 5) and the Hoppscotch web app's `https://hoppscotch.io/oauth` (its OAuth 2.0 authorization, the last request): any https redirect, and http on a loopback address, is accepted. The client id carries the registration (no secret). Saves `oauthClientId`.\n\n[Authentication and Accept agent](https://developers.igh.app/authentication.html) · [Getting started](https://developers.igh.app/getting-started.html)",
              "preRequestScript": "",
              "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar body = answer() || {};\nsave(\"oauthClientId\", body.client_id);\nhopp.test(\"a public client\", function () { hopp.expect(body.token_endpoint_auth_method).to.equal(\"none\"); });"
            },
            {
              "v": "17",
              "_ref_id": "req_b25bddb154d2e534cb23e05c",
              "name": "4. Make the authorization address (PKCE S256), then open it in a browser",
              "method": "GET",
              "endpoint": "<<oauthIssuer>>/.well-known/oauth-authorization-server",
              "params": [],
              "headers": [],
              "auth": {
                "authType": "none",
                "authActive": true
              },
              "body": {
                "contentType": null,
                "body": null
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "200",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "plain",
                  "active": true
                }
              ],
              "responses": {},
              "description": "Reads the authorization server's metadata again, then its script makes a PKCE verifier (kept in the secret `oauthVerifier`), its S256 challenge and a state, and the authorization address with `resource=<<recordUrl>>`: the Console prints it and the environment holds it as `oauthAuthorizeUrl`. Open it in a browser and approve; the browser lands on `<<oauthRedirectUri>>?code=…&state=…`. Copy that whole address into `oauthCode`, then send 5 (the code works once, for a few minutes). Send 4 again for a fresh address.\n\n[Authentication and Accept agent](https://developers.igh.app/authentication.html) · [Getting started](https://developers.igh.app/getting-started.html)",
              "preRequestScript": "",
              "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\n// A new authorization address with PKCE (S256): a random verifier (kept in the\n// secret oauthVerifier), its SHA-256 as the challenge, and a random state.\nvar base64url = function (bytes) {\n  var alphabet = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_\";\n  var out = \"\";\n  for (var i = 0; i < bytes.length; i += 3) {\n    var n = (bytes[i] << 16) | ((bytes[i + 1] || 0) << 8) | (bytes[i + 2] || 0);\n    out += alphabet[(n >> 18) & 63] + alphabet[(n >> 12) & 63];\n    if (i + 1 < bytes.length) { out += alphabet[(n >> 6) & 63]; }\n    if (i + 2 < bytes.length) { out += alphabet[n & 63]; }\n  }\n  return out;\n};\nvar body = answer() || {};\nif (body.authorization_endpoint) { save(\"oauthAuthUrl\", body.authorization_endpoint); }\nvar clientId = hopp.env.get(\"oauthClientId\");\nif (!clientId || !hopp.env.get(\"oauthAuthUrl\")) {\n  console.warn(\"No oauthClientId or oauthAuthUrl: send 2 and 3 first.\");\n} else {\n  var verifier = base64url(new Uint8Array(crypto.getRandomValues(new Uint8Array(32))));\n  var digest = await crypto.subtle.digest(\"SHA-256\", new TextEncoder().encode(verifier));\n  var challenge = base64url(new Uint8Array(digest));\n  var state = base64url(new Uint8Array(crypto.getRandomValues(new Uint8Array(16))));\n  save(\"oauthVerifier\", verifier);\n  save(\"oauthState\", state);\n  save(\"oauthCode\", \"\");\n  var query = [\n    [\"response_type\", \"code\"], [\"client_id\", clientId], [\"redirect_uri\", hopp.env.get(\"oauthRedirectUri\")],\n    [\"code_challenge\", challenge], [\"code_challenge_method\", \"S256\"], [\"scope\", hopp.env.get(\"oauthScope\") || \"igh\"],\n    [\"resource\", String(hopp.env.get(\"recordUrl\") || \"\").replace(/\\/+$/, \"\")], [\"state\", state]\n  ].map(function (pair) { return encodeURIComponent(pair[0]) + \"=\" + encodeURIComponent(pair[1]); }).join(\"&\");\n  var address = hopp.env.get(\"oauthAuthUrl\") + \"?\" + query;\n  save(\"oauthAuthorizeUrl\", address);\n  console.log(\"Open this address in a browser (also in the environment as oauthAuthorizeUrl). An administrator approves in the IGH app; the browser then lands on \" + hopp.env.get(\"oauthRedirectUri\") + \"?code=… (nothing answers there): copy that whole address into oauthCode and send 5.\");\n  console.log(address);\n  hopp.test(\"an authorization address with PKCE S256\", function () { hopp.expect(challenge).to.match(/^[A-Za-z0-9_-]{43}$/); });\n}"
            },
            {
              "v": "17",
              "_ref_id": "req_6e72ba86ee7428c25ee54b04",
              "name": "5. Exchange the code for a token",
              "method": "POST",
              "endpoint": "<<oauthTokenUrl>>",
              "params": [],
              "headers": [],
              "auth": {
                "authType": "none",
                "authActive": true
              },
              "body": {
                "contentType": "application/x-www-form-urlencoded",
                "body": "grant_type: authorization_code\ncode: <<oauthCode>>\nredirect_uri: <<oauthRedirectUri>>\nclient_id: <<oauthClientId>>\ncode_verifier: <<oauthVerifier>>\nresource: <<recordUrl>>"
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "200",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "oauth",
                  "active": true
                }
              ],
              "responses": {},
              "description": "`grant_type=authorization_code` with the code, the loopback redirect, the client id, the PKCE verifier and the resource. The pre-request script takes the code out of the pasted address and checks its state. Sets `<<token>>` (an `igh_agent_…` access token of about an hour) and `oauthRefreshToken`.\n\n[Authentication and Accept agent](https://developers.igh.app/authentication.html) · [Getting started](https://developers.igh.app/getting-started.html)",
              "preRequestScript": "// oauthCode holds the code, or the whole address the browser landed on: keep\n// only the code, after checking the state that request 4 made.\nvar pasted = String(hopp.env.get(\"oauthCode\") || \"\").trim();\nif (!pasted) {\n  throw new Error(\"Not sent: paste the address the browser landed on (or its code) into oauthCode first; request 4 prints the address to open.\");\n}\nif (!hopp.env.get(\"oauthVerifier\")) {\n  throw new Error(\"Not sent: no oauthVerifier: send 4 for a new authorization address.\");\n}\nvar param = function (name) {\n  var found = new RegExp(\"[?&]\" + name + \"=([^&#]*)\").exec(pasted);\n  return found ? decodeURIComponent(found[1].replace(/\\+/g, \" \")) : \"\";\n};\nif (param(\"error\")) {\n  throw new Error(\"Not sent: the authorization ended with \" + param(\"error\") + (param(\"error_description\") ? \" (\" + param(\"error_description\") + \")\" : \"\") + \". Send 4 for a new address.\");\n}\nif (/[?&]code=/.test(pasted)) {\n  if (param(\"state\") !== hopp.env.get(\"oauthState\")) {\n    throw new Error(\"Not sent: the state in the pasted address isn't the one request 4 made: open 4's latest address again.\");\n  }\n  hopp.env.active.set(\"oauthCode\", param(\"code\"));\n}",
              "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar body = answer() || {};\nif (body.access_token) {\n  save(\"token\", body.access_token);\n  save(\"tokenKind\", \"oauth\");\n  save(\"oauthRefreshToken\", body.refresh_token);\n  save(\"oauthCode\", \"\");\n  save(\"oauthVerifier\", \"\");\n  hopp.test(\"token is the OAuth access token\", function () { hopp.expect(body.access_token).to.match(/^igh_agent_/); });\n} else {\n  console.log(\"The code exchange failed: \" + (body.error || hopp.response.statusCode) + (body.error_description ? \" (\" + body.error_description + \")\" : \"\") + \". A code works once and for a few minutes: send 4 for a new address and approve again.\");\n  save(\"oauthCode\", \"\");\n}"
            },
            {
              "v": "17",
              "_ref_id": "req_d4039d5471605f178f4a0479",
              "name": "6. Check the OAuth token",
              "method": "GET",
              "endpoint": "<<baseUrl>>/integration",
              "params": [],
              "headers": [],
              "auth": {
                "authType": "inherit",
                "authActive": true
              },
              "body": {
                "contentType": null,
                "body": null
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "200",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "api",
                  "active": true
                }
              ],
              "responses": {},
              "description": "Uses `<<token>>` like every other request.\n\n**`GET /v1/integration`** — The integration this credential belongs to, the house, and its settings.\n\n[Reference](https://developers.igh.app/reference.html#op-integration) · [Concepts](https://developers.igh.app/overview.html)",
              "preRequestScript": "",
              "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar body = answer();\nif (body && body.integration) {\n  var credential = body.integration.credential || {};\n  console.log(\"House \" + body.house + \", integration \" + body.integration.name + \" (\" + body.integration.id + \"), credential \" + credential.kind + \".\");\n  console.log(\"Settings: \" + JSON.stringify(body.integration.settings));\n  fill(\"serial\", String(body.gateway || \"\").toLowerCase());\n}"
            },
            {
              "v": "17",
              "_ref_id": "req_716e946d7c0dcbba71a5f81a",
              "name": "7. Refresh the token",
              "method": "POST",
              "endpoint": "<<oauthTokenUrl>>",
              "params": [],
              "headers": [],
              "auth": {
                "authType": "none",
                "authActive": true
              },
              "body": {
                "contentType": "application/x-www-form-urlencoded",
                "body": "grant_type: refresh_token\nrefresh_token: <<oauthRefreshToken>>\nclient_id: <<oauthClientId>>\nresource: <<recordUrl>>"
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "200",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "oauth",
                  "active": true
                }
              ],
              "responses": {},
              "description": "Refresh tokens rotate: the answer's refresh token replaces the old one. Sets `<<token>>` and `oauthRefreshToken` (5 set it).\n\n[Authentication and Accept agent](https://developers.igh.app/authentication.html) · [Getting started](https://developers.igh.app/getting-started.html)",
              "preRequestScript": "if (!hopp.env.get(\"oauthRefreshToken\")) {\n  throw new Error(\"Not sent: no oauthRefreshToken: get a token with 5 first.\");\n}",
              "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar body = answer() || {};\nif (body.access_token) {\n  save(\"token\", body.access_token);\n  save(\"oauthRefreshToken\", body.refresh_token);\n  hopp.test(\"a new access token in token\", function () { hopp.expect(body.access_token).to.match(/^igh_agent_/); });\n}"
            },
            {
              "v": "17",
              "_ref_id": "req_4a7b3726b5a7fa6003df44af",
              "name": "8. Revoke (the refresh token)",
              "method": "POST",
              "endpoint": "<<oauthRevokeUrl>>",
              "params": [],
              "headers": [],
              "auth": {
                "authType": "none",
                "authActive": true
              },
              "body": {
                "contentType": "application/x-www-form-urlencoded",
                "body": "token: <<oauthRefreshToken>>\nclient_id: <<oauthClientId>>"
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "200",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "oauth",
                  "active": true
                }
              ],
              "responses": {},
              "description": "Revoking the refresh token removes the connection in the home (the integration's link): `<<token>>` stops working too (401). An access token sent here changes nothing.\n\n[Authentication and Accept agent](https://developers.igh.app/authentication.html) · [Getting started](https://developers.igh.app/getting-started.html)",
              "preRequestScript": "",
              "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nif (hopp.response.statusCode === 200) { save(\"oauthRefreshToken\", \"\"); console.log(\"Revoked: the OAuth token answers 401 from now on.\"); }"
            },
            {
              "v": "17",
              "_ref_id": "req_b03fcc87db496ffc4c530288",
              "name": "Hoppscotch's OAuth 2.0 authorization (web app at hoppscotch.io)",
              "method": "GET",
              "endpoint": "<<baseUrl>>/integration",
              "params": [],
              "headers": [],
              "auth": {
                "authType": "oauth-2",
                "authActive": true,
                "addTo": "HEADERS",
                "grantTypeInfo": {
                  "grantType": "AUTHORIZATION_CODE",
                  "authEndpoint": "<<oauthAuthUrl>>",
                  "tokenEndpoint": "<<oauthTokenUrl>>",
                  "clientID": "<<oauthClientId>>",
                  "clientSecret": "",
                  "scopes": "<<oauthScope>>",
                  "token": "",
                  "refreshToken": "",
                  "isPKCE": true,
                  "codeVerifierMethod": "S256",
                  "authRequestParams": [
                    {
                      "id": 1,
                      "key": "resource",
                      "value": "<<recordUrl>>",
                      "active": true
                    }
                  ],
                  "tokenRequestParams": [
                    {
                      "id": 1,
                      "key": "resource",
                      "value": "<<recordUrl>>",
                      "active": true,
                      "sendIn": "body"
                    }
                  ],
                  "refreshRequestParams": [
                    {
                      "id": 1,
                      "key": "resource",
                      "value": "<<recordUrl>>",
                      "active": true,
                      "sendIn": "body"
                    }
                  ],
                  "tokenType": "access_token"
                }
              },
              "body": {
                "contentType": null,
                "body": null
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "200",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "api",
                  "active": true
                }
              ],
              "responses": {},
              "description": "The same flow with Hoppscotch's OAuth 2.0 authorization (Authorization Code, PKCE S256, `resource` in the authorization request), after 1 to 3. **Hoppscotch web app only**: its callback `https://hoppscotch.io/oauth` is registered by 3; the desktop app's callback `app://hoppscotch/oauth` is refused by IGH (https, or http on a loopback address), so in the desktop app use 4 and 5. Authorization tab → **Generate Token** → approve → Send: the test copies the token into `<<token>>`.\n\n**`GET /v1/integration`** — The integration this credential belongs to, the house, and its settings.\n\n[Reference](https://developers.igh.app/reference.html#op-integration) · [Concepts](https://developers.igh.app/overview.html)",
              "preRequestScript": "",
              "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar body = answer();\nif (body && body.integration) {\n  var credential = body.integration.credential || {};\n  console.log(\"House \" + body.house + \", integration \" + body.integration.name + \" (\" + body.integration.id + \"), credential \" + credential.kind + \".\");\n  console.log(\"Settings: \" + JSON.stringify(body.integration.settings));\n  fill(\"serial\", String(body.gateway || \"\").toLowerCase());\n}\n\nvar auth = hopp.request.auth || {};\nvar info = auth.grantTypeInfo || {};\nvar access = String(info.token || \"\");\nif (hopp.response.statusCode === 200 && /^igh_/.test(access)) {\n  save(\"token\", access);\n  save(\"tokenKind\", \"oauth\");\n  if (info.refreshToken) { save(\"oauthRefreshToken\", info.refreshToken); }\n  hopp.test(\"token is the OAuth access token\", function () { hopp.expect(access).to.match(/^igh_agent_/); });\n} else {\n  console.log(\"No token from the OAuth 2.0 helper here: Authorization tab → Generate Token first (Hoppscotch's web app only; in the desktop app use 3, 4 and 5).\");\n}"
            }
          ],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "headers": [],
          "variables": [],
          "preRequestScript": "",
          "testScript": ""
        },
        {
          "v": 12,
          "_ref_id": "coll_fe2f806fc0752fc00cea3cb4",
          "name": "(e) MCP lazy access over JSON-RPC (cloud only)",
          "description": "The MCP server at the integration's address, spoken by hand: Streamable HTTP, one JSON-RPC message per POST, JSON answers. With no credential it offers `about`, `request_access` and `check_access`; `check_access` hands out the credential once an administrator accepts, then every tool the integration allows is offered.",
          "folders": [],
          "requests": [
            {
              "v": "17",
              "_ref_id": "req_a4250f2e21974fad6e370d58",
              "name": "1. initialize (no credential)",
              "method": "POST",
              "endpoint": "<<recordUrl>>",
              "params": [],
              "headers": [
                {
                  "key": "Accept",
                  "value": "application/json, text/event-stream",
                  "active": true,
                  "description": ""
                },
                {
                  "key": "MCP-Protocol-Version",
                  "value": "2025-11-25",
                  "active": true,
                  "description": ""
                }
              ],
              "auth": {
                "authType": "none",
                "authActive": true
              },
              "body": {
                "contentType": "application/json",
                "body": "{\n  \"jsonrpc\": \"2.0\",\n  \"id\": \"initialize\",\n  \"method\": \"initialize\",\n  \"params\": {\n    \"protocolVersion\": \"2025-11-25\",\n    \"capabilities\": {},\n    \"clientInfo\": {\n      \"name\": \"Hoppscotch\",\n      \"version\": \"1\"\n    }\n  }\n}"
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "200",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "mcp",
                  "active": true
                },
                {
                  "key": "igh.record",
                  "value": "true",
                  "active": true
                }
              ],
              "responses": {},
              "description": "Opens the conversation (stateless: no session id). The answer names the server and the protocol version.\n\n[Authentication and Accept agent](https://developers.igh.app/authentication.html) · [Getting started](https://developers.igh.app/getting-started.html)",
              "preRequestScript": "",
              "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar body = answer() || {};\nvar result = body.result || {};\nconsole.log(\"Server \" + JSON.stringify(result.serverInfo) + \", protocol \" + result.protocolVersion);\nhopp.test(\"initialized\", function () { hopp.expect(result.protocolVersion).to.be.a(\"string\"); });"
            },
            {
              "v": "17",
              "_ref_id": "req_c4f1c0da1e6cfe8c0a1a644a",
              "name": "2. tools/list (no credential)",
              "method": "POST",
              "endpoint": "<<recordUrl>>",
              "params": [],
              "headers": [
                {
                  "key": "Accept",
                  "value": "application/json, text/event-stream",
                  "active": true,
                  "description": ""
                },
                {
                  "key": "MCP-Protocol-Version",
                  "value": "2025-11-25",
                  "active": true,
                  "description": ""
                }
              ],
              "auth": {
                "authType": "none",
                "authActive": true
              },
              "body": {
                "contentType": "application/json",
                "body": "{\n  \"jsonrpc\": \"2.0\",\n  \"id\": \"tools-list\",\n  \"method\": \"tools/list\",\n  \"params\": {}\n}"
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "200",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "mcp",
                  "active": true
                },
                {
                  "key": "igh.record",
                  "value": "true",
                  "active": true
                }
              ],
              "responses": {},
              "description": "Without a credential only `about`, `request_access` and `check_access` are offered.\n\n[Authentication and Accept agent](https://developers.igh.app/authentication.html) · [Getting started](https://developers.igh.app/getting-started.html)",
              "preRequestScript": "",
              "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar body = answer() || {};\nvar result = body.result || {};\nvar names = (result.tools || []).map(function (t) { return t.name; });\nconsole.log(\"Tools: \" + names.join(\", \"));\nhopp.test(\"about, request_access and check_access\", function () {\n  [\"about\", \"request_access\", \"check_access\"].forEach(function (name) { hopp.expect(names).to.include(name); });\n});"
            },
            {
              "v": "17",
              "_ref_id": "req_325e91a56e7aee1ae75935cc",
              "name": "3. tools/call request_access",
              "method": "POST",
              "endpoint": "<<recordUrl>>",
              "params": [],
              "headers": [
                {
                  "key": "Accept",
                  "value": "application/json, text/event-stream",
                  "active": true,
                  "description": ""
                },
                {
                  "key": "MCP-Protocol-Version",
                  "value": "2025-11-25",
                  "active": true,
                  "description": ""
                }
              ],
              "auth": {
                "authType": "none",
                "authActive": true
              },
              "body": {
                "contentType": "application/json",
                "body": "{\n  \"jsonrpc\": \"2.0\",\n  \"id\": \"request-access\",\n  \"method\": \"tools/call\",\n  \"params\": {\n    \"name\": \"request_access\",\n    \"arguments\": {\n      \"name\": \"Hoppscotch (MCP)\",\n      \"platform\": \"Hoppscotch\"\n    }\n  }\n}"
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "200",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "mcp",
                  "active": true
                },
                {
                  "key": "igh.record",
                  "value": "true",
                  "active": true
                }
              ],
              "responses": {},
              "description": "Shows the matching code (Console, test result). An open request for the same name and platform is refused (`already_waiting`) until it ends.\n\n[Authentication and Accept agent](https://developers.igh.app/authentication.html) · [Getting started](https://developers.igh.app/getting-started.html)",
              "preRequestScript": "",
              "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar body = answer() || {};\nvar result = body.result || {};\nvar content = result.structuredContent || {};\nif (content.code) {\n  save(\"mcpCode\", content.code);\n  save(\"pairInterval\", content.interval || 5);\n  save(\"pairPolls\", 0);\n  save(\"pairUserCode\", content.userCode);\n  console.log(\"Matching code: \" + content.userCode + \". The administrator accepts it in the IGH app.\");\n  hopp.test(\"matching code to compare in the IGH app: \" + content.userCode, function () { hopp.expect(content.userCode).to.be.a(\"string\"); });\n} else {\n  console.log(\"request_access: \" + JSON.stringify(content));\n}"
            },
            {
              "v": "17",
              "_ref_id": "req_3591924a6928c8d21fc40204",
              "name": "4. tools/call check_access (send again until accepted)",
              "method": "POST",
              "endpoint": "<<recordUrl>>",
              "params": [],
              "headers": [
                {
                  "key": "Accept",
                  "value": "application/json, text/event-stream",
                  "active": true,
                  "description": ""
                },
                {
                  "key": "MCP-Protocol-Version",
                  "value": "2025-11-25",
                  "active": true,
                  "description": ""
                }
              ],
              "auth": {
                "authType": "none",
                "authActive": true
              },
              "body": {
                "contentType": "application/json",
                "body": "{\n  \"jsonrpc\": \"2.0\",\n  \"id\": \"check-access\",\n  \"method\": \"tools/call\",\n  \"params\": {\n    \"name\": \"check_access\",\n    \"arguments\": {\n      \"code\": \"<<mcpCode>>\"\n    }\n  }\n}"
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "200",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "mcp",
                  "active": true
                },
                {
                  "key": "igh.record",
                  "value": "true",
                  "active": true
                }
              ],
              "responses": {},
              "description": "`pending` until an administrator answers; `accepted` carries the credential (set as `<<token>>`) and the `claude mcp add` command that configures a client. Send it again until accepted (from the second poll on it first waits `pairInterval` seconds), or poll hands-free: this folder → Run Collection, select only this request, Iterations 120, Delay 0, **Keep variable values** and **Stop run if an error occurs** on.\n\n[Authentication and Accept agent](https://developers.igh.app/authentication.html) · [Getting started](https://developers.igh.app/getting-started.html)",
              "preRequestScript": "if (!hopp.env.get(\"mcpCode\")) {\n  throw new Error(\"Not sent: no mcpCode. Send 3 (request_access) first; after an answer start again from 3.\");\n}\nif (Number(hopp.env.get(\"pairPolls\") || 0) > 0) {\n  var seconds = Number(hopp.env.get(\"pairInterval\") || 5);\n  await new Promise(function (resolve) { setTimeout(resolve, seconds * 1000); });\n}",
              "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar body = answer() || {};\nvar result = body.result || {};\nvar content = result.structuredContent || {};\nvar polls = Number(hopp.env.get(\"pairPolls\") || 0) + 1;\nsave(\"pairPolls\", polls);\nif (content.status === \"accepted\") {\n  save(\"token\", content.credential);\n  save(\"tokenKind\", content.kind);\n  save(\"mcpCode\", \"\");\n  save(\"pairPolls\", 0);\n  console.log(\"Configure a client: \" + content.command);\n  hopp.test(\"accepted: token is set (\" + content.kind + \")\", function () { hopp.expect(content.credential).to.match(/^igh_(agent|session)_/); });\n} else if (content.status === \"pending\") {\n  if (content.slowDown) { save(\"pairInterval\", Number(hopp.env.get(\"pairInterval\") || 5) + 5); }\n  console.log(\"Waiting for an administrator (poll \" + polls + \"). Matching code: \" + hopp.env.get(\"pairUserCode\") + \". Send this request again: it waits pairInterval seconds first.\");\n} else {\n  console.log(\"check_access: \" + JSON.stringify(content));\n  save(\"mcpCode\", \"\");\n}"
            },
            {
              "v": "17",
              "_ref_id": "req_0e694479088bff9d1477973b",
              "name": "5. tools/list (with <<token>>)",
              "method": "POST",
              "endpoint": "<<recordUrl>>",
              "params": [],
              "headers": [
                {
                  "key": "Accept",
                  "value": "application/json, text/event-stream",
                  "active": true,
                  "description": ""
                },
                {
                  "key": "MCP-Protocol-Version",
                  "value": "2025-11-25",
                  "active": true,
                  "description": ""
                }
              ],
              "auth": {
                "authType": "inherit",
                "authActive": true
              },
              "body": {
                "contentType": "application/json",
                "body": "{\n  \"jsonrpc\": \"2.0\",\n  \"id\": \"tools-list-token\",\n  \"method\": \"tools/list\",\n  \"params\": {}\n}"
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "200",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "mcp",
                  "active": true
                },
                {
                  "key": "igh.record",
                  "value": "true",
                  "active": true
                }
              ],
              "responses": {},
              "description": "With the credential: every tool the integration's settings allow (automations, schedules, intents and renaming only when on).\n\n[Authentication and Accept agent](https://developers.igh.app/authentication.html) · [Getting started](https://developers.igh.app/getting-started.html)",
              "preRequestScript": "",
              "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar body = answer() || {};\nvar result = body.result || {};\nvar names = (result.tools || []).map(function (t) { return t.name; });\nconsole.log(\"Tools: \" + names.join(\", \"));\nhopp.test(\"describe_home is offered\", function () { hopp.expect(names).to.include(\"describe_home\"); });"
            },
            {
              "v": "17",
              "_ref_id": "req_f2180fc5e365bb38c3d33582",
              "name": "6. tools/call describe_home",
              "method": "POST",
              "endpoint": "<<recordUrl>>",
              "params": [],
              "headers": [
                {
                  "key": "Accept",
                  "value": "application/json, text/event-stream",
                  "active": true,
                  "description": ""
                },
                {
                  "key": "MCP-Protocol-Version",
                  "value": "2025-11-25",
                  "active": true,
                  "description": ""
                }
              ],
              "auth": {
                "authType": "inherit",
                "authActive": true
              },
              "body": {
                "contentType": "application/json",
                "body": "{\n  \"jsonrpc\": \"2.0\",\n  \"id\": \"describe-home\",\n  \"method\": \"tools/call\",\n  \"params\": {\n    \"name\": \"describe_home\",\n    \"arguments\": {}\n  }\n}"
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "200",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "mcp",
                  "active": true
                },
                {
                  "key": "igh.record",
                  "value": "true",
                  "active": true
                }
              ],
              "responses": {},
              "description": "The house as the integration shows it, through MCP (`GET /v1/house` behind it).\n\n[Authentication and Accept agent](https://developers.igh.app/authentication.html) · [Getting started](https://developers.igh.app/getting-started.html)",
              "preRequestScript": "",
              "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar body = answer() || {};\nvar result = body.result || {};\nvar content = result.structuredContent || {};\nhopp.test(\"not an error\", function () { hopp.expect(result.isError).to.not.equal(true); });\nconsole.log(\"House: \" + (content.house || \"?\"));"
            }
          ],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "headers": [],
          "variables": [],
          "preRequestScript": "",
          "testScript": ""
        },
        {
          "v": 12,
          "_ref_id": "coll_8c33b469a49913aefe20b0b8",
          "name": "(f) Negative cases",
          "description": "Credentials that must fail. Some need a setting changed in the IGH app first (named in the request); change it back afterwards.",
          "folders": [],
          "requests": [
            {
              "v": "17",
              "_ref_id": "req_d49b768608f7e2938aec4261",
              "name": "No token → 401",
              "method": "GET",
              "endpoint": "<<baseUrl>>/house",
              "params": [],
              "headers": [],
              "auth": {
                "authType": "none",
                "authActive": true
              },
              "body": {
                "contentType": null,
                "body": null
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "401",
                  "active": true
                },
                {
                  "key": "igh.code",
                  "value": "unauthorized",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "api",
                  "active": true
                }
              ],
              "responses": {},
              "description": "The API's 401 is the error body; the relay sends no `WWW-Authenticate` header (the console says whether one came). MCP's challenge is the next request.\n\n**`GET /v1/house`** — Floors, rooms, units (modules with their gangs), scenes; live state and operations.\n\n`GET /v1/house` answers the home as the integration shows it: `Floor`s → `Room`s → 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.\n\n- `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)).\n- `GET /v1/devices/{id}`: one unit, module, gang or scene.\n\nCall `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`.\n\n[Reference](https://developers.igh.app/reference.html#op-house) · [Getting started](https://developers.igh.app/getting-started.html)",
              "preRequestScript": "",
              "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar challenge = (hopp.response.headers || []).filter(function (h) { return String(h.key).toLowerCase() === \"www-authenticate\"; }).map(function (h) { return h.value; })[0];\nconsole.log(\"WWW-Authenticate: \" + (challenge || \"(none)\"));"
            },
            {
              "v": "17",
              "_ref_id": "req_ae0d9e28645d1d3484adf298",
              "name": "No token on an MCP tool → 401 with WWW-Authenticate (cloud only)",
              "method": "POST",
              "endpoint": "<<recordUrl>>",
              "params": [],
              "headers": [
                {
                  "key": "Accept",
                  "value": "application/json, text/event-stream",
                  "active": true,
                  "description": ""
                },
                {
                  "key": "MCP-Protocol-Version",
                  "value": "2025-11-25",
                  "active": true,
                  "description": ""
                }
              ],
              "auth": {
                "authType": "none",
                "authActive": true
              },
              "body": {
                "contentType": "application/json",
                "body": "{\n  \"jsonrpc\": \"2.0\",\n  \"id\": \"call\",\n  \"method\": \"tools/call\",\n  \"params\": {\n    \"name\": \"describe_home\",\n    \"arguments\": {}\n  }\n}"
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "401",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "plain",
                  "active": true
                },
                {
                  "key": "igh.record",
                  "value": "true",
                  "active": true
                }
              ],
              "responses": {},
              "description": "A tool that needs a credential, called without one: `WWW-Authenticate: Bearer error=\"invalid_token\", resource_metadata=…, scope=\"igh\"`, which is how an MCP client finds the OAuth server.\n\n[Authentication and Accept agent](https://developers.igh.app/authentication.html) · [Getting started](https://developers.igh.app/getting-started.html)",
              "preRequestScript": "",
              "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar challenge = (hopp.response.headers || []).filter(function (h) { return String(h.key).toLowerCase() === \"www-authenticate\"; }).map(function (h) { return h.value; })[0];\nconsole.log(\"WWW-Authenticate: \" + (challenge || \"(none)\"));\n\nhopp.test(\"the challenge names the resource metadata\", function () { hopp.expect(String(challenge)).to.include(\"resource_metadata=\"); });"
            },
            {
              "v": "17",
              "_ref_id": "req_9b67ea840c332cb454523369",
              "name": "Garbage token → 401",
              "method": "GET",
              "endpoint": "<<baseUrl>>/house",
              "params": [],
              "headers": [],
              "auth": {
                "authType": "bearer",
                "authActive": true,
                "token": "hoppscotch-garbage"
              },
              "body": {
                "contentType": null,
                "body": null
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "401",
                  "active": true
                },
                {
                  "key": "igh.code",
                  "value": "unauthorized",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "api",
                  "active": true
                }
              ],
              "responses": {},
              "description": "A bearer value that is no IGH credential.\n\n**`GET /v1/house`** — Floors, rooms, units (modules with their gangs), scenes; live state and operations.\n\n`GET /v1/house` answers the home as the integration shows it: `Floor`s → `Room`s → 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.\n\n- `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)).\n- `GET /v1/devices/{id}`: one unit, module, gang or scene.\n\nCall `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`.\n\n[Reference](https://developers.igh.app/reference.html#op-house) · [Getting started](https://developers.igh.app/getting-started.html)",
              "preRequestScript": "",
              "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar challenge = (hopp.response.headers || []).filter(function (h) { return String(h.key).toLowerCase() === \"www-authenticate\"; }).map(function (h) { return h.value; })[0];\nconsole.log(\"WWW-Authenticate: \" + (challenge || \"(none)\"));"
            },
            {
              "v": "17",
              "_ref_id": "req_af367c8b0af67df779eadcab",
              "name": "Revoked token → 401",
              "method": "GET",
              "endpoint": "<<baseUrl>>/house",
              "params": [],
              "headers": [],
              "auth": {
                "authType": "bearer",
                "authActive": true,
                "token": "<<revokedToken>>"
              },
              "body": {
                "contentType": null,
                "body": null
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "401",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "api",
                  "active": true
                },
                {
                  "key": "igh.needs",
                  "value": "revokedToken",
                  "active": true
                },
                {
                  "key": "igh.what",
                  "value": "a revoked credential",
                  "active": true
                }
              ],
              "responses": {},
              "description": "Put a credential that was revoked (a deleted key, a removed agent) into `revokedToken`. 401 `unauthorized` (a session that ended: `session_expired`).\n\n**`GET /v1/house`** — Floors, rooms, units (modules with their gangs), scenes; live state and operations.\n\n`GET /v1/house` answers the home as the integration shows it: `Floor`s → `Room`s → 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.\n\n- `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)).\n- `GET /v1/devices/{id}`: one unit, module, gang or scene.\n\nCall `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`.\n\n[Reference](https://developers.igh.app/reference.html#op-house) · [Getting started](https://developers.igh.app/getting-started.html)",
              "preRequestScript": "",
              "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar challenge = (hopp.response.headers || []).filter(function (h) { return String(h.key).toLowerCase() === \"www-authenticate\"; }).map(function (h) { return h.value; })[0];\nconsole.log(\"WWW-Authenticate: \" + (challenge || \"(none)\"));"
            },
            {
              "v": "17",
              "_ref_id": "req_1a95da15b7ab8e8e7e6d4e5c",
              "name": "Agent access off → 403 agent_access_disabled",
              "method": "GET",
              "endpoint": "<<baseUrl>>/integration",
              "params": [],
              "headers": [],
              "auth": {
                "authType": "inherit",
                "authActive": true
              },
              "body": {
                "contentType": null,
                "body": null
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "403",
                  "active": true
                },
                {
                  "key": "igh.code",
                  "value": "agent_access_disabled",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "api",
                  "active": true
                }
              ],
              "responses": {},
              "description": "With `<<token>>` an agent or session credential (b, c, d or e) and the integration's Agent access turned off in the app.\n\n**`GET /v1/integration`** — The integration this credential belongs to, the house, and its settings.\n\n[Reference](https://developers.igh.app/reference.html#op-integration) · [Concepts](https://developers.igh.app/overview.html)",
              "preRequestScript": "",
              "testScript": ""
            },
            {
              "v": "17",
              "_ref_id": "req_25779c8f92f275e5b82bb05e",
              "name": "Cloud access off → 403 cloud_access_disabled (cloud only)",
              "method": "GET",
              "endpoint": "<<baseUrl>>/integration",
              "params": [],
              "headers": [],
              "auth": {
                "authType": "inherit",
                "authActive": true
              },
              "body": {
                "contentType": null,
                "body": null
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "403",
                  "active": true
                },
                {
                  "key": "igh.code",
                  "value": "cloud_access_disabled",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "api",
                  "active": true
                },
                {
                  "key": "igh.only",
                  "value": "cloud",
                  "active": true
                }
              ],
              "responses": {},
              "description": "Through the cloud, with the integration's Cloud access turned off in the app (any credential, keys too). On the home network the same credential still works.\n\n**`GET /v1/integration`** — The integration this credential belongs to, the house, and its settings.\n\n[Reference](https://developers.igh.app/reference.html#op-integration) · [Concepts](https://developers.igh.app/overview.html)",
              "preRequestScript": "",
              "testScript": ""
            },
            {
              "v": "17",
              "_ref_id": "req_28a1057f4f2972f7f05cf857",
              "name": "Another integration's token on /r/{id} → 403 (cloud only)",
              "method": "POST",
              "endpoint": "<<recordUrl>>",
              "params": [],
              "headers": [
                {
                  "key": "Accept",
                  "value": "application/json, text/event-stream",
                  "active": true,
                  "description": ""
                },
                {
                  "key": "MCP-Protocol-Version",
                  "value": "2025-11-25",
                  "active": true,
                  "description": ""
                }
              ],
              "auth": {
                "authType": "bearer",
                "authActive": true,
                "token": "<<otherRecordToken>>"
              },
              "body": {
                "contentType": "application/json",
                "body": "{\n  \"jsonrpc\": \"2.0\",\n  \"id\": \"initialize\",\n  \"method\": \"initialize\",\n  \"params\": {\n    \"protocolVersion\": \"2025-11-25\",\n    \"capabilities\": {},\n    \"clientInfo\": {\n      \"name\": \"Hoppscotch\",\n      \"version\": \"1\"\n    }\n  }\n}"
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "403",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "plain",
                  "active": true
                },
                {
                  "key": "igh.record",
                  "value": "true",
                  "active": true
                },
                {
                  "key": "igh.needs",
                  "value": "otherRecordToken",
                  "active": true
                },
                {
                  "key": "igh.what",
                  "value": "another integration's credential",
                  "active": true
                }
              ],
              "responses": {},
              "description": "Put a credential of another API integration (of any home) into `otherRecordToken`: the per-integration address refuses it before any method.\n\n[Authentication and Accept agent](https://developers.igh.app/authentication.html) · [Getting started](https://developers.igh.app/getting-started.html)",
              "preRequestScript": "",
              "testScript": ""
            }
          ],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "headers": [],
          "variables": [],
          "preRequestScript": "",
          "testScript": ""
        }
      ],
      "requests": [],
      "auth": {
        "authType": "inherit",
        "authActive": true
      },
      "headers": [],
      "variables": [],
      "preRequestScript": "",
      "testScript": ""
    },
    {
      "v": 12,
      "_ref_id": "coll_c7a0649fd4d80d7648d0db1a",
      "name": "Getting started",
      "description": "Run these first: who the credential is, the house (it fills the id variables), the state now.",
      "folders": [],
      "requests": [
        {
          "v": "17",
          "_ref_id": "req_2359cceaaf8057ae87cdba83",
          "name": "About",
          "method": "GET",
          "endpoint": "<<baseUrl>>/about",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "none",
            "authActive": true
          },
          "body": {
            "contentType": null,
            "body": null
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            }
          ],
          "responses": {},
          "description": "Needs no credential.\n\n**`GET /v1/about`** — How to connect, and the app steps (static text).\n\n[Reference](https://developers.igh.app/reference.html#op-about) · [Agents](https://developers.igh.app/agents.html)",
          "preRequestScript": "",
          "testScript": ""
        },
        {
          "v": "17",
          "_ref_id": "req_5180be28d726179741490bd0",
          "name": "Integration (who am I)",
          "method": "GET",
          "endpoint": "<<baseUrl>>/integration",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": null,
            "body": null
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            }
          ],
          "responses": {},
          "description": "**`GET /v1/integration`** — The integration this credential belongs to, the house, and its settings.\n\n[Reference](https://developers.igh.app/reference.html#op-integration) · [Concepts](https://developers.igh.app/overview.html)",
          "preRequestScript": "",
          "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar body = answer();\nif (body && body.integration) {\n  var credential = body.integration.credential || {};\n  console.log(\"House \" + body.house + \", integration \" + body.integration.name + \" (\" + body.integration.id + \"), credential \" + credential.kind + \".\");\n  console.log(\"Settings: \" + JSON.stringify(body.integration.settings));\n  fill(\"serial\", String(body.gateway || \"\").toLowerCase());\n}"
        },
        {
          "v": "17",
          "_ref_id": "req_aaa43d36c8d55cc0593de814",
          "name": "House",
          "method": "GET",
          "endpoint": "<<baseUrl>>/house",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": null,
            "body": null
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            }
          ],
          "responses": {},
          "description": "Fills the id variables the other folders use (`deviceId`, `lightId`, `dimmerId`, `shutterId`, `acId`, `irId`, `lockId`, `gateId`, `roomId`, `sceneId`), each only while it is empty: clear one to pick again, or set it to the id you want.\n\n**`GET /v1/house`** — Floors, rooms, units (modules with their gangs), scenes; live state and operations.\n\n`GET /v1/house` answers the home as the integration shows it: `Floor`s → `Room`s → 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.\n\n- `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)).\n- `GET /v1/devices/{id}`: one unit, module, gang or scene.\n\nCall `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`.\n\n[Reference](https://developers.igh.app/reference.html#op-house) · [Getting started](https://developers.igh.app/getting-started.html)",
          "preRequestScript": "",
          "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar house = answer();\nif (house && house.floors) {\n  var units = [];\n  var rooms = [];\n  house.floors.forEach(function (floor) { floor.rooms.forEach(function (room) { rooms.push(room); }); });\n  (house.rooms || []).forEach(function (room) { rooms.push(room); });\n  rooms.forEach(function (room) { (room.units || []).forEach(function (unit) { units.push(unit); }); });\n  (house.units || []).forEach(function (unit) { units.push(unit); });\n  var devices = [];\n  units.forEach(function (unit) {\n    devices.push(unit);\n    (unit.gangs || []).forEach(function (gang) { devices.push(gang); });\n  });\n  var has = function (device, op) { return (device.operations || []).indexOf(op) !== -1; };\n  var first = function (test) { for (var i = 0; i < devices.length; i += 1) { if (test(devices[i])) { return devices[i]; } } return null; };\n  var light = first(function (d) { return has(d, \"set_power\") && !has(d, \"set_temp\") && !has(d, \"key\") && (!d.class || d.class.indexOf(\"light\") === 0); });\n  var pick = {\n    lightId: light,\n    dimmerId: first(function (d) { return has(d, \"set_brightness\"); }),\n    shutterId: first(function (d) { return has(d, \"set_position\"); }),\n    acId: first(function (d) { return has(d, \"set_ac_mode\"); }),\n    irId: first(function (d) { return has(d, \"key\"); }),\n    lockId: first(function (d) { return has(d, \"unlock\"); }),\n    gateId: first(function (d) { return d.class === \"gate\"; })\n  };\n  Object.keys(pick).forEach(function (key) { if (pick[key]) { fill(key, pick[key].id); } });\n  var device = light || first(function (d) { return (d.operations || []).length > 0; }) || devices[0];\n  if (device) {\n    fill(\"deviceId\", device.id);\n    fill(\"deviceName\", device.name);\n  }\n  if (hopp.env.get(\"deviceId\")) { save(\"unitRef\", hopp.env.get(\"deviceId\").split(\":\")[2]); }\n  if (rooms[0]) { fill(\"roomId\", rooms[0].id); fill(\"roomName\", rooms[0].name); }\n  if (house.scenes && house.scenes[0]) { fill(\"sceneId\", house.scenes[0].id); }\n  console.log(\"House \" + house.house + \" (gateway \" + house.gateway + \"): \" + devices.length + \" devices, \" + rooms.length + \" rooms, \" + (house.scenes || []).length + \" scenes.\");\n  [\"deviceId\", \"lightId\", \"dimmerId\", \"shutterId\", \"acId\", \"irId\", \"lockId\", \"gateId\", \"roomId\", \"sceneId\"].forEach(function (key) {\n    console.log(key + \" = \" + (hopp.env.get(key) || \"(none in this house)\"));\n  });\n}"
        },
        {
          "v": "17",
          "_ref_id": "req_8b7c9dc148cf54b0d5ba865e",
          "name": "State",
          "method": "GET",
          "endpoint": "<<baseUrl>>/state",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": null,
            "body": null
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            }
          ],
          "responses": {},
          "description": "**`GET /v1/state`** — Every exposed unit's and gang's state now.\n\n[Reference](https://developers.igh.app/reference.html#op-state) · [The house](https://developers.igh.app/resources.html)",
          "preRequestScript": "",
          "testScript": ""
        }
      ],
      "auth": {
        "authType": "inherit",
        "authActive": true
      },
      "headers": [],
      "variables": [],
      "preRequestScript": "",
      "testScript": ""
    },
    {
      "v": 12,
      "_ref_id": "coll_5dc4d91e6324a4f8c0ceb01c",
      "name": "Devices and control",
      "description": "One device, then one control request per class. Every request marked [switches a device] operates a real unit. The ids come from Getting started → House; set any of them to the device you want to try.",
      "folders": [
        {
          "v": 12,
          "_ref_id": "coll_b4b821d636ab3c1dc9f50b3c",
          "name": "Lights",
          "description": "**Switches a real device in the home.** Send it only when that is fine.",
          "folders": [],
          "requests": [
            {
              "v": "17",
              "_ref_id": "req_32b648592886605b644aaa79",
              "name": "Light on [switches a device]",
              "method": "POST",
              "endpoint": "<<baseUrl>>/devices/<<lightId>>/control",
              "params": [],
              "headers": [],
              "auth": {
                "authType": "inherit",
                "authActive": true
              },
              "body": {
                "contentType": "application/json",
                "body": "{\n  \"op\": \"set_power\",\n  \"args\": {\n    \"on\": true\n  }\n}"
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "200",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "api",
                  "active": true
                },
                {
                  "key": "igh.needs",
                  "value": "lightId",
                  "active": true
                },
                {
                  "key": "igh.what",
                  "value": "light",
                  "active": true
                }
              ],
              "responses": {},
              "description": "**Switches a real device in the home.** Send it only when that is fine. The light in `<<lightId>>`.\n\n**`POST /v1/devices/{id}/control`** — One operation: set state, unit mode (`set_mode`), run a scene.\n\n`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`):\n\n| Operation | Args |\n| --- | --- |\n| `set_power` | `{ \"on\": true }` |\n| `set_brightness` | `{ \"bri\": 0–100 }` |\n| `set_position` | `{ \"pos\": 0–100 }` (shutters, curtains) |\n| `open`, `close`, `stop` | none (`open` / `close` go all the way, even while the cover moves) |\n| `up_toggle`, `down_toggle` | none (shutters, curtains: stop if moving that way, else go) |\n| `flip`, `down_flip` | none (a shutter whose flip time is set, else `422`: tilt the slats / close, then tilt) |\n| `set_mode` | `{ \"mode\": \"auto\" \\| \"eco\" \\| \"manual\" }` (the unit's mode; `sleep` is not available) |\n| `set_temp`, `set_ac_mode`, `set_fan` | `{ \"temp\": 24 }`, `{ \"mode\": \"cool\" }`, `{ \"fan\": \"auto\" }` |\n| `key` | `{ \"key\": \"<remote key>\" }` (IR remotes) |\n| `run` | none (a scene; also `POST /v1/scenes/{id}/run`, `POST /v1/scenes/{id}/run`) |\n\n- Values out of range are `422` `validation`.\n- **Locks and gates**: unlocking or opening needs `allow_unlock: on` (else `403` `unlock_not_allowed`) and, for an agent, the user's explicit confirmation every time.\n- A unit that doesn't answer over IGHC is `504` `unit_unreachable`; when the gateway's IGHC adapter can't carry frames, `503` `ighc_unavailable`.\n- Every change is logged with the credential's name (see `GET /v1/activity`).\n\n[Reference](https://developers.igh.app/reference.html#op-control) · [Controlling devices](https://developers.igh.app/resources.html)",
              "preRequestScript": "",
              "testScript": ""
            },
            {
              "v": "17",
              "_ref_id": "req_2d6f957c9ff2d7d0373c2cf4",
              "name": "Light off [switches a device]",
              "method": "POST",
              "endpoint": "<<baseUrl>>/devices/<<lightId>>/control",
              "params": [],
              "headers": [],
              "auth": {
                "authType": "inherit",
                "authActive": true
              },
              "body": {
                "contentType": "application/json",
                "body": "{\n  \"op\": \"set_power\",\n  \"args\": {\n    \"on\": false\n  }\n}"
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "200",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "api",
                  "active": true
                },
                {
                  "key": "igh.needs",
                  "value": "lightId",
                  "active": true
                },
                {
                  "key": "igh.what",
                  "value": "light",
                  "active": true
                }
              ],
              "responses": {},
              "description": "**Switches a real device in the home.** Send it only when that is fine. The light in `<<lightId>>`.\n\n**`POST /v1/devices/{id}/control`** — One operation: set state, unit mode (`set_mode`), run a scene.\n\n`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`):\n\n| Operation | Args |\n| --- | --- |\n| `set_power` | `{ \"on\": true }` |\n| `set_brightness` | `{ \"bri\": 0–100 }` |\n| `set_position` | `{ \"pos\": 0–100 }` (shutters, curtains) |\n| `open`, `close`, `stop` | none (`open` / `close` go all the way, even while the cover moves) |\n| `up_toggle`, `down_toggle` | none (shutters, curtains: stop if moving that way, else go) |\n| `flip`, `down_flip` | none (a shutter whose flip time is set, else `422`: tilt the slats / close, then tilt) |\n| `set_mode` | `{ \"mode\": \"auto\" \\| \"eco\" \\| \"manual\" }` (the unit's mode; `sleep` is not available) |\n| `set_temp`, `set_ac_mode`, `set_fan` | `{ \"temp\": 24 }`, `{ \"mode\": \"cool\" }`, `{ \"fan\": \"auto\" }` |\n| `key` | `{ \"key\": \"<remote key>\" }` (IR remotes) |\n| `run` | none (a scene; also `POST /v1/scenes/{id}/run`, `POST /v1/scenes/{id}/run`) |\n\n- Values out of range are `422` `validation`.\n- **Locks and gates**: unlocking or opening needs `allow_unlock: on` (else `403` `unlock_not_allowed`) and, for an agent, the user's explicit confirmation every time.\n- A unit that doesn't answer over IGHC is `504` `unit_unreachable`; when the gateway's IGHC adapter can't carry frames, `503` `ighc_unavailable`.\n- Every change is logged with the credential's name (see `GET /v1/activity`).\n\n[Reference](https://developers.igh.app/reference.html#op-control) · [Controlling devices](https://developers.igh.app/resources.html)",
              "preRequestScript": "",
              "testScript": ""
            },
            {
              "v": "17",
              "_ref_id": "req_852780e4b5a96d987281e110",
              "name": "Light level 40% [switches a device]",
              "method": "POST",
              "endpoint": "<<baseUrl>>/devices/<<dimmerId>>/control",
              "params": [],
              "headers": [],
              "auth": {
                "authType": "inherit",
                "authActive": true
              },
              "body": {
                "contentType": "application/json",
                "body": "{\n  \"op\": \"set_brightness\",\n  \"args\": {\n    \"bri\": 40\n  }\n}"
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "200",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "api",
                  "active": true
                },
                {
                  "key": "igh.needs",
                  "value": "dimmerId",
                  "active": true
                },
                {
                  "key": "igh.what",
                  "value": "dimmer",
                  "active": true
                }
              ],
              "responses": {},
              "description": "**Switches a real device in the home.** Send it only when that is fine. The dimmer in `<<dimmerId>>`.\n\n**`POST /v1/devices/{id}/control`** — One operation: set state, unit mode (`set_mode`), run a scene.\n\n`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`):\n\n| Operation | Args |\n| --- | --- |\n| `set_power` | `{ \"on\": true }` |\n| `set_brightness` | `{ \"bri\": 0–100 }` |\n| `set_position` | `{ \"pos\": 0–100 }` (shutters, curtains) |\n| `open`, `close`, `stop` | none (`open` / `close` go all the way, even while the cover moves) |\n| `up_toggle`, `down_toggle` | none (shutters, curtains: stop if moving that way, else go) |\n| `flip`, `down_flip` | none (a shutter whose flip time is set, else `422`: tilt the slats / close, then tilt) |\n| `set_mode` | `{ \"mode\": \"auto\" \\| \"eco\" \\| \"manual\" }` (the unit's mode; `sleep` is not available) |\n| `set_temp`, `set_ac_mode`, `set_fan` | `{ \"temp\": 24 }`, `{ \"mode\": \"cool\" }`, `{ \"fan\": \"auto\" }` |\n| `key` | `{ \"key\": \"<remote key>\" }` (IR remotes) |\n| `run` | none (a scene; also `POST /v1/scenes/{id}/run`, `POST /v1/scenes/{id}/run`) |\n\n- Values out of range are `422` `validation`.\n- **Locks and gates**: unlocking or opening needs `allow_unlock: on` (else `403` `unlock_not_allowed`) and, for an agent, the user's explicit confirmation every time.\n- A unit that doesn't answer over IGHC is `504` `unit_unreachable`; when the gateway's IGHC adapter can't carry frames, `503` `ighc_unavailable`.\n- Every change is logged with the credential's name (see `GET /v1/activity`).\n\n[Reference](https://developers.igh.app/reference.html#op-control) · [Controlling devices](https://developers.igh.app/resources.html)",
              "preRequestScript": "",
              "testScript": ""
            }
          ],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "headers": [],
          "variables": [],
          "preRequestScript": "",
          "testScript": ""
        },
        {
          "v": 12,
          "_ref_id": "coll_b7bbbcbf2e135b6bb919f428",
          "name": "Shutters",
          "description": "**Switches a real device in the home.** Send it only when that is fine.",
          "folders": [],
          "requests": [
            {
              "v": "17",
              "_ref_id": "req_a98eded8ae080a1638823388",
              "name": "Shutter to 50% [switches a device]",
              "method": "POST",
              "endpoint": "<<baseUrl>>/devices/<<shutterId>>/control",
              "params": [],
              "headers": [],
              "auth": {
                "authType": "inherit",
                "authActive": true
              },
              "body": {
                "contentType": "application/json",
                "body": "{\n  \"op\": \"set_position\",\n  \"args\": {\n    \"pos\": 50\n  }\n}"
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "200",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "api",
                  "active": true
                },
                {
                  "key": "igh.needs",
                  "value": "shutterId",
                  "active": true
                },
                {
                  "key": "igh.what",
                  "value": "shutter",
                  "active": true
                }
              ],
              "responses": {},
              "description": "**Switches a real device in the home.** Send it only when that is fine. The shutter in `<<shutterId>>`.\n\n**`POST /v1/devices/{id}/control`** — One operation: set state, unit mode (`set_mode`), run a scene.\n\n`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`):\n\n| Operation | Args |\n| --- | --- |\n| `set_power` | `{ \"on\": true }` |\n| `set_brightness` | `{ \"bri\": 0–100 }` |\n| `set_position` | `{ \"pos\": 0–100 }` (shutters, curtains) |\n| `open`, `close`, `stop` | none (`open` / `close` go all the way, even while the cover moves) |\n| `up_toggle`, `down_toggle` | none (shutters, curtains: stop if moving that way, else go) |\n| `flip`, `down_flip` | none (a shutter whose flip time is set, else `422`: tilt the slats / close, then tilt) |\n| `set_mode` | `{ \"mode\": \"auto\" \\| \"eco\" \\| \"manual\" }` (the unit's mode; `sleep` is not available) |\n| `set_temp`, `set_ac_mode`, `set_fan` | `{ \"temp\": 24 }`, `{ \"mode\": \"cool\" }`, `{ \"fan\": \"auto\" }` |\n| `key` | `{ \"key\": \"<remote key>\" }` (IR remotes) |\n| `run` | none (a scene; also `POST /v1/scenes/{id}/run`, `POST /v1/scenes/{id}/run`) |\n\n- Values out of range are `422` `validation`.\n- **Locks and gates**: unlocking or opening needs `allow_unlock: on` (else `403` `unlock_not_allowed`) and, for an agent, the user's explicit confirmation every time.\n- A unit that doesn't answer over IGHC is `504` `unit_unreachable`; when the gateway's IGHC adapter can't carry frames, `503` `ighc_unavailable`.\n- Every change is logged with the credential's name (see `GET /v1/activity`).\n\n[Reference](https://developers.igh.app/reference.html#op-control) · [Controlling devices](https://developers.igh.app/resources.html)",
              "preRequestScript": "",
              "testScript": ""
            },
            {
              "v": "17",
              "_ref_id": "req_5832103dc76e86b39348a024",
              "name": "Shutter close [switches a device]",
              "method": "POST",
              "endpoint": "<<baseUrl>>/devices/<<shutterId>>/control",
              "params": [],
              "headers": [],
              "auth": {
                "authType": "inherit",
                "authActive": true
              },
              "body": {
                "contentType": "application/json",
                "body": "{\n  \"op\": \"close\"\n}"
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "200",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "api",
                  "active": true
                },
                {
                  "key": "igh.needs",
                  "value": "shutterId",
                  "active": true
                },
                {
                  "key": "igh.what",
                  "value": "shutter",
                  "active": true
                }
              ],
              "responses": {},
              "description": "**Switches a real device in the home.** Send it only when that is fine. The shutter in `<<shutterId>>`.\n\n**`POST /v1/devices/{id}/control`** — One operation: set state, unit mode (`set_mode`), run a scene.\n\n`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`):\n\n| Operation | Args |\n| --- | --- |\n| `set_power` | `{ \"on\": true }` |\n| `set_brightness` | `{ \"bri\": 0–100 }` |\n| `set_position` | `{ \"pos\": 0–100 }` (shutters, curtains) |\n| `open`, `close`, `stop` | none (`open` / `close` go all the way, even while the cover moves) |\n| `up_toggle`, `down_toggle` | none (shutters, curtains: stop if moving that way, else go) |\n| `flip`, `down_flip` | none (a shutter whose flip time is set, else `422`: tilt the slats / close, then tilt) |\n| `set_mode` | `{ \"mode\": \"auto\" \\| \"eco\" \\| \"manual\" }` (the unit's mode; `sleep` is not available) |\n| `set_temp`, `set_ac_mode`, `set_fan` | `{ \"temp\": 24 }`, `{ \"mode\": \"cool\" }`, `{ \"fan\": \"auto\" }` |\n| `key` | `{ \"key\": \"<remote key>\" }` (IR remotes) |\n| `run` | none (a scene; also `POST /v1/scenes/{id}/run`, `POST /v1/scenes/{id}/run`) |\n\n- Values out of range are `422` `validation`.\n- **Locks and gates**: unlocking or opening needs `allow_unlock: on` (else `403` `unlock_not_allowed`) and, for an agent, the user's explicit confirmation every time.\n- A unit that doesn't answer over IGHC is `504` `unit_unreachable`; when the gateway's IGHC adapter can't carry frames, `503` `ighc_unavailable`.\n- Every change is logged with the credential's name (see `GET /v1/activity`).\n\n[Reference](https://developers.igh.app/reference.html#op-control) · [Controlling devices](https://developers.igh.app/resources.html)",
              "preRequestScript": "",
              "testScript": ""
            },
            {
              "v": "17",
              "_ref_id": "req_85d4b4504fb3ee990f41da53",
              "name": "Shutter stop [switches a device]",
              "method": "POST",
              "endpoint": "<<baseUrl>>/devices/<<shutterId>>/control",
              "params": [],
              "headers": [],
              "auth": {
                "authType": "inherit",
                "authActive": true
              },
              "body": {
                "contentType": "application/json",
                "body": "{\n  \"op\": \"stop\"\n}"
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "200",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "api",
                  "active": true
                },
                {
                  "key": "igh.needs",
                  "value": "shutterId",
                  "active": true
                },
                {
                  "key": "igh.what",
                  "value": "shutter",
                  "active": true
                }
              ],
              "responses": {},
              "description": "**Switches a real device in the home.** Send it only when that is fine. The shutter in `<<shutterId>>`.\n\n**`POST /v1/devices/{id}/control`** — One operation: set state, unit mode (`set_mode`), run a scene.\n\n`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`):\n\n| Operation | Args |\n| --- | --- |\n| `set_power` | `{ \"on\": true }` |\n| `set_brightness` | `{ \"bri\": 0–100 }` |\n| `set_position` | `{ \"pos\": 0–100 }` (shutters, curtains) |\n| `open`, `close`, `stop` | none (`open` / `close` go all the way, even while the cover moves) |\n| `up_toggle`, `down_toggle` | none (shutters, curtains: stop if moving that way, else go) |\n| `flip`, `down_flip` | none (a shutter whose flip time is set, else `422`: tilt the slats / close, then tilt) |\n| `set_mode` | `{ \"mode\": \"auto\" \\| \"eco\" \\| \"manual\" }` (the unit's mode; `sleep` is not available) |\n| `set_temp`, `set_ac_mode`, `set_fan` | `{ \"temp\": 24 }`, `{ \"mode\": \"cool\" }`, `{ \"fan\": \"auto\" }` |\n| `key` | `{ \"key\": \"<remote key>\" }` (IR remotes) |\n| `run` | none (a scene; also `POST /v1/scenes/{id}/run`, `POST /v1/scenes/{id}/run`) |\n\n- Values out of range are `422` `validation`.\n- **Locks and gates**: unlocking or opening needs `allow_unlock: on` (else `403` `unlock_not_allowed`) and, for an agent, the user's explicit confirmation every time.\n- A unit that doesn't answer over IGHC is `504` `unit_unreachable`; when the gateway's IGHC adapter can't carry frames, `503` `ighc_unavailable`.\n- Every change is logged with the credential's name (see `GET /v1/activity`).\n\n[Reference](https://developers.igh.app/reference.html#op-control) · [Controlling devices](https://developers.igh.app/resources.html)",
              "preRequestScript": "",
              "testScript": ""
            }
          ],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "headers": [],
          "variables": [],
          "preRequestScript": "",
          "testScript": ""
        },
        {
          "v": 12,
          "_ref_id": "coll_62445432e15b9a6d13b2b647",
          "name": "Air conditioning",
          "description": "**Switches a real device in the home.** Send it only when that is fine. The values a unit takes are in its state and the app.",
          "folders": [],
          "requests": [
            {
              "v": "17",
              "_ref_id": "req_95e3991bbcd876b81989d2f4",
              "name": "AC mode cool [switches a device]",
              "method": "POST",
              "endpoint": "<<baseUrl>>/devices/<<acId>>/control",
              "params": [],
              "headers": [],
              "auth": {
                "authType": "inherit",
                "authActive": true
              },
              "body": {
                "contentType": "application/json",
                "body": "{\n  \"op\": \"set_ac_mode\",\n  \"args\": {\n    \"mode\": \"cool\"\n  }\n}"
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "200",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "api",
                  "active": true
                },
                {
                  "key": "igh.needs",
                  "value": "acId",
                  "active": true
                },
                {
                  "key": "igh.what",
                  "value": "air conditioner",
                  "active": true
                }
              ],
              "responses": {},
              "description": "**Switches a real device in the home.** Send it only when that is fine. The air conditioner in `<<acId>>`.\n\n**`POST /v1/devices/{id}/control`** — One operation: set state, unit mode (`set_mode`), run a scene.\n\n`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`):\n\n| Operation | Args |\n| --- | --- |\n| `set_power` | `{ \"on\": true }` |\n| `set_brightness` | `{ \"bri\": 0–100 }` |\n| `set_position` | `{ \"pos\": 0–100 }` (shutters, curtains) |\n| `open`, `close`, `stop` | none (`open` / `close` go all the way, even while the cover moves) |\n| `up_toggle`, `down_toggle` | none (shutters, curtains: stop if moving that way, else go) |\n| `flip`, `down_flip` | none (a shutter whose flip time is set, else `422`: tilt the slats / close, then tilt) |\n| `set_mode` | `{ \"mode\": \"auto\" \\| \"eco\" \\| \"manual\" }` (the unit's mode; `sleep` is not available) |\n| `set_temp`, `set_ac_mode`, `set_fan` | `{ \"temp\": 24 }`, `{ \"mode\": \"cool\" }`, `{ \"fan\": \"auto\" }` |\n| `key` | `{ \"key\": \"<remote key>\" }` (IR remotes) |\n| `run` | none (a scene; also `POST /v1/scenes/{id}/run`, `POST /v1/scenes/{id}/run`) |\n\n- Values out of range are `422` `validation`.\n- **Locks and gates**: unlocking or opening needs `allow_unlock: on` (else `403` `unlock_not_allowed`) and, for an agent, the user's explicit confirmation every time.\n- A unit that doesn't answer over IGHC is `504` `unit_unreachable`; when the gateway's IGHC adapter can't carry frames, `503` `ighc_unavailable`.\n- Every change is logged with the credential's name (see `GET /v1/activity`).\n\n[Reference](https://developers.igh.app/reference.html#op-control) · [Controlling devices](https://developers.igh.app/resources.html)",
              "preRequestScript": "",
              "testScript": ""
            },
            {
              "v": "17",
              "_ref_id": "req_858aa14b47d45676660ecfbb",
              "name": "AC setpoint 24 °C [switches a device]",
              "method": "POST",
              "endpoint": "<<baseUrl>>/devices/<<acId>>/control",
              "params": [],
              "headers": [],
              "auth": {
                "authType": "inherit",
                "authActive": true
              },
              "body": {
                "contentType": "application/json",
                "body": "{\n  \"op\": \"set_temp\",\n  \"args\": {\n    \"temp\": 24\n  }\n}"
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "200",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "api",
                  "active": true
                },
                {
                  "key": "igh.needs",
                  "value": "acId",
                  "active": true
                },
                {
                  "key": "igh.what",
                  "value": "air conditioner",
                  "active": true
                }
              ],
              "responses": {},
              "description": "**Switches a real device in the home.** Send it only when that is fine. The air conditioner in `<<acId>>`.\n\n**`POST /v1/devices/{id}/control`** — One operation: set state, unit mode (`set_mode`), run a scene.\n\n`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`):\n\n| Operation | Args |\n| --- | --- |\n| `set_power` | `{ \"on\": true }` |\n| `set_brightness` | `{ \"bri\": 0–100 }` |\n| `set_position` | `{ \"pos\": 0–100 }` (shutters, curtains) |\n| `open`, `close`, `stop` | none (`open` / `close` go all the way, even while the cover moves) |\n| `up_toggle`, `down_toggle` | none (shutters, curtains: stop if moving that way, else go) |\n| `flip`, `down_flip` | none (a shutter whose flip time is set, else `422`: tilt the slats / close, then tilt) |\n| `set_mode` | `{ \"mode\": \"auto\" \\| \"eco\" \\| \"manual\" }` (the unit's mode; `sleep` is not available) |\n| `set_temp`, `set_ac_mode`, `set_fan` | `{ \"temp\": 24 }`, `{ \"mode\": \"cool\" }`, `{ \"fan\": \"auto\" }` |\n| `key` | `{ \"key\": \"<remote key>\" }` (IR remotes) |\n| `run` | none (a scene; also `POST /v1/scenes/{id}/run`, `POST /v1/scenes/{id}/run`) |\n\n- Values out of range are `422` `validation`.\n- **Locks and gates**: unlocking or opening needs `allow_unlock: on` (else `403` `unlock_not_allowed`) and, for an agent, the user's explicit confirmation every time.\n- A unit that doesn't answer over IGHC is `504` `unit_unreachable`; when the gateway's IGHC adapter can't carry frames, `503` `ighc_unavailable`.\n- Every change is logged with the credential's name (see `GET /v1/activity`).\n\n[Reference](https://developers.igh.app/reference.html#op-control) · [Controlling devices](https://developers.igh.app/resources.html)",
              "preRequestScript": "",
              "testScript": ""
            },
            {
              "v": "17",
              "_ref_id": "req_08cdf2fda4d853232f8c5505",
              "name": "AC fan auto [switches a device]",
              "method": "POST",
              "endpoint": "<<baseUrl>>/devices/<<acId>>/control",
              "params": [],
              "headers": [],
              "auth": {
                "authType": "inherit",
                "authActive": true
              },
              "body": {
                "contentType": "application/json",
                "body": "{\n  \"op\": \"set_fan\",\n  \"args\": {\n    \"fan\": \"auto\"\n  }\n}"
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "200",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "api",
                  "active": true
                },
                {
                  "key": "igh.needs",
                  "value": "acId",
                  "active": true
                },
                {
                  "key": "igh.what",
                  "value": "air conditioner",
                  "active": true
                }
              ],
              "responses": {},
              "description": "**Switches a real device in the home.** Send it only when that is fine. The air conditioner in `<<acId>>`.\n\n**`POST /v1/devices/{id}/control`** — One operation: set state, unit mode (`set_mode`), run a scene.\n\n`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`):\n\n| Operation | Args |\n| --- | --- |\n| `set_power` | `{ \"on\": true }` |\n| `set_brightness` | `{ \"bri\": 0–100 }` |\n| `set_position` | `{ \"pos\": 0–100 }` (shutters, curtains) |\n| `open`, `close`, `stop` | none (`open` / `close` go all the way, even while the cover moves) |\n| `up_toggle`, `down_toggle` | none (shutters, curtains: stop if moving that way, else go) |\n| `flip`, `down_flip` | none (a shutter whose flip time is set, else `422`: tilt the slats / close, then tilt) |\n| `set_mode` | `{ \"mode\": \"auto\" \\| \"eco\" \\| \"manual\" }` (the unit's mode; `sleep` is not available) |\n| `set_temp`, `set_ac_mode`, `set_fan` | `{ \"temp\": 24 }`, `{ \"mode\": \"cool\" }`, `{ \"fan\": \"auto\" }` |\n| `key` | `{ \"key\": \"<remote key>\" }` (IR remotes) |\n| `run` | none (a scene; also `POST /v1/scenes/{id}/run`, `POST /v1/scenes/{id}/run`) |\n\n- Values out of range are `422` `validation`.\n- **Locks and gates**: unlocking or opening needs `allow_unlock: on` (else `403` `unlock_not_allowed`) and, for an agent, the user's explicit confirmation every time.\n- A unit that doesn't answer over IGHC is `504` `unit_unreachable`; when the gateway's IGHC adapter can't carry frames, `503` `ighc_unavailable`.\n- Every change is logged with the credential's name (see `GET /v1/activity`).\n\n[Reference](https://developers.igh.app/reference.html#op-control) · [Controlling devices](https://developers.igh.app/resources.html)",
              "preRequestScript": "",
              "testScript": ""
            }
          ],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "headers": [],
          "variables": [],
          "preRequestScript": "",
          "testScript": ""
        },
        {
          "v": 12,
          "_ref_id": "coll_faa08cd0456594db6025246b",
          "name": "IR remote",
          "description": "**Switches a real device in the home.** Send it only when that is fine. `irKey` is one of the keys the remote learned (as the app names them).",
          "folders": [],
          "requests": [
            {
              "v": "17",
              "_ref_id": "req_2768f11e41799ac752604913",
              "name": "IR key [switches a device]",
              "method": "POST",
              "endpoint": "<<baseUrl>>/devices/<<irId>>/control",
              "params": [],
              "headers": [],
              "auth": {
                "authType": "inherit",
                "authActive": true
              },
              "body": {
                "contentType": "application/json",
                "body": "{\n  \"op\": \"key\",\n  \"args\": {\n    \"key\": \"<<irKey>>\"\n  }\n}"
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "200",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "api",
                  "active": true
                },
                {
                  "key": "igh.needs",
                  "value": "irId",
                  "active": true
                },
                {
                  "key": "igh.what",
                  "value": "IR remote",
                  "active": true
                }
              ],
              "responses": {},
              "description": "**Switches a real device in the home.** Send it only when that is fine. The IR remote in `<<irId>>`.\n\n**`POST /v1/devices/{id}/control`** — One operation: set state, unit mode (`set_mode`), run a scene.\n\n`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`):\n\n| Operation | Args |\n| --- | --- |\n| `set_power` | `{ \"on\": true }` |\n| `set_brightness` | `{ \"bri\": 0–100 }` |\n| `set_position` | `{ \"pos\": 0–100 }` (shutters, curtains) |\n| `open`, `close`, `stop` | none (`open` / `close` go all the way, even while the cover moves) |\n| `up_toggle`, `down_toggle` | none (shutters, curtains: stop if moving that way, else go) |\n| `flip`, `down_flip` | none (a shutter whose flip time is set, else `422`: tilt the slats / close, then tilt) |\n| `set_mode` | `{ \"mode\": \"auto\" \\| \"eco\" \\| \"manual\" }` (the unit's mode; `sleep` is not available) |\n| `set_temp`, `set_ac_mode`, `set_fan` | `{ \"temp\": 24 }`, `{ \"mode\": \"cool\" }`, `{ \"fan\": \"auto\" }` |\n| `key` | `{ \"key\": \"<remote key>\" }` (IR remotes) |\n| `run` | none (a scene; also `POST /v1/scenes/{id}/run`, `POST /v1/scenes/{id}/run`) |\n\n- Values out of range are `422` `validation`.\n- **Locks and gates**: unlocking or opening needs `allow_unlock: on` (else `403` `unlock_not_allowed`) and, for an agent, the user's explicit confirmation every time.\n- A unit that doesn't answer over IGHC is `504` `unit_unreachable`; when the gateway's IGHC adapter can't carry frames, `503` `ighc_unavailable`.\n- Every change is logged with the credential's name (see `GET /v1/activity`).\n\n[Reference](https://developers.igh.app/reference.html#op-control) · [Controlling devices](https://developers.igh.app/resources.html)",
              "preRequestScript": "",
              "testScript": ""
            }
          ],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "headers": [],
          "variables": [],
          "preRequestScript": "",
          "testScript": ""
        },
        {
          "v": 12,
          "_ref_id": "coll_38af9f14c0f3f20a06ae5f9a",
          "name": "Locks and gates",
          "description": "**Switches a real device in the home.** Send it only when that is fine. Unlocking and opening need the integration's Unlocking on; otherwise 403 `unlock_not_allowed`.",
          "folders": [],
          "requests": [
            {
              "v": "17",
              "_ref_id": "req_ae1ce1bf52f1aeca52fdb94d",
              "name": "Lock [switches a device]",
              "method": "POST",
              "endpoint": "<<baseUrl>>/devices/<<lockId>>/control",
              "params": [],
              "headers": [],
              "auth": {
                "authType": "inherit",
                "authActive": true
              },
              "body": {
                "contentType": "application/json",
                "body": "{\n  \"op\": \"lock\"\n}"
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "200",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "api",
                  "active": true
                },
                {
                  "key": "igh.needs",
                  "value": "lockId",
                  "active": true
                },
                {
                  "key": "igh.what",
                  "value": "lock",
                  "active": true
                }
              ],
              "responses": {},
              "description": "**Switches a real device in the home.** Send it only when that is fine. The lock in `<<lockId>>`.\n\n**`POST /v1/devices/{id}/control`** — One operation: set state, unit mode (`set_mode`), run a scene.\n\n`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`):\n\n| Operation | Args |\n| --- | --- |\n| `set_power` | `{ \"on\": true }` |\n| `set_brightness` | `{ \"bri\": 0–100 }` |\n| `set_position` | `{ \"pos\": 0–100 }` (shutters, curtains) |\n| `open`, `close`, `stop` | none (`open` / `close` go all the way, even while the cover moves) |\n| `up_toggle`, `down_toggle` | none (shutters, curtains: stop if moving that way, else go) |\n| `flip`, `down_flip` | none (a shutter whose flip time is set, else `422`: tilt the slats / close, then tilt) |\n| `set_mode` | `{ \"mode\": \"auto\" \\| \"eco\" \\| \"manual\" }` (the unit's mode; `sleep` is not available) |\n| `set_temp`, `set_ac_mode`, `set_fan` | `{ \"temp\": 24 }`, `{ \"mode\": \"cool\" }`, `{ \"fan\": \"auto\" }` |\n| `key` | `{ \"key\": \"<remote key>\" }` (IR remotes) |\n| `run` | none (a scene; also `POST /v1/scenes/{id}/run`, `POST /v1/scenes/{id}/run`) |\n\n- Values out of range are `422` `validation`.\n- **Locks and gates**: unlocking or opening needs `allow_unlock: on` (else `403` `unlock_not_allowed`) and, for an agent, the user's explicit confirmation every time.\n- A unit that doesn't answer over IGHC is `504` `unit_unreachable`; when the gateway's IGHC adapter can't carry frames, `503` `ighc_unavailable`.\n- Every change is logged with the credential's name (see `GET /v1/activity`).\n\n[Reference](https://developers.igh.app/reference.html#op-control) · [Controlling devices](https://developers.igh.app/resources.html)",
              "preRequestScript": "",
              "testScript": ""
            },
            {
              "v": "17",
              "_ref_id": "req_d5a10f20de5cf6f6b413e97e",
              "name": "Unlock [switches a device]",
              "method": "POST",
              "endpoint": "<<baseUrl>>/devices/<<lockId>>/control",
              "params": [],
              "headers": [],
              "auth": {
                "authType": "inherit",
                "authActive": true
              },
              "body": {
                "contentType": "application/json",
                "body": "{\n  \"op\": \"unlock\"\n}"
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "200,403",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "api",
                  "active": true
                },
                {
                  "key": "igh.needs",
                  "value": "lockId",
                  "active": true
                },
                {
                  "key": "igh.what",
                  "value": "lock",
                  "active": true
                }
              ],
              "responses": {},
              "description": "**Switches a real device in the home.** Send it only when that is fine. The lock in `<<lockId>>`.\n\n**`POST /v1/devices/{id}/control`** — One operation: set state, unit mode (`set_mode`), run a scene.\n\n`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`):\n\n| Operation | Args |\n| --- | --- |\n| `set_power` | `{ \"on\": true }` |\n| `set_brightness` | `{ \"bri\": 0–100 }` |\n| `set_position` | `{ \"pos\": 0–100 }` (shutters, curtains) |\n| `open`, `close`, `stop` | none (`open` / `close` go all the way, even while the cover moves) |\n| `up_toggle`, `down_toggle` | none (shutters, curtains: stop if moving that way, else go) |\n| `flip`, `down_flip` | none (a shutter whose flip time is set, else `422`: tilt the slats / close, then tilt) |\n| `set_mode` | `{ \"mode\": \"auto\" \\| \"eco\" \\| \"manual\" }` (the unit's mode; `sleep` is not available) |\n| `set_temp`, `set_ac_mode`, `set_fan` | `{ \"temp\": 24 }`, `{ \"mode\": \"cool\" }`, `{ \"fan\": \"auto\" }` |\n| `key` | `{ \"key\": \"<remote key>\" }` (IR remotes) |\n| `run` | none (a scene; also `POST /v1/scenes/{id}/run`, `POST /v1/scenes/{id}/run`) |\n\n- Values out of range are `422` `validation`.\n- **Locks and gates**: unlocking or opening needs `allow_unlock: on` (else `403` `unlock_not_allowed`) and, for an agent, the user's explicit confirmation every time.\n- A unit that doesn't answer over IGHC is `504` `unit_unreachable`; when the gateway's IGHC adapter can't carry frames, `503` `ighc_unavailable`.\n- Every change is logged with the credential's name (see `GET /v1/activity`).\n\n[Reference](https://developers.igh.app/reference.html#op-control) · [Controlling devices](https://developers.igh.app/resources.html)",
              "preRequestScript": "",
              "testScript": ""
            },
            {
              "v": "17",
              "_ref_id": "req_421465e82f0283182b722981",
              "name": "Gate open [switches a device]",
              "method": "POST",
              "endpoint": "<<baseUrl>>/devices/<<gateId>>/control",
              "params": [],
              "headers": [],
              "auth": {
                "authType": "inherit",
                "authActive": true
              },
              "body": {
                "contentType": "application/json",
                "body": "{\n  \"op\": \"open\"\n}"
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "200,403",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "api",
                  "active": true
                },
                {
                  "key": "igh.needs",
                  "value": "gateId",
                  "active": true
                },
                {
                  "key": "igh.what",
                  "value": "gate",
                  "active": true
                }
              ],
              "responses": {},
              "description": "**Switches a real device in the home.** Send it only when that is fine. The gate in `<<gateId>>`.\n\n**`POST /v1/devices/{id}/control`** — One operation: set state, unit mode (`set_mode`), run a scene.\n\n`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`):\n\n| Operation | Args |\n| --- | --- |\n| `set_power` | `{ \"on\": true }` |\n| `set_brightness` | `{ \"bri\": 0–100 }` |\n| `set_position` | `{ \"pos\": 0–100 }` (shutters, curtains) |\n| `open`, `close`, `stop` | none (`open` / `close` go all the way, even while the cover moves) |\n| `up_toggle`, `down_toggle` | none (shutters, curtains: stop if moving that way, else go) |\n| `flip`, `down_flip` | none (a shutter whose flip time is set, else `422`: tilt the slats / close, then tilt) |\n| `set_mode` | `{ \"mode\": \"auto\" \\| \"eco\" \\| \"manual\" }` (the unit's mode; `sleep` is not available) |\n| `set_temp`, `set_ac_mode`, `set_fan` | `{ \"temp\": 24 }`, `{ \"mode\": \"cool\" }`, `{ \"fan\": \"auto\" }` |\n| `key` | `{ \"key\": \"<remote key>\" }` (IR remotes) |\n| `run` | none (a scene; also `POST /v1/scenes/{id}/run`, `POST /v1/scenes/{id}/run`) |\n\n- Values out of range are `422` `validation`.\n- **Locks and gates**: unlocking or opening needs `allow_unlock: on` (else `403` `unlock_not_allowed`) and, for an agent, the user's explicit confirmation every time.\n- A unit that doesn't answer over IGHC is `504` `unit_unreachable`; when the gateway's IGHC adapter can't carry frames, `503` `ighc_unavailable`.\n- Every change is logged with the credential's name (see `GET /v1/activity`).\n\n[Reference](https://developers.igh.app/reference.html#op-control) · [Controlling devices](https://developers.igh.app/resources.html)",
              "preRequestScript": "",
              "testScript": ""
            }
          ],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "headers": [],
          "variables": [],
          "preRequestScript": "",
          "testScript": ""
        },
        {
          "v": 12,
          "_ref_id": "coll_e3e554f15bc3790e414cfa2b",
          "name": "Unit mode",
          "description": "**Switches a real device in the home.** Send it only when that is fine. `set_mode`: auto, eco or manual (sleep is not built yet: 501).",
          "folders": [],
          "requests": [
            {
              "v": "17",
              "_ref_id": "req_1f44429f7782d29ca1164257",
              "name": "Unit mode eco [switches a device]",
              "method": "POST",
              "endpoint": "<<baseUrl>>/devices/<<lightId>>/control",
              "params": [],
              "headers": [],
              "auth": {
                "authType": "inherit",
                "authActive": true
              },
              "body": {
                "contentType": "application/json",
                "body": "{\n  \"op\": \"set_mode\",\n  \"args\": {\n    \"mode\": \"eco\"\n  }\n}"
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "200",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "api",
                  "active": true
                },
                {
                  "key": "igh.needs",
                  "value": "lightId",
                  "active": true
                },
                {
                  "key": "igh.what",
                  "value": "light",
                  "active": true
                }
              ],
              "responses": {},
              "description": "**Switches a real device in the home.** Send it only when that is fine. The light in `<<lightId>>`.\n\n**`POST /v1/devices/{id}/control`** — One operation: set state, unit mode (`set_mode`), run a scene.\n\n`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`):\n\n| Operation | Args |\n| --- | --- |\n| `set_power` | `{ \"on\": true }` |\n| `set_brightness` | `{ \"bri\": 0–100 }` |\n| `set_position` | `{ \"pos\": 0–100 }` (shutters, curtains) |\n| `open`, `close`, `stop` | none (`open` / `close` go all the way, even while the cover moves) |\n| `up_toggle`, `down_toggle` | none (shutters, curtains: stop if moving that way, else go) |\n| `flip`, `down_flip` | none (a shutter whose flip time is set, else `422`: tilt the slats / close, then tilt) |\n| `set_mode` | `{ \"mode\": \"auto\" \\| \"eco\" \\| \"manual\" }` (the unit's mode; `sleep` is not available) |\n| `set_temp`, `set_ac_mode`, `set_fan` | `{ \"temp\": 24 }`, `{ \"mode\": \"cool\" }`, `{ \"fan\": \"auto\" }` |\n| `key` | `{ \"key\": \"<remote key>\" }` (IR remotes) |\n| `run` | none (a scene; also `POST /v1/scenes/{id}/run`, `POST /v1/scenes/{id}/run`) |\n\n- Values out of range are `422` `validation`.\n- **Locks and gates**: unlocking or opening needs `allow_unlock: on` (else `403` `unlock_not_allowed`) and, for an agent, the user's explicit confirmation every time.\n- A unit that doesn't answer over IGHC is `504` `unit_unreachable`; when the gateway's IGHC adapter can't carry frames, `503` `ighc_unavailable`.\n- Every change is logged with the credential's name (see `GET /v1/activity`).\n\n[Reference](https://developers.igh.app/reference.html#op-control) · [Controlling devices](https://developers.igh.app/resources.html)",
              "preRequestScript": "",
              "testScript": ""
            },
            {
              "v": "17",
              "_ref_id": "req_39e9725b3b5b48e7235a8a2e",
              "name": "Unit mode auto [switches a device]",
              "method": "POST",
              "endpoint": "<<baseUrl>>/devices/<<lightId>>/control",
              "params": [],
              "headers": [],
              "auth": {
                "authType": "inherit",
                "authActive": true
              },
              "body": {
                "contentType": "application/json",
                "body": "{\n  \"op\": \"set_mode\",\n  \"args\": {\n    \"mode\": \"auto\"\n  }\n}"
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "200",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "api",
                  "active": true
                },
                {
                  "key": "igh.needs",
                  "value": "lightId",
                  "active": true
                },
                {
                  "key": "igh.what",
                  "value": "light",
                  "active": true
                }
              ],
              "responses": {},
              "description": "**Switches a real device in the home.** Send it only when that is fine. The light in `<<lightId>>`.\n\n**`POST /v1/devices/{id}/control`** — One operation: set state, unit mode (`set_mode`), run a scene.\n\n`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`):\n\n| Operation | Args |\n| --- | --- |\n| `set_power` | `{ \"on\": true }` |\n| `set_brightness` | `{ \"bri\": 0–100 }` |\n| `set_position` | `{ \"pos\": 0–100 }` (shutters, curtains) |\n| `open`, `close`, `stop` | none (`open` / `close` go all the way, even while the cover moves) |\n| `up_toggle`, `down_toggle` | none (shutters, curtains: stop if moving that way, else go) |\n| `flip`, `down_flip` | none (a shutter whose flip time is set, else `422`: tilt the slats / close, then tilt) |\n| `set_mode` | `{ \"mode\": \"auto\" \\| \"eco\" \\| \"manual\" }` (the unit's mode; `sleep` is not available) |\n| `set_temp`, `set_ac_mode`, `set_fan` | `{ \"temp\": 24 }`, `{ \"mode\": \"cool\" }`, `{ \"fan\": \"auto\" }` |\n| `key` | `{ \"key\": \"<remote key>\" }` (IR remotes) |\n| `run` | none (a scene; also `POST /v1/scenes/{id}/run`, `POST /v1/scenes/{id}/run`) |\n\n- Values out of range are `422` `validation`.\n- **Locks and gates**: unlocking or opening needs `allow_unlock: on` (else `403` `unlock_not_allowed`) and, for an agent, the user's explicit confirmation every time.\n- A unit that doesn't answer over IGHC is `504` `unit_unreachable`; when the gateway's IGHC adapter can't carry frames, `503` `ighc_unavailable`.\n- Every change is logged with the credential's name (see `GET /v1/activity`).\n\n[Reference](https://developers.igh.app/reference.html#op-control) · [Controlling devices](https://developers.igh.app/resources.html)",
              "preRequestScript": "",
              "testScript": ""
            }
          ],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "headers": [],
          "variables": [],
          "preRequestScript": "",
          "testScript": ""
        },
        {
          "v": 12,
          "_ref_id": "coll_2dbe443b1357a3fa8be081b1",
          "name": "Explain",
          "description": "Why a device did what it did (the last 24 h unless `from` and `to` say otherwise).",
          "folders": [],
          "requests": [
            {
              "v": "17",
              "_ref_id": "req_d714027108f7d6fc33a6715d",
              "name": "Explain (body)",
              "method": "POST",
              "endpoint": "<<baseUrl>>/explain",
              "params": [],
              "headers": [],
              "auth": {
                "authType": "inherit",
                "authActive": true
              },
              "body": {
                "contentType": "application/json",
                "body": "{\n  \"device\": \"<<deviceId>>\"\n}"
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "200",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "api",
                  "active": true
                },
                {
                  "key": "igh.needs",
                  "value": "deviceId",
                  "active": true
                },
                {
                  "key": "igh.what",
                  "value": "device",
                  "active": true
                }
              ],
              "responses": {},
              "description": "**`POST /v1/explain`** — Why a device did what it did.\n\n`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):\n\n- activity log entries for the device, each with its cause (a person, an automation, a schedule, an integration);\n- automations and gateway schedules that target it;\n- the unit's own (cached) schedules, its auto or eco mode and touch settings;\n- sensors in the same room that triggered;\n- integrations (the API, Google, Alexa, Matter, HomeKit, Home Assistant) that sent it commands.\n\nEvery 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\".\n\n[Reference](https://developers.igh.app/reference.html#op-explain) · [Explain](https://developers.igh.app/explain.html)",
              "preRequestScript": "",
              "testScript": ""
            },
            {
              "v": "17",
              "_ref_id": "req_864197c4a123fa363ef12940",
              "name": "Explain (device path)",
              "method": "GET",
              "endpoint": "<<baseUrl>>/devices/<<deviceId>>/explain",
              "params": [
                {
                  "key": "from",
                  "value": "",
                  "active": false,
                  "description": "epoch ms"
                },
                {
                  "key": "to",
                  "value": "",
                  "active": false,
                  "description": "epoch ms"
                }
              ],
              "headers": [],
              "auth": {
                "authType": "inherit",
                "authActive": true
              },
              "body": {
                "contentType": null,
                "body": null
              },
              "requestVariables": [
                {
                  "key": "igh.expect",
                  "value": "200",
                  "active": true
                },
                {
                  "key": "igh.kind",
                  "value": "api",
                  "active": true
                },
                {
                  "key": "igh.needs",
                  "value": "deviceId",
                  "active": true
                },
                {
                  "key": "igh.what",
                  "value": "device",
                  "active": true
                }
              ],
              "responses": {},
              "description": "**`GET /v1/devices/{id}/explain`** — Why the device did what it did (`?from&to`).\n\n[Reference](https://developers.igh.app/reference.html#op-explainDevice) · [Explain](https://developers.igh.app/explain.html)",
              "preRequestScript": "",
              "testScript": ""
            }
          ],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "headers": [],
          "variables": [],
          "preRequestScript": "",
          "testScript": ""
        }
      ],
      "requests": [
        {
          "v": "17",
          "_ref_id": "req_c031c19d0880b19f3c8ae8c7",
          "name": "Device",
          "method": "GET",
          "endpoint": "<<baseUrl>>/devices/<<deviceId>>",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": null,
            "body": null
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            },
            {
              "key": "igh.needs",
              "value": "deviceId",
              "active": true
            },
            {
              "key": "igh.what",
              "value": "device",
              "active": true
            }
          ],
          "responses": {},
          "description": "**`GET /v1/devices/{id}`** — One unit, module, gang or scene.\n\n[Reference](https://developers.igh.app/reference.html#op-device) · [The house](https://developers.igh.app/resources.html)",
          "preRequestScript": "",
          "testScript": ""
        }
      ],
      "auth": {
        "authType": "inherit",
        "authActive": true
      },
      "headers": [],
      "variables": [],
      "preRequestScript": "",
      "testScript": ""
    },
    {
      "v": 12,
      "_ref_id": "coll_5c443874bb4bc7600c0cd939",
      "name": "Scenes",
      "description": "List, then create a test scene (\"Hoppscotch test scene\": one 0.5 s delay, so running it switches nothing), read it, edit it with `ifRevision` and run it. The API never deletes: remove the test scene in the app. \"Run a scene of the house\" runs a real one.",
      "folders": [],
      "requests": [
        {
          "v": "17",
          "_ref_id": "req_302c16190271fc66bad29876",
          "name": "Scenes",
          "method": "GET",
          "endpoint": "<<baseUrl>>/scenes",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": null,
            "body": null
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            }
          ],
          "responses": {},
          "description": "**`GET /v1/scenes`** — The exposed scenes, their members and whether they run.\n\n- `GET /v1/scenes`: the exposed scenes, their members (as device ids, plus how many members this integration doesn't show) and whether they run.\n- `POST /v1/scenes/{id}/run` runs one.\n- `POST /v1/scenes` creates one in the app's scene schema (`SceneBody`): its `steps` (`Step`: `operate` a unit with one of its operations, `delay`, `run_scene`, `toggle_together`, `notify`) run in order and name only exposed devices, by unit id (the last part of a device id). On a *Selected* integration the new scene joins the selection. It is marked as created by this integration.\n- `PATCH /v1/scenes/{id}` changes one; send the `revision` you read as `ifRevision`, or a change made meanwhile is refused with `409` `revision_mismatch`: read it again and redo the edit.\n\nDeleting a scene is done in the app.\n\n[Reference](https://developers.igh.app/reference.html#op-scenes) · [Scenes](https://developers.igh.app/resources.html)",
          "preRequestScript": "",
          "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar body = answer();\nif (body) {\n  var first = (body.scenes || [])[0];\n  if (first) { fill(\"sceneId\", first.id); }\n}"
        },
        {
          "v": "17",
          "_ref_id": "req_7ceccff56543ae9e25aefaef",
          "name": "Create a test scene",
          "method": "POST",
          "endpoint": "<<baseUrl>>/scenes",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": "application/json",
            "body": "{\n  \"name\": \"Hoppscotch test scene\",\n  \"steps\": [\n    {\n      \"kind\": \"delay\",\n      \"ms\": 500\n    }\n  ]\n}"
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "201",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            }
          ],
          "responses": {},
          "description": "Creates a real scene in the home (it does nothing when it runs). Fills `createdSceneId` and `sceneRevision`.\n\n**`POST /v1/scenes`** — Create a scene (it joins a Selected integration's selection).\n\n[Reference](https://developers.igh.app/reference.html#op-createScene) · [Scenes](https://developers.igh.app/resources.html)",
          "preRequestScript": "",
          "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar body = answer();\nif (body) {\n  save(\"createdSceneId\", body.id);\n  save(\"sceneRevision\", body.scene && body.scene.revision !== undefined ? body.scene.revision : 0);\n  console.log(\"Created \" + body.id + \" at revision \" + hopp.env.get(\"sceneRevision\"));\n}"
        },
        {
          "v": "17",
          "_ref_id": "req_010a641e62cf49d559342bff",
          "name": "The test scene",
          "method": "GET",
          "endpoint": "<<baseUrl>>/scenes/<<createdSceneId>>",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": null,
            "body": null
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            },
            {
              "key": "igh.needs",
              "value": "createdSceneId",
              "active": true
            },
            {
              "key": "igh.what",
              "value": "test scene",
              "active": true
            }
          ],
          "responses": {},
          "description": "**`GET /v1/scenes/{id}`** — One scene.\n\n[Reference](https://developers.igh.app/reference.html#op-scene) · [Scenes](https://developers.igh.app/resources.html)",
          "preRequestScript": "",
          "testScript": ""
        },
        {
          "v": "17",
          "_ref_id": "req_5591619045143e9d0da68412",
          "name": "Edit the test scene (ifRevision)",
          "method": "PATCH",
          "endpoint": "<<baseUrl>>/scenes/<<createdSceneId>>",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": "application/json",
            "body": "{\n  \"ifRevision\": <<sceneRevision>>,\n  \"name\": \"Hoppscotch test scene (edited)\"\n}"
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            },
            {
              "key": "igh.needs",
              "value": "createdSceneId",
              "active": true
            },
            {
              "key": "igh.what",
              "value": "test scene",
              "active": true
            }
          ],
          "responses": {},
          "description": "Sends the revision read; a scene changed since is `409 revision_mismatch`. Keeps the new revision.\n\n**`PATCH /v1/scenes/{id}`** — Edit a scene (`ifRevision`).\n\n[Reference](https://developers.igh.app/reference.html#op-editScene) · [Scenes](https://developers.igh.app/resources.html)",
          "preRequestScript": "",
          "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar body = answer();\nif (body && body.scene) { save(\"sceneRevision\", body.scene.revision); }"
        },
        {
          "v": "17",
          "_ref_id": "req_06d598467654ff7218758af3",
          "name": "Run the test scene",
          "method": "POST",
          "endpoint": "<<baseUrl>>/scenes/<<createdSceneId>>/run",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": null,
            "body": null
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            },
            {
              "key": "igh.needs",
              "value": "createdSceneId",
              "active": true
            },
            {
              "key": "igh.what",
              "value": "test scene",
              "active": true
            }
          ],
          "responses": {},
          "description": "Runs the test scene: a 0.5 s delay, nothing switches.\n\n**`POST /v1/scenes/{id}/run`** — Run a scene.\n\n[Reference](https://developers.igh.app/reference.html#op-runScene) · [Controlling devices](https://developers.igh.app/resources.html)",
          "preRequestScript": "",
          "testScript": ""
        },
        {
          "v": "17",
          "_ref_id": "req_b76bdd39aebfe089ad3cd09d",
          "name": "Run a scene of the house [switches devices]",
          "method": "POST",
          "endpoint": "<<baseUrl>>/scenes/<<sceneId>>/run",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": null,
            "body": null
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            },
            {
              "key": "igh.needs",
              "value": "sceneId",
              "active": true
            },
            {
              "key": "igh.what",
              "value": "scene",
              "active": true
            }
          ],
          "responses": {},
          "description": "**Switches a real device in the home.** Send it only when that is fine. Runs `<<sceneId>>`, the house's first scene unless you set another.\n\n**`POST /v1/scenes/{id}/run`** — Run a scene.\n\n[Reference](https://developers.igh.app/reference.html#op-runScene) · [Controlling devices](https://developers.igh.app/resources.html)",
          "preRequestScript": "",
          "testScript": ""
        }
      ],
      "auth": {
        "authType": "inherit",
        "authActive": true
      },
      "headers": [],
      "variables": [],
      "preRequestScript": "",
      "testScript": ""
    },
    {
      "v": 12,
      "_ref_id": "coll_119d4bf138537f5781ee99fc",
      "name": "Automations",
      "description": "Needs the integration's automations on (otherwise 404 `not_found`). The test automation is created disabled, with a 0.5 s delay as its only action; delete it in the app.",
      "folders": [],
      "requests": [
        {
          "v": "17",
          "_ref_id": "req_c00dc063d7c49d4e06298738",
          "name": "Automations",
          "method": "GET",
          "endpoint": "<<baseUrl>>/automations",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": null,
            "body": null
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200,404",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            }
          ],
          "responses": {},
          "description": "404 `not_found` while the integration's automations are off.\n\n**`GET /v1/automations`** — Automations whose references are all exposed (`automations: on`).\n\n[Reference](https://developers.igh.app/reference.html#op-automations) · [Automations and schedules](https://developers.igh.app/resources.html)",
          "preRequestScript": "",
          "testScript": ""
        },
        {
          "v": "17",
          "_ref_id": "req_aa110b95089ab288fd9e82c4",
          "name": "Create a test automation (disabled)",
          "method": "POST",
          "endpoint": "<<baseUrl>>/automations",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": "application/json",
            "body": "{\n  \"name\": \"Hoppscotch test automation\",\n  \"enabled\": false,\n  \"triggers\": [\n    {\n      \"subject\": \"unit:<<unitRef>>\",\n      \"signal\": \"on\",\n      \"eq\": true\n    }\n  ],\n  \"actions\": [\n    {\n      \"kind\": \"delay\",\n      \"ms\": 500\n    }\n  ]\n}"
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "201",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            },
            {
              "key": "igh.needs",
              "value": "unitRef",
              "active": true
            },
            {
              "key": "igh.what",
              "value": "unit (Getting started → House fills unitRef)",
              "active": true
            }
          ],
          "responses": {},
          "description": "Creates a real automation, disabled: when `<<unitRef>>` turns on it would wait 0.5 s. Fills `automationId` and `automationRevision`.\n\n**`POST /v1/automations`** — Create an automation (the app's schema).\n\n[Reference](https://developers.igh.app/reference.html#op-createAutomation) · [Automations and schedules](https://developers.igh.app/resources.html)",
          "preRequestScript": "",
          "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar body = answer();\nif (body) {\n  var a = body.automation || {};\n  save(\"automationId\", a.id);\n  save(\"automationRevision\", a.revision !== undefined ? a.revision : 0);\n}"
        },
        {
          "v": "17",
          "_ref_id": "req_a932e1b2d36e04d5880afa89",
          "name": "The test automation",
          "method": "GET",
          "endpoint": "<<baseUrl>>/automations/<<automationId>>",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": null,
            "body": null
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            },
            {
              "key": "igh.needs",
              "value": "automationId",
              "active": true
            },
            {
              "key": "igh.what",
              "value": "automation",
              "active": true
            }
          ],
          "responses": {},
          "description": "**`GET /v1/automations/{id}`** — One automation.\n\n[Reference](https://developers.igh.app/reference.html#op-automation)",
          "preRequestScript": "",
          "testScript": ""
        },
        {
          "v": "17",
          "_ref_id": "req_1c6cfd91998f6a372cc93ad4",
          "name": "Edit the test automation (ifRevision)",
          "method": "PATCH",
          "endpoint": "<<baseUrl>>/automations/<<automationId>>",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": "application/json",
            "body": "{\n  \"ifRevision\": <<automationRevision>>,\n  \"name\": \"Hoppscotch test automation (edited)\",\n  \"enabled\": false\n}"
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            },
            {
              "key": "igh.needs",
              "value": "automationId",
              "active": true
            },
            {
              "key": "igh.what",
              "value": "automation",
              "active": true
            }
          ],
          "responses": {},
          "description": "PUT does the same. A revision other than the stored one is `409 revision_mismatch`.\n\n**`PATCH /v1/automations/{id}`** — Edit an automation (`ifRevision`; PUT does the same).\n\n[Reference](https://developers.igh.app/reference.html#op-editAutomation) · [Automations and schedules](https://developers.igh.app/resources.html)",
          "preRequestScript": "",
          "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar body = answer();\nif (body && body.automation) { save(\"automationRevision\", body.automation.revision); }"
        }
      ],
      "auth": {
        "authType": "inherit",
        "authActive": true
      },
      "headers": [],
      "variables": [],
      "preRequestScript": "",
      "testScript": ""
    },
    {
      "v": 12,
      "_ref_id": "coll_a9775f55bd635b4a2657f2da",
      "name": "Schedules",
      "description": "The gateway's schedules (the integration's schedules on, otherwise 404) and a unit's own schedules (read-only). The test schedule is created paused; delete it in the app.",
      "folders": [],
      "requests": [
        {
          "v": "17",
          "_ref_id": "req_60600a3492b293d7010d71a9",
          "name": "Schedules",
          "method": "GET",
          "endpoint": "<<baseUrl>>/schedules",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": null,
            "body": null
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200,404",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            }
          ],
          "responses": {},
          "description": "404 `not_found` while the integration's schedules are off.\n\n**`GET /v1/schedules`** — The gateway's schedules whose references are all exposed (`schedules: on`).\n\n[Reference](https://developers.igh.app/reference.html#op-schedules) · [Automations and schedules](https://developers.igh.app/resources.html)",
          "preRequestScript": "",
          "testScript": ""
        },
        {
          "v": "17",
          "_ref_id": "req_2a99eff44abeaa06fbd69096",
          "name": "Create a test schedule (paused)",
          "method": "POST",
          "endpoint": "<<baseUrl>>/schedules",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": "application/json",
            "body": "{\n  \"name\": \"Hoppscotch test schedule\",\n  \"paused\": true,\n  \"triggers\": [\n    {\n      \"at\": {\n        \"time\": \"07:00\"\n      },\n      \"days\": {\n        \"mask\": 62\n      }\n    }\n  ],\n  \"actions\": [\n    {\n      \"kind\": \"delay\",\n      \"ms\": 500\n    }\n  ]\n}"
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "201",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            }
          ],
          "responses": {},
          "description": "Creates a real schedule, paused (weekdays Monday–Friday at 07:00: `mask` bit 0 is Sunday). Fills `scheduleId` and `scheduleRevision`.\n\n**`POST /v1/schedules`** — Create a gateway schedule.\n\n[Reference](https://developers.igh.app/reference.html#op-createSchedule) · [Automations and schedules](https://developers.igh.app/resources.html)",
          "preRequestScript": "",
          "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar body = answer();\nif (body) {\n  var s = body.schedule || {};\n  save(\"scheduleId\", s.id);\n  save(\"scheduleRevision\", s.revision !== undefined ? s.revision : 0);\n}"
        },
        {
          "v": "17",
          "_ref_id": "req_7e8c7ff70a826c58ba0088c5",
          "name": "The test schedule",
          "method": "GET",
          "endpoint": "<<baseUrl>>/schedules/<<scheduleId>>",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": null,
            "body": null
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            },
            {
              "key": "igh.needs",
              "value": "scheduleId",
              "active": true
            },
            {
              "key": "igh.what",
              "value": "schedule",
              "active": true
            }
          ],
          "responses": {},
          "description": "**`GET /v1/schedules/{id}`** — One gateway schedule.\n\n[Reference](https://developers.igh.app/reference.html#op-schedule)",
          "preRequestScript": "",
          "testScript": ""
        },
        {
          "v": "17",
          "_ref_id": "req_81a18acf860a98b27aef7b2c",
          "name": "Edit the test schedule (ifRevision)",
          "method": "PATCH",
          "endpoint": "<<baseUrl>>/schedules/<<scheduleId>>",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": "application/json",
            "body": "{\n  \"ifRevision\": <<scheduleRevision>>,\n  \"name\": \"Hoppscotch test schedule (edited)\",\n  \"paused\": true\n}"
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            },
            {
              "key": "igh.needs",
              "value": "scheduleId",
              "active": true
            },
            {
              "key": "igh.what",
              "value": "schedule",
              "active": true
            }
          ],
          "responses": {},
          "description": "**`PATCH /v1/schedules/{id}`** — Edit a gateway schedule (`ifRevision`; PUT does the same).\n\n[Reference](https://developers.igh.app/reference.html#op-editSchedule) · [Automations and schedules](https://developers.igh.app/resources.html)",
          "preRequestScript": "",
          "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar body = answer();\nif (body && body.schedule) { save(\"scheduleRevision\", body.schedule.revision); }"
        },
        {
          "v": "17",
          "_ref_id": "req_d3d9fd218233865f71d58734",
          "name": "A unit's own schedules",
          "method": "GET",
          "endpoint": "<<baseUrl>>/devices/<<deviceId>>/schedules",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": null,
            "body": null
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200,404",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            },
            {
              "key": "igh.needs",
              "value": "deviceId",
              "active": true
            },
            {
              "key": "igh.what",
              "value": "device",
              "active": true
            }
          ],
          "responses": {},
          "description": "404 for a device that keeps no schedules of its own.\n\n**`GET /v1/devices/{id}/schedules`** — The unit's own schedules as last read (read-only).\n\n[Reference](https://developers.igh.app/reference.html#op-unitSchedules) · [Automations and schedules](https://developers.igh.app/resources.html)",
          "preRequestScript": "",
          "testScript": ""
        }
      ],
      "auth": {
        "authType": "inherit",
        "authActive": true
      },
      "headers": [],
      "variables": [],
      "preRequestScript": "",
      "testScript": ""
    },
    {
      "v": 12,
      "_ref_id": "coll_3869e67062db02fa73182340",
      "name": "Intents",
      "description": "Unit settings: the table of every intent, a device's intents with their cached values, and a change (`advanced_config: read_write`).",
      "folders": [],
      "requests": [
        {
          "v": "17",
          "_ref_id": "req_187f2683bfcd2dbaf9874227",
          "name": "Intent table",
          "method": "GET",
          "endpoint": "<<baseUrl>>/intents",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": null,
            "body": null
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            }
          ],
          "responses": {},
          "description": "**`GET /v1/intents`** — Every intent's definition.\n\n[Reference](https://developers.igh.app/reference.html#op-intentTable) · [Intents](https://developers.igh.app/intents.html)",
          "preRequestScript": "",
          "testScript": ""
        },
        {
          "v": "17",
          "_ref_id": "req_2e4317d66a37a93b4d38be06",
          "name": "Device intents",
          "method": "GET",
          "endpoint": "<<baseUrl>>/devices/<<deviceId>>/intents",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": null,
            "body": null
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200,403",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            },
            {
              "key": "igh.needs",
              "value": "deviceId",
              "active": true
            },
            {
              "key": "igh.what",
              "value": "device",
              "active": true
            }
          ],
          "responses": {},
          "description": "403 `config_not_allowed` while the integration's unit settings are off. Fills `intentId` and `intentValue` with the first setting it may change.\n\n**`GET /v1/devices/{id}/intents`** — The device's configuration intents with their cached values.\n\nA 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:\n\n- **read_write** — changed with `PUT /v1/devices/{id}/intents/{intent} { value }` (`PUT /v1/devices/{id}/intents/{intent}`) when the integration has `advanced_config: read_write`. The gateway sends the frames, and the new value is reported only after the unit confirmed it.\n- **read_only** — shown from the cache with `guidance` and an `appAction` naming the app screen that changes it.\n\n`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.\n\n- `advanced_config: off`: every intent call is `403` `config_not_allowed`; `read`: every change is.\n- A read-only intent, and resetting a unit, are `403` `not_offered`.\n- A setting packed with others in one register needs a cached value to change: without one it is `409` `not_cached` with `appAction: unit.menus` (`unit.menus`): the user reads the unit's menu in the app once.\n- Unit mode (Auto, Eco, Manual) is a control operation, `set_mode` (see `POST /v1/devices/{id}/control`), not an intent.\n\nThe intents, one by one, follow.\n\n[Reference](https://developers.igh.app/reference.html#op-intents) · [Cached unit data](https://developers.igh.app/cached-data.html)",
          "preRequestScript": "",
          "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar body = answer();\nif (body) {\n  var writable = (body.intents || []).filter(function (i) { return i.access === \"read_write\" && i.value !== null && i.value !== undefined; })[0];\n  if (writable) { fill(\"intentId\", writable.intent); save(\"intentValue\", JSON.stringify(writable.value)); }\n}"
        },
        {
          "v": "17",
          "_ref_id": "req_5fc35738f43dc17bfefd0301",
          "name": "Set an intent [changes a unit setting]",
          "method": "PUT",
          "endpoint": "<<baseUrl>>/devices/<<deviceId>>/intents/<<intentId>>",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": "application/json",
            "body": "{\n  \"value\": <<intentValue>>\n}"
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200,403",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            },
            {
              "key": "igh.needs",
              "value": "deviceId",
              "active": true
            },
            {
              "key": "igh.what",
              "value": "device",
              "active": true
            }
          ],
          "responses": {},
          "description": "Writes `<<intentValue>>` (as Device intents read it, so nothing changes until you edit it) to `<<intentId>>` of `<<deviceId>>`. **Changes a real unit setting** once you change the value. 403 `config_not_allowed` unless the integration may change unit settings.\n\n**`PUT /v1/devices/{id}/intents/{intent}`** — Change a writable intent (`advanced_config: read_write`).\n\n[Reference](https://developers.igh.app/reference.html#op-setIntent) · [Intents](https://developers.igh.app/intents.html)",
          "preRequestScript": "if (!hopp.env.get(\"intentId\") || !hopp.env.get(\"intentValue\")) {\n  console.log(\"No intentId or intentValue: send Device intents first. Using the doc example off_after_no_motion = 10.\");\n  hopp.env.active.set(\"intentId\", \"off_after_no_motion\");\n  hopp.env.active.set(\"intentValue\", \"10\");\n}",
          "testScript": ""
        }
      ],
      "auth": {
        "authType": "inherit",
        "authActive": true
      },
      "headers": [],
      "variables": [],
      "preRequestScript": "",
      "testScript": ""
    },
    {
      "v": 12,
      "_ref_id": "coll_0ef41bf5756e7302bfd45cf1",
      "name": "Sensors and energy",
      "description": "Readings, a sensor's detections, and energy and cost by scope.",
      "folders": [],
      "requests": [
        {
          "v": "17",
          "_ref_id": "req_02be34978e54cf22cb887184",
          "name": "Sensors",
          "method": "GET",
          "endpoint": "<<baseUrl>>/sensors",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": null,
            "body": null
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            }
          ],
          "responses": {},
          "description": "**`GET /v1/sensors`** — Every exposed sensor's readings.\n\n[Reference](https://developers.igh.app/reference.html#op-sensors) · [Sensors, energy and activity](https://developers.igh.app/resources.html)",
          "preRequestScript": "",
          "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar body = answer();\nif (body) {\n  var first = (body.sensors || [])[0];\n  if (first) { fill(\"sensorId\", first.id || first.device); }\n}"
        },
        {
          "v": "17",
          "_ref_id": "req_d208fed2a5085715849859a2",
          "name": "Detections",
          "method": "GET",
          "endpoint": "<<baseUrl>>/sensors/<<sensorId>>/detections",
          "params": [
            {
              "key": "limit",
              "value": "20",
              "active": true,
              "description": "how many"
            },
            {
              "key": "cursor",
              "value": "",
              "active": false,
              "description": "nextCursor of the previous page"
            }
          ],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": null,
            "body": null
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            },
            {
              "key": "igh.needs",
              "value": "sensorId",
              "active": true
            },
            {
              "key": "igh.what",
              "value": "sensor",
              "active": true
            }
          ],
          "responses": {},
          "description": "**`GET /v1/sensors/{id}/detections`** — A sensor's detections (`?cursor&limit`).\n\n[Reference](https://developers.igh.app/reference.html#op-detections) · [Sensors, energy and activity](https://developers.igh.app/resources.html)",
          "preRequestScript": "",
          "testScript": ""
        },
        {
          "v": "17",
          "_ref_id": "req_66a5d6979624345e2817f930",
          "name": "Energy (house, today)",
          "method": "GET",
          "endpoint": "<<baseUrl>>/energy",
          "params": [
            {
              "key": "scope",
              "value": "house",
              "active": true,
              "description": "house, room:<id>, floor:<id> or device:<device id>"
            },
            {
              "key": "period",
              "value": "day",
              "active": true,
              "description": "day, week, month or year (or from and to)"
            }
          ],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": null,
            "body": null
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            }
          ],
          "responses": {},
          "description": "**`GET /v1/energy`** — Energy and cost (`?scope=house|room:<id>|floor:<id>|device:<id>&period|from&to`).\n\n[Reference](https://developers.igh.app/reference.html#op-energy) · [Sensors, energy and activity](https://developers.igh.app/resources.html)",
          "preRequestScript": "",
          "testScript": ""
        },
        {
          "v": "17",
          "_ref_id": "req_22ad6674003f499241c2077c",
          "name": "Energy (a room, this month)",
          "method": "GET",
          "endpoint": "<<baseUrl>>/energy",
          "params": [
            {
              "key": "scope",
              "value": "room:<<roomId>>",
              "active": true,
              "description": "house, room:<id>, floor:<id> or device:<device id>"
            },
            {
              "key": "period",
              "value": "month",
              "active": true,
              "description": "day, week, month or year"
            }
          ],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": null,
            "body": null
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            },
            {
              "key": "igh.needs",
              "value": "roomId",
              "active": true
            },
            {
              "key": "igh.what",
              "value": "room",
              "active": true
            }
          ],
          "responses": {},
          "description": "**`GET /v1/energy`** — Energy and cost (`?scope=house|room:<id>|floor:<id>|device:<id>&period|from&to`).\n\n[Reference](https://developers.igh.app/reference.html#op-energy) · [Sensors, energy and activity](https://developers.igh.app/resources.html)",
          "preRequestScript": "",
          "testScript": ""
        }
      ],
      "auth": {
        "authType": "inherit",
        "authActive": true
      },
      "headers": [],
      "variables": [],
      "preRequestScript": "",
      "testScript": ""
    },
    {
      "v": 12,
      "_ref_id": "coll_b56436ba7656a991f73b19df",
      "name": "Activity",
      "description": "The activity log of exposed items.",
      "folders": [],
      "requests": [
        {
          "v": "17",
          "_ref_id": "req_1a2fd9f0ae036ae0c95992c6",
          "name": "Activity",
          "method": "GET",
          "endpoint": "<<baseUrl>>/activity",
          "params": [
            {
              "key": "device",
              "value": "<<deviceId>>",
              "active": true,
              "description": "one exposed device (leave out for every one)"
            },
            {
              "key": "limit",
              "value": "20",
              "active": true,
              "description": "1–200 (default 50)"
            },
            {
              "key": "from",
              "value": "",
              "active": false,
              "description": "epoch ms (default: 30 days before to)"
            },
            {
              "key": "to",
              "value": "",
              "active": false,
              "description": "epoch ms (default: now)"
            }
          ],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": null,
            "body": null
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            }
          ],
          "responses": {},
          "description": "**`GET /v1/activity`** — The activity log of exposed items (`?device&from&to&limit`).\n\n[Reference](https://developers.igh.app/reference.html#op-activity) · [Sensors, energy and activity](https://developers.igh.app/resources.html)",
          "preRequestScript": "",
          "testScript": ""
        }
      ],
      "auth": {
        "authType": "inherit",
        "authActive": true
      },
      "headers": [],
      "variables": [],
      "preRequestScript": "",
      "testScript": ""
    },
    {
      "v": 12,
      "_ref_id": "coll_47195e3634236c4cb389e7e4",
      "name": "Structure",
      "description": "Renames, with the integration's renaming (`structural`) on; otherwise 403 `structural_not_allowed`. The bodies send the current names (from House), so nothing changes until you edit them.",
      "folders": [],
      "requests": [
        {
          "v": "17",
          "_ref_id": "req_3fb77b4c18cdf1a61b322352",
          "name": "Rename a room",
          "method": "PATCH",
          "endpoint": "<<baseUrl>>/rooms/<<roomId>>",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": "application/json",
            "body": "{\n  \"name\": \"<<roomName>>\"\n}"
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200,403",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            },
            {
              "key": "igh.needs",
              "value": "roomId",
              "active": true
            },
            {
              "key": "igh.what",
              "value": "room",
              "active": true
            }
          ],
          "responses": {},
          "description": "**`PATCH /v1/rooms/{id}`** — Rename a room inside the exposure (`structural: on`).\n\n[Reference](https://developers.igh.app/reference.html#op-renameRoom) · [Renaming rooms and units](https://developers.igh.app/resources.html)",
          "preRequestScript": "",
          "testScript": ""
        },
        {
          "v": "17",
          "_ref_id": "req_14511373da7e6b4de73ba0aa",
          "name": "Rename a unit or gang",
          "method": "PATCH",
          "endpoint": "<<baseUrl>>/devices/<<deviceId>>",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": "application/json",
            "body": "{\n  \"name\": \"<<deviceName>>\"\n}"
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200,403",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            },
            {
              "key": "igh.needs",
              "value": "deviceId",
              "active": true
            },
            {
              "key": "igh.what",
              "value": "device",
              "active": true
            }
          ],
          "responses": {},
          "description": "**`PATCH /v1/devices/{id}`** — Rename a unit or gang (`structural: on`).\n\n[Reference](https://developers.igh.app/reference.html#op-renameDevice) · [Renaming rooms and units](https://developers.igh.app/resources.html)",
          "preRequestScript": "",
          "testScript": ""
        }
      ],
      "auth": {
        "authType": "inherit",
        "authActive": true
      },
      "headers": [],
      "variables": [],
      "preRequestScript": "",
      "testScript": ""
    },
    {
      "v": 12,
      "_ref_id": "coll_4e27b5ae87dd0675e8d0d362",
      "name": "TLS",
      "description": "The gateway's local CA, to pin. Fetch it through a cloud environment first; on the home network Hoppscotch must trust it already.",
      "folders": [],
      "requests": [
        {
          "v": "17",
          "_ref_id": "req_08618b4cd825265a17050cf6",
          "name": "Gateway CA (to pin)",
          "method": "GET",
          "endpoint": "<<baseUrl>>/gateway/tls",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": null,
            "body": null
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            }
          ],
          "responses": {},
          "description": "Works on both transports, but the first time fetch it through the cloud environment: on the home network Hoppscotch must already trust the CA. The Console prints `ca`: save it as `ca.pem`, compare `fingerprint` (`openssl x509 -in ca.pem -noout -fingerprint -sha256`), then add the file to the gateway's domain in Settings → Interceptor → Native (README step 1).\n\n**`GET /v1/gateway/tls`** — The gateway's local CA (PEM) and its SHA-256 fingerprint, to pin.\n\nOn 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.\n\n**Never turn off certificate verification** (`curl -k`, `verify=False`, `rejectUnauthorized: false`, `InsecureSkipVerify`). There is no plain-HTTP fallback.\n\n**Getting the CA.** Either:\n- from the integration's page in the app: the CA's fingerprint and a download of its file; or\n- `GET /v1/gateway/tls` through `https://api.igh.app` (publicly trusted TLS): `{ ca, fingerprint, algorithm: \"SHA-256\", createdAt, house }`. `ca` is the PEM certificate; `fingerprint` is the SHA-256 of its DER bytes, upper-case hex pairs joined by colons.\n\nCheck 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.\n\n**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.\n\n[Reference](https://developers.igh.app/reference.html#op-tls) · [Getting started](https://developers.igh.app/getting-started.html)",
          "preRequestScript": "",
          "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar body = answer();\nif (body && body.ca) {\n  save(\"caFingerprint\", body.fingerprint);\n  console.log(\"CA fingerprint (\" + body.algorithm + \"): \" + body.fingerprint);\n  console.log(\"Save the next entry, from -----BEGIN to END CERTIFICATE-----, as ca.pem; then README step 1.\");\n  console.log(body.ca);\n}"
        }
      ],
      "auth": {
        "authType": "inherit",
        "authActive": true
      },
      "headers": [],
      "variables": [],
      "preRequestScript": "",
      "testScript": ""
    },
    {
      "v": 12,
      "_ref_id": "coll_f53bc3f1a097be6ce4cde85c",
      "name": "Errors and edge cases",
      "description": "Each request provokes one refusal and checks its status and code. Some need a setting of the integration (named in the request).",
      "folders": [],
      "requests": [
        {
          "v": "17",
          "_ref_id": "req_f640c26f5632edd9f416ecb1",
          "name": "Unknown id → 404 not_found",
          "method": "GET",
          "endpoint": "<<baseUrl>>/devices/hoppscotch-unknown-device",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": null,
            "body": null
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "404",
              "active": true
            },
            {
              "key": "igh.code",
              "value": "not_found",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            }
          ],
          "responses": {},
          "description": "**`GET /v1/devices/{id}`** — One unit, module, gang or scene.\n\n[Reference](https://developers.igh.app/reference.html#op-device) · [The house](https://developers.igh.app/resources.html)",
          "preRequestScript": "",
          "testScript": ""
        },
        {
          "v": "17",
          "_ref_id": "req_e12c7b4ed55b2a7837e92aa1",
          "name": "Unknown operation → 422 validation",
          "method": "POST",
          "endpoint": "<<baseUrl>>/devices/<<deviceId>>/control",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": "application/json",
            "body": "{\n  \"op\": \"hoppscotch_unknown_operation\"\n}"
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "422",
              "active": true
            },
            {
              "key": "igh.code",
              "value": "validation",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            },
            {
              "key": "igh.needs",
              "value": "deviceId",
              "active": true
            },
            {
              "key": "igh.what",
              "value": "device",
              "active": true
            }
          ],
          "responses": {},
          "description": "An operation the device doesn't take; nothing switches.\n\n**`POST /v1/devices/{id}/control`** — One operation: set state, unit mode (`set_mode`), run a scene.\n\n`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`):\n\n| Operation | Args |\n| --- | --- |\n| `set_power` | `{ \"on\": true }` |\n| `set_brightness` | `{ \"bri\": 0–100 }` |\n| `set_position` | `{ \"pos\": 0–100 }` (shutters, curtains) |\n| `open`, `close`, `stop` | none (`open` / `close` go all the way, even while the cover moves) |\n| `up_toggle`, `down_toggle` | none (shutters, curtains: stop if moving that way, else go) |\n| `flip`, `down_flip` | none (a shutter whose flip time is set, else `422`: tilt the slats / close, then tilt) |\n| `set_mode` | `{ \"mode\": \"auto\" \\| \"eco\" \\| \"manual\" }` (the unit's mode; `sleep` is not available) |\n| `set_temp`, `set_ac_mode`, `set_fan` | `{ \"temp\": 24 }`, `{ \"mode\": \"cool\" }`, `{ \"fan\": \"auto\" }` |\n| `key` | `{ \"key\": \"<remote key>\" }` (IR remotes) |\n| `run` | none (a scene; also `POST /v1/scenes/{id}/run`, `POST /v1/scenes/{id}/run`) |\n\n- Values out of range are `422` `validation`.\n- **Locks and gates**: unlocking or opening needs `allow_unlock: on` (else `403` `unlock_not_allowed`) and, for an agent, the user's explicit confirmation every time.\n- A unit that doesn't answer over IGHC is `504` `unit_unreachable`; when the gateway's IGHC adapter can't carry frames, `503` `ighc_unavailable`.\n- Every change is logged with the credential's name (see `GET /v1/activity`).\n\n[Reference](https://developers.igh.app/reference.html#op-control) · [Controlling devices](https://developers.igh.app/resources.html)",
          "preRequestScript": "",
          "testScript": ""
        },
        {
          "v": "17",
          "_ref_id": "req_b5caf834f834cbc572801611",
          "name": "Unlock not allowed → 403 unlock_not_allowed",
          "method": "POST",
          "endpoint": "<<baseUrl>>/devices/<<lockId>>/control",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": "application/json",
            "body": "{\n  \"op\": \"unlock\"\n}"
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "403",
              "active": true
            },
            {
              "key": "igh.code",
              "value": "unlock_not_allowed",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            },
            {
              "key": "igh.needs",
              "value": "lockId",
              "active": true
            },
            {
              "key": "igh.what",
              "value": "lock",
              "active": true
            }
          ],
          "responses": {},
          "description": "With the integration's Unlocking off. **With Unlocking on it unlocks the lock.**\n\n**`POST /v1/devices/{id}/control`** — One operation: set state, unit mode (`set_mode`), run a scene.\n\n`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`):\n\n| Operation | Args |\n| --- | --- |\n| `set_power` | `{ \"on\": true }` |\n| `set_brightness` | `{ \"bri\": 0–100 }` |\n| `set_position` | `{ \"pos\": 0–100 }` (shutters, curtains) |\n| `open`, `close`, `stop` | none (`open` / `close` go all the way, even while the cover moves) |\n| `up_toggle`, `down_toggle` | none (shutters, curtains: stop if moving that way, else go) |\n| `flip`, `down_flip` | none (a shutter whose flip time is set, else `422`: tilt the slats / close, then tilt) |\n| `set_mode` | `{ \"mode\": \"auto\" \\| \"eco\" \\| \"manual\" }` (the unit's mode; `sleep` is not available) |\n| `set_temp`, `set_ac_mode`, `set_fan` | `{ \"temp\": 24 }`, `{ \"mode\": \"cool\" }`, `{ \"fan\": \"auto\" }` |\n| `key` | `{ \"key\": \"<remote key>\" }` (IR remotes) |\n| `run` | none (a scene; also `POST /v1/scenes/{id}/run`, `POST /v1/scenes/{id}/run`) |\n\n- Values out of range are `422` `validation`.\n- **Locks and gates**: unlocking or opening needs `allow_unlock: on` (else `403` `unlock_not_allowed`) and, for an agent, the user's explicit confirmation every time.\n- A unit that doesn't answer over IGHC is `504` `unit_unreachable`; when the gateway's IGHC adapter can't carry frames, `503` `ighc_unavailable`.\n- Every change is logged with the credential's name (see `GET /v1/activity`).\n\n[Reference](https://developers.igh.app/reference.html#op-control) · [Controlling devices](https://developers.igh.app/resources.html)",
          "preRequestScript": "",
          "testScript": ""
        },
        {
          "v": "17",
          "_ref_id": "req_90caca431c50120b46c5b90a",
          "name": "Config not allowed → 403 config_not_allowed",
          "method": "GET",
          "endpoint": "<<baseUrl>>/devices/<<deviceId>>/intents",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": null,
            "body": null
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "403",
              "active": true
            },
            {
              "key": "igh.code",
              "value": "config_not_allowed",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            },
            {
              "key": "igh.needs",
              "value": "deviceId",
              "active": true
            },
            {
              "key": "igh.what",
              "value": "device",
              "active": true
            }
          ],
          "responses": {},
          "description": "With the integration's unit settings off.\n\n**`GET /v1/devices/{id}/intents`** — The device's configuration intents with their cached values.\n\nA 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:\n\n- **read_write** — changed with `PUT /v1/devices/{id}/intents/{intent} { value }` (`PUT /v1/devices/{id}/intents/{intent}`) when the integration has `advanced_config: read_write`. The gateway sends the frames, and the new value is reported only after the unit confirmed it.\n- **read_only** — shown from the cache with `guidance` and an `appAction` naming the app screen that changes it.\n\n`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.\n\n- `advanced_config: off`: every intent call is `403` `config_not_allowed`; `read`: every change is.\n- A read-only intent, and resetting a unit, are `403` `not_offered`.\n- A setting packed with others in one register needs a cached value to change: without one it is `409` `not_cached` with `appAction: unit.menus` (`unit.menus`): the user reads the unit's menu in the app once.\n- Unit mode (Auto, Eco, Manual) is a control operation, `set_mode` (see `POST /v1/devices/{id}/control`), not an intent.\n\nThe intents, one by one, follow.\n\n[Reference](https://developers.igh.app/reference.html#op-intents) · [Cached unit data](https://developers.igh.app/cached-data.html)",
          "preRequestScript": "",
          "testScript": ""
        },
        {
          "v": "17",
          "_ref_id": "req_50ad81e8082faaf54ee48c06",
          "name": "Read-only intent → 403 not_offered",
          "method": "PUT",
          "endpoint": "<<baseUrl>>/devices/<<deviceId>>/intents/key_immunity",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": "application/json",
            "body": "{\n  \"value\": 1\n}"
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "403",
              "active": true
            },
            {
              "key": "igh.code",
              "value": "not_offered",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            },
            {
              "key": "igh.needs",
              "value": "deviceId",
              "active": true
            },
            {
              "key": "igh.what",
              "value": "device",
              "active": true
            }
          ],
          "responses": {},
          "description": "Key Immunity is read-only through the API (with unit settings on; off, it is `config_not_allowed`).\n\n**`PUT /v1/devices/{id}/intents/{intent}`** — Change a writable intent (`advanced_config: read_write`).\n\n[Reference](https://developers.igh.app/reference.html#op-setIntent) · [Intents](https://developers.igh.app/intents.html)",
          "preRequestScript": "",
          "testScript": ""
        },
        {
          "v": "17",
          "_ref_id": "req_932977db7de55d552e0ec8d7",
          "name": "Structural not allowed → 403 structural_not_allowed",
          "method": "PATCH",
          "endpoint": "<<baseUrl>>/rooms/<<roomId>>",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": "application/json",
            "body": "{\n  \"name\": \"<<roomName>>\",\n  \"floor\": \"hoppscotch\"\n}"
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "403",
              "active": true
            },
            {
              "key": "igh.code",
              "value": "structural_not_allowed",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            },
            {
              "key": "igh.needs",
              "value": "roomId",
              "active": true
            },
            {
              "key": "igh.what",
              "value": "room",
              "active": true
            }
          ],
          "responses": {},
          "description": "A rename may change only `name`: any other field is refused, whatever the settings.\n\n**`PATCH /v1/rooms/{id}`** — Rename a room inside the exposure (`structural: on`).\n\n[Reference](https://developers.igh.app/reference.html#op-renameRoom) · [Renaming rooms and units](https://developers.igh.app/resources.html)",
          "preRequestScript": "",
          "testScript": ""
        },
        {
          "v": "17",
          "_ref_id": "req_d637cd0dc4b6af7a193d56bf",
          "name": "Revision mismatch → 409 revision_mismatch",
          "method": "PATCH",
          "endpoint": "<<baseUrl>>/scenes/<<createdSceneId>>",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": "application/json",
            "body": "{\n  \"ifRevision\": 999999,\n  \"name\": \"Hoppscotch test scene (edited)\"\n}"
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "409",
              "active": true
            },
            {
              "key": "igh.code",
              "value": "revision_mismatch",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            },
            {
              "key": "igh.needs",
              "value": "createdSceneId",
              "active": true
            },
            {
              "key": "igh.what",
              "value": "test scene (Scenes → Create a test scene)",
              "active": true
            }
          ],
          "responses": {},
          "description": "**`PATCH /v1/scenes/{id}`** — Edit a scene (`ifRevision`).\n\n[Reference](https://developers.igh.app/reference.html#op-editScene) · [Scenes](https://developers.igh.app/resources.html)",
          "preRequestScript": "",
          "testScript": ""
        },
        {
          "v": "17",
          "_ref_id": "req_4dff79b5677810c5ea65ba36",
          "name": "Another API version → 410 version_unsupported (local only)",
          "method": "GET",
          "endpoint": "<<baseUrl>>/house",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": null,
            "body": null
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "410",
              "active": true
            },
            {
              "key": "igh.code",
              "value": "version_unsupported",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            },
            {
              "key": "igh.only",
              "value": "local",
              "active": true
            }
          ],
          "responses": {},
          "description": "Sends `/v2/house` (the pre-request script rewrites the URL). The cloud relay routes only `/v1`, so this runs on the home network only.\n\n**`GET /v1/house`** — Floors, rooms, units (modules with their gangs), scenes; live state and operations.\n\n`GET /v1/house` answers the home as the integration shows it: `Floor`s → `Room`s → 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.\n\n- `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)).\n- `GET /v1/devices/{id}`: one unit, module, gang or scene.\n\nCall `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`.\n\n[Reference](https://developers.igh.app/reference.html#op-house) · [Getting started](https://developers.igh.app/getting-started.html)",
          "preRequestScript": "hopp.request.setUrl(String(hopp.env.get(\"baseUrl\") || \"\").replace(/\\/v1$/, \"/v2\") + \"/house\");",
          "testScript": ""
        },
        {
          "v": "17",
          "_ref_id": "req_13366ea8507e7b9429a8dede",
          "name": "Bad token → 401 unauthorized",
          "method": "GET",
          "endpoint": "<<baseUrl>>/house",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "bearer",
            "authActive": true,
            "token": "igh_key_hoppscotch-not-a-credential"
          },
          "body": {
            "contentType": null,
            "body": null
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "401",
              "active": true
            },
            {
              "key": "igh.code",
              "value": "unauthorized",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            }
          ],
          "responses": {},
          "description": "**`GET /v1/house`** — Floors, rooms, units (modules with their gangs), scenes; live state and operations.\n\n`GET /v1/house` answers the home as the integration shows it: `Floor`s → `Room`s → 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.\n\n- `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)).\n- `GET /v1/devices/{id}`: one unit, module, gang or scene.\n\nCall `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`.\n\n[Reference](https://developers.igh.app/reference.html#op-house) · [Getting started](https://developers.igh.app/getting-started.html)",
          "preRequestScript": "",
          "testScript": ""
        },
        {
          "v": "17",
          "_ref_id": "req_d7cbf67dfa63635957f98ca4",
          "name": "Rate limit burst → 429 rate_limited (cloud only)",
          "method": "GET",
          "endpoint": "<<baseUrl>>/state",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": null,
            "body": null
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200,429",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            },
            {
              "key": "igh.only",
              "value": "cloud",
              "active": true
            }
          ],
          "responses": {},
          "description": "The relay allows 50 requests a second and 600 a minute per home (all its integrations, HTTPS, WebSocket and MCP together). Run this request alone (its folder → Run Collection, select only this request, Iterations 650, Delay 0): past the limit it answers 429 `rate_limited` with `retryAfter` (and the `Retry-After` header), which the Console prints. The gateway itself has no limit.\n\n**`GET /v1/state`** — Every exposed unit's and gang's state now.\n\n[Reference](https://developers.igh.app/reference.html#op-state) · [The house](https://developers.igh.app/resources.html)",
          "preRequestScript": "",
          "testScript": ""
        }
      ],
      "auth": {
        "authType": "inherit",
        "authActive": true
      },
      "headers": [],
      "variables": [],
      "preRequestScript": "",
      "testScript": ""
    },
    {
      "v": 12,
      "_ref_id": "coll_fada5167ff6cecb923dabd50",
      "name": "Smoke run (read-only)",
      "description": "Every read, top to bottom, with nothing switched or created: this folder → Run Collection after setting `<<token>>` (keep \"Stop run if an error occurs\" off: a request whose id is still empty stops before it is sent). Automations and schedules may answer 404 and device intents 403 when the integration has them off.",
      "folders": [],
      "requests": [
        {
          "v": "17",
          "_ref_id": "req_8a8541daa927a22318979d85",
          "name": "About",
          "method": "GET",
          "endpoint": "<<baseUrl>>/about",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "none",
            "authActive": true
          },
          "body": {
            "contentType": null,
            "body": null
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            }
          ],
          "responses": {},
          "description": "Needs no credential.\n\n**`GET /v1/about`** — How to connect, and the app steps (static text).\n\n[Reference](https://developers.igh.app/reference.html#op-about) · [Agents](https://developers.igh.app/agents.html)",
          "preRequestScript": "",
          "testScript": ""
        },
        {
          "v": "17",
          "_ref_id": "req_63e254b5658c1af3f1b3e882",
          "name": "Integration (who am I)",
          "method": "GET",
          "endpoint": "<<baseUrl>>/integration",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": null,
            "body": null
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            }
          ],
          "responses": {},
          "description": "**`GET /v1/integration`** — The integration this credential belongs to, the house, and its settings.\n\n[Reference](https://developers.igh.app/reference.html#op-integration) · [Concepts](https://developers.igh.app/overview.html)",
          "preRequestScript": "",
          "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar body = answer();\nif (body && body.integration) {\n  var credential = body.integration.credential || {};\n  console.log(\"House \" + body.house + \", integration \" + body.integration.name + \" (\" + body.integration.id + \"), credential \" + credential.kind + \".\");\n  console.log(\"Settings: \" + JSON.stringify(body.integration.settings));\n  fill(\"serial\", String(body.gateway || \"\").toLowerCase());\n}"
        },
        {
          "v": "17",
          "_ref_id": "req_bb581a4981798848b8fadafa",
          "name": "House",
          "method": "GET",
          "endpoint": "<<baseUrl>>/house",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": null,
            "body": null
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            }
          ],
          "responses": {},
          "description": "Fills the id variables the other folders use (`deviceId`, `lightId`, `dimmerId`, `shutterId`, `acId`, `irId`, `lockId`, `gateId`, `roomId`, `sceneId`), each only while it is empty: clear one to pick again, or set it to the id you want.\n\n**`GET /v1/house`** — Floors, rooms, units (modules with their gangs), scenes; live state and operations.\n\n`GET /v1/house` answers the home as the integration shows it: `Floor`s → `Room`s → 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.\n\n- `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)).\n- `GET /v1/devices/{id}`: one unit, module, gang or scene.\n\nCall `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`.\n\n[Reference](https://developers.igh.app/reference.html#op-house) · [Getting started](https://developers.igh.app/getting-started.html)",
          "preRequestScript": "",
          "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar house = answer();\nif (house && house.floors) {\n  var units = [];\n  var rooms = [];\n  house.floors.forEach(function (floor) { floor.rooms.forEach(function (room) { rooms.push(room); }); });\n  (house.rooms || []).forEach(function (room) { rooms.push(room); });\n  rooms.forEach(function (room) { (room.units || []).forEach(function (unit) { units.push(unit); }); });\n  (house.units || []).forEach(function (unit) { units.push(unit); });\n  var devices = [];\n  units.forEach(function (unit) {\n    devices.push(unit);\n    (unit.gangs || []).forEach(function (gang) { devices.push(gang); });\n  });\n  var has = function (device, op) { return (device.operations || []).indexOf(op) !== -1; };\n  var first = function (test) { for (var i = 0; i < devices.length; i += 1) { if (test(devices[i])) { return devices[i]; } } return null; };\n  var light = first(function (d) { return has(d, \"set_power\") && !has(d, \"set_temp\") && !has(d, \"key\") && (!d.class || d.class.indexOf(\"light\") === 0); });\n  var pick = {\n    lightId: light,\n    dimmerId: first(function (d) { return has(d, \"set_brightness\"); }),\n    shutterId: first(function (d) { return has(d, \"set_position\"); }),\n    acId: first(function (d) { return has(d, \"set_ac_mode\"); }),\n    irId: first(function (d) { return has(d, \"key\"); }),\n    lockId: first(function (d) { return has(d, \"unlock\"); }),\n    gateId: first(function (d) { return d.class === \"gate\"; })\n  };\n  Object.keys(pick).forEach(function (key) { if (pick[key]) { fill(key, pick[key].id); } });\n  var device = light || first(function (d) { return (d.operations || []).length > 0; }) || devices[0];\n  if (device) {\n    fill(\"deviceId\", device.id);\n    fill(\"deviceName\", device.name);\n  }\n  if (hopp.env.get(\"deviceId\")) { save(\"unitRef\", hopp.env.get(\"deviceId\").split(\":\")[2]); }\n  if (rooms[0]) { fill(\"roomId\", rooms[0].id); fill(\"roomName\", rooms[0].name); }\n  if (house.scenes && house.scenes[0]) { fill(\"sceneId\", house.scenes[0].id); }\n  console.log(\"House \" + house.house + \" (gateway \" + house.gateway + \"): \" + devices.length + \" devices, \" + rooms.length + \" rooms, \" + (house.scenes || []).length + \" scenes.\");\n  [\"deviceId\", \"lightId\", \"dimmerId\", \"shutterId\", \"acId\", \"irId\", \"lockId\", \"gateId\", \"roomId\", \"sceneId\"].forEach(function (key) {\n    console.log(key + \" = \" + (hopp.env.get(key) || \"(none in this house)\"));\n  });\n}"
        },
        {
          "v": "17",
          "_ref_id": "req_06126295832bc07889170b41",
          "name": "State",
          "method": "GET",
          "endpoint": "<<baseUrl>>/state",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": null,
            "body": null
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            }
          ],
          "responses": {},
          "description": "**`GET /v1/state`** — Every exposed unit's and gang's state now.\n\n[Reference](https://developers.igh.app/reference.html#op-state) · [The house](https://developers.igh.app/resources.html)",
          "preRequestScript": "",
          "testScript": ""
        },
        {
          "v": "17",
          "_ref_id": "req_67910092705f9f3fd6b6ea4b",
          "name": "Device",
          "method": "GET",
          "endpoint": "<<baseUrl>>/devices/<<deviceId>>",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": null,
            "body": null
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            },
            {
              "key": "igh.needs",
              "value": "deviceId",
              "active": true
            },
            {
              "key": "igh.what",
              "value": "device",
              "active": true
            }
          ],
          "responses": {},
          "description": "**`GET /v1/devices/{id}`** — One unit, module, gang or scene.\n\n[Reference](https://developers.igh.app/reference.html#op-device) · [The house](https://developers.igh.app/resources.html)",
          "preRequestScript": "",
          "testScript": ""
        },
        {
          "v": "17",
          "_ref_id": "req_29cec5d4df40cbf881cfe1b1",
          "name": "Scenes",
          "method": "GET",
          "endpoint": "<<baseUrl>>/scenes",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": null,
            "body": null
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            }
          ],
          "responses": {},
          "description": "**`GET /v1/scenes`** — The exposed scenes, their members and whether they run.\n\n- `GET /v1/scenes`: the exposed scenes, their members (as device ids, plus how many members this integration doesn't show) and whether they run.\n- `POST /v1/scenes/{id}/run` runs one.\n- `POST /v1/scenes` creates one in the app's scene schema (`SceneBody`): its `steps` (`Step`: `operate` a unit with one of its operations, `delay`, `run_scene`, `toggle_together`, `notify`) run in order and name only exposed devices, by unit id (the last part of a device id). On a *Selected* integration the new scene joins the selection. It is marked as created by this integration.\n- `PATCH /v1/scenes/{id}` changes one; send the `revision` you read as `ifRevision`, or a change made meanwhile is refused with `409` `revision_mismatch`: read it again and redo the edit.\n\nDeleting a scene is done in the app.\n\n[Reference](https://developers.igh.app/reference.html#op-scenes) · [Scenes](https://developers.igh.app/resources.html)",
          "preRequestScript": "",
          "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar body = answer();\nif (body) {\n  var first = (body.scenes || [])[0];\n  if (first) { fill(\"sceneId\", first.id); }\n}"
        },
        {
          "v": "17",
          "_ref_id": "req_5522b6d7fe7fa84c9db33159",
          "name": "Sensors",
          "method": "GET",
          "endpoint": "<<baseUrl>>/sensors",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": null,
            "body": null
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            }
          ],
          "responses": {},
          "description": "**`GET /v1/sensors`** — Every exposed sensor's readings.\n\n[Reference](https://developers.igh.app/reference.html#op-sensors) · [Sensors, energy and activity](https://developers.igh.app/resources.html)",
          "preRequestScript": "",
          "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar body = answer();\nif (body) {\n  var first = (body.sensors || [])[0];\n  if (first) { fill(\"sensorId\", first.id || first.device); }\n}"
        },
        {
          "v": "17",
          "_ref_id": "req_d7729ea3bd4d59553d3e2604",
          "name": "Energy (house, today)",
          "method": "GET",
          "endpoint": "<<baseUrl>>/energy",
          "params": [
            {
              "key": "scope",
              "value": "house",
              "active": true,
              "description": "house, room:<id>, floor:<id> or device:<device id>"
            },
            {
              "key": "period",
              "value": "day",
              "active": true,
              "description": "day, week, month or year (or from and to)"
            }
          ],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": null,
            "body": null
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            }
          ],
          "responses": {},
          "description": "**`GET /v1/energy`** — Energy and cost (`?scope=house|room:<id>|floor:<id>|device:<id>&period|from&to`).\n\n[Reference](https://developers.igh.app/reference.html#op-energy) · [Sensors, energy and activity](https://developers.igh.app/resources.html)",
          "preRequestScript": "",
          "testScript": ""
        },
        {
          "v": "17",
          "_ref_id": "req_9a27281f77fabba2b31f5a33",
          "name": "Activity",
          "method": "GET",
          "endpoint": "<<baseUrl>>/activity",
          "params": [
            {
              "key": "device",
              "value": "<<deviceId>>",
              "active": true,
              "description": "one exposed device (leave out for every one)"
            },
            {
              "key": "limit",
              "value": "20",
              "active": true,
              "description": "1–200 (default 50)"
            },
            {
              "key": "from",
              "value": "",
              "active": false,
              "description": "epoch ms (default: 30 days before to)"
            },
            {
              "key": "to",
              "value": "",
              "active": false,
              "description": "epoch ms (default: now)"
            }
          ],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": null,
            "body": null
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            }
          ],
          "responses": {},
          "description": "**`GET /v1/activity`** — The activity log of exposed items (`?device&from&to&limit`).\n\n[Reference](https://developers.igh.app/reference.html#op-activity) · [Sensors, energy and activity](https://developers.igh.app/resources.html)",
          "preRequestScript": "",
          "testScript": ""
        },
        {
          "v": "17",
          "_ref_id": "req_3a68d2d002cae20eb2efd72a",
          "name": "Intent table",
          "method": "GET",
          "endpoint": "<<baseUrl>>/intents",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": null,
            "body": null
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            }
          ],
          "responses": {},
          "description": "**`GET /v1/intents`** — Every intent's definition.\n\n[Reference](https://developers.igh.app/reference.html#op-intentTable) · [Intents](https://developers.igh.app/intents.html)",
          "preRequestScript": "",
          "testScript": ""
        },
        {
          "v": "17",
          "_ref_id": "req_455da69164635c8e4d3481ed",
          "name": "Device intents",
          "method": "GET",
          "endpoint": "<<baseUrl>>/devices/<<deviceId>>/intents",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": null,
            "body": null
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200,403",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            },
            {
              "key": "igh.needs",
              "value": "deviceId",
              "active": true
            },
            {
              "key": "igh.what",
              "value": "device",
              "active": true
            }
          ],
          "responses": {},
          "description": "403 `config_not_allowed` while the integration's unit settings are off. Fills `intentId` and `intentValue` with the first setting it may change.\n\n**`GET /v1/devices/{id}/intents`** — The device's configuration intents with their cached values.\n\nA 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:\n\n- **read_write** — changed with `PUT /v1/devices/{id}/intents/{intent} { value }` (`PUT /v1/devices/{id}/intents/{intent}`) when the integration has `advanced_config: read_write`. The gateway sends the frames, and the new value is reported only after the unit confirmed it.\n- **read_only** — shown from the cache with `guidance` and an `appAction` naming the app screen that changes it.\n\n`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.\n\n- `advanced_config: off`: every intent call is `403` `config_not_allowed`; `read`: every change is.\n- A read-only intent, and resetting a unit, are `403` `not_offered`.\n- A setting packed with others in one register needs a cached value to change: without one it is `409` `not_cached` with `appAction: unit.menus` (`unit.menus`): the user reads the unit's menu in the app once.\n- Unit mode (Auto, Eco, Manual) is a control operation, `set_mode` (see `POST /v1/devices/{id}/control`), not an intent.\n\nThe intents, one by one, follow.\n\n[Reference](https://developers.igh.app/reference.html#op-intents) · [Cached unit data](https://developers.igh.app/cached-data.html)",
          "preRequestScript": "",
          "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar body = answer();\nif (body) {\n  var writable = (body.intents || []).filter(function (i) { return i.access === \"read_write\" && i.value !== null && i.value !== undefined; })[0];\n  if (writable) { fill(\"intentId\", writable.intent); save(\"intentValue\", JSON.stringify(writable.value)); }\n}"
        },
        {
          "v": "17",
          "_ref_id": "req_9fe52b97307cf0778ec5b344",
          "name": "Automations",
          "method": "GET",
          "endpoint": "<<baseUrl>>/automations",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": null,
            "body": null
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200,404",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            }
          ],
          "responses": {},
          "description": "404 `not_found` while the integration's automations are off.\n\n**`GET /v1/automations`** — Automations whose references are all exposed (`automations: on`).\n\n[Reference](https://developers.igh.app/reference.html#op-automations) · [Automations and schedules](https://developers.igh.app/resources.html)",
          "preRequestScript": "",
          "testScript": ""
        },
        {
          "v": "17",
          "_ref_id": "req_90fa49955ae6268e294ca26c",
          "name": "Schedules",
          "method": "GET",
          "endpoint": "<<baseUrl>>/schedules",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": null,
            "body": null
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200,404",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            }
          ],
          "responses": {},
          "description": "404 `not_found` while the integration's schedules are off.\n\n**`GET /v1/schedules`** — The gateway's schedules whose references are all exposed (`schedules: on`).\n\n[Reference](https://developers.igh.app/reference.html#op-schedules) · [Automations and schedules](https://developers.igh.app/resources.html)",
          "preRequestScript": "",
          "testScript": ""
        },
        {
          "v": "17",
          "_ref_id": "req_2e4425a051d80e96ed68b91d",
          "name": "Gateway CA (to pin)",
          "method": "GET",
          "endpoint": "<<baseUrl>>/gateway/tls",
          "params": [],
          "headers": [],
          "auth": {
            "authType": "inherit",
            "authActive": true
          },
          "body": {
            "contentType": null,
            "body": null
          },
          "requestVariables": [
            {
              "key": "igh.expect",
              "value": "200",
              "active": true
            },
            {
              "key": "igh.kind",
              "value": "api",
              "active": true
            }
          ],
          "responses": {},
          "description": "Works on both transports, but the first time fetch it through the cloud environment: on the home network Hoppscotch must already trust the CA. The Console prints `ca`: save it as `ca.pem`, compare `fingerprint` (`openssl x509 -in ca.pem -noout -fingerprint -sha256`), then add the file to the gateway's domain in Settings → Interceptor → Native (README step 1).\n\n**`GET /v1/gateway/tls`** — The gateway's local CA (PEM) and its SHA-256 fingerprint, to pin.\n\nOn 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.\n\n**Never turn off certificate verification** (`curl -k`, `verify=False`, `rejectUnauthorized: false`, `InsecureSkipVerify`). There is no plain-HTTP fallback.\n\n**Getting the CA.** Either:\n- from the integration's page in the app: the CA's fingerprint and a download of its file; or\n- `GET /v1/gateway/tls` through `https://api.igh.app` (publicly trusted TLS): `{ ca, fingerprint, algorithm: \"SHA-256\", createdAt, house }`. `ca` is the PEM certificate; `fingerprint` is the SHA-256 of its DER bytes, upper-case hex pairs joined by colons.\n\nCheck 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.\n\n**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.\n\n[Reference](https://developers.igh.app/reference.html#op-tls) · [Getting started](https://developers.igh.app/getting-started.html)",
          "preRequestScript": "",
          "testScript": "// Shared by every request's post-request script (Hoppscotch runs each script in\n// a function of its own, so each one carries these): save() writes the selected\n// environment (every key the scripts write is declared there), fill() only\n// while empty, answer() is the JSON body or null.\nfunction save(key, value) {\n  hopp.env.active.set(key, value === undefined || value === null ? \"\" : String(value));\n}\nfunction fill(key, value) {\n  if (!hopp.env.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }\n}\nfunction answer() {\n  try { return hopp.response.body.asJSON(); } catch (e) { return null; }\n}\n\nvar body = answer();\nif (body && body.ca) {\n  save(\"caFingerprint\", body.fingerprint);\n  console.log(\"CA fingerprint (\" + body.algorithm + \"): \" + body.fingerprint);\n  console.log(\"Save the next entry, from -----BEGIN to END CERTIFICATE-----, as ca.pem; then README step 1.\");\n  console.log(body.ca);\n}"
        }
      ],
      "auth": {
        "authType": "inherit",
        "authActive": true
      },
      "headers": [],
      "variables": [],
      "preRequestScript": "",
      "testScript": ""
    }
  ],
  "requests": [],
  "auth": {
    "authType": "bearer",
    "authActive": true,
    "token": "<<token>>"
  },
  "headers": [],
  "variables": [],
  "preRequestScript": "// IGH home API, before every request. Each request says what it needs in its\n// Variables tab (igh.only, igh.record, igh.needs, igh.what). Hoppscotch can't\n// skip a request from a script, so one that can't work here stops before it is\n// sent, with the reason in the response pane (in the Runner, the run goes on\n// unless \"Stop run if an error occurs\" is on).\nvar only = hopp.request.variables.get(\"igh.only\") || \"\";\nvar transport = hopp.env.get(\"transport\") || \"\";\nif (only && transport && transport !== only) {\n  throw new Error(\"Not sent: this request runs only in an environment with transport \" + only + \" (this one is \" + transport + \").\");\n}\nif (hopp.request.variables.get(\"igh.record\") === \"true\" && !hopp.env.get(\"recordUrl\")) {\n  throw new Error(\"Not sent: set recordUrl in the environment: the integration's MCP address (https://mcp.igh.app/r/<id>), copied from its page in the IGH app.\");\n}\nvar needs = hopp.request.variables.get(\"igh.needs\") || \"\";\nif (needs && !hopp.env.get(needs)) {\n  throw new Error(\"Not sent: \" + needs + \" is empty (\" + (hopp.request.variables.get(\"igh.what\") || \"\") + \"): set it in the environment (Getting started → House fills the device ids).\");\n}\nif (/<|igh-\\.local/.test(hopp.env.get(\"baseUrl\") || \"\")) {\n  console.warn(\"baseUrl has no gateway id: set serial in the environment (the gateway id, lower case).\");\n}",
  "testScript": "// IGH home API, after every request: the status the request expects\n// (igh.expect in its Variables tab) and, for a refusal, the one error shape\n// { error: { code, message, appAction?, retryAfter? } }.\nvar expected = String(hopp.request.variables.get(\"igh.expect\") || \"\").split(\",\").filter(Boolean).map(Number);\nvar kind = hopp.request.variables.get(\"igh.kind\") || \"api\";\nvar wanted = hopp.request.variables.get(\"igh.code\") || \"\";\nvar status = hopp.response.statusCode;\nvar body = null;\ntry { body = hopp.response.body.asJSON(); } catch (e) { body = null; }\nvar header = function (name) {\n  var found = (hopp.response.headers || []).filter(function (h) { return String(h.key).toLowerCase() === name; })[0];\n  return found ? found.value : \"\";\n};\nif (expected.length > 0) {\n  hopp.test(\"status is \" + expected.join(\" or \"), function () {\n    hopp.expect(expected).to.include(status);\n  });\n}\nif (kind === \"api\" && status >= 400) {\n  hopp.test(\"the error is { error: { code, message } }\", function () {\n    hopp.expect(body).to.be.an(\"object\");\n    hopp.expect(body.error).to.be.an(\"object\");\n    hopp.expect(body.error.code).to.be.a(\"string\");\n    hopp.expect(body.error.message).to.be.a(\"string\");\n  });\n  if (body && body.error && typeof body.error === \"object\") {\n    console.log(\"IGH \" + status + \" \" + body.error.code + \": \" + body.error.message);\n    if (body.error.appAction) { console.log(\"appAction (the app screen that does it): \" + body.error.appAction); }\n    if (body.error.retryAfter !== undefined) {\n      console.log(\"retryAfter: \" + body.error.retryAfter + \" s (Retry-After header: \" + (header(\"retry-after\") || \"none\") + \")\");\n    }\n  }\n}\nif (kind === \"api\" && status < 300 && body && !/\\/about$/.test(String(hopp.request.url))) {\n  hopp.test(\"the answer names the house\", function () { hopp.expect(body.house).to.be.a(\"string\"); });\n}\nif (wanted) {\n  hopp.test(\"error code is \" + wanted, function () {\n    var got = body && body.error ? (typeof body.error === \"string\" ? body.error : body.error.code) : undefined;\n    hopp.expect(got).to.equal(wanted);\n  });\n}\nif (status === 401 && !hopp.env.get(\"token\")) {\n  console.warn(\"token is empty: run one of the Authorization folders first.\");\n}"
}
