{
  "info": {
    "_postman_id": "733b9aa7-d88c-4bd0-8879-0f2e63dcd018",
    "name": "IGH home API v1",
    "description": "The IGH home API v1 (1.1.0), generated from its OpenAPI document. Guides: https://developers.igh.app · Postman guide: https://developers.igh.app/postman.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. Run **Smoke run (read-only)** in the Collection Runner, 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 skip themselves on the home network when they need the relay); *(local only)* ones skip themselves in a cloud environment.\n\nEvery request checks its status; 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`.\n\nThe WebSocket `/v1/stream` is not in this file (Collection v2.1 has no WebSocket requests): import the `collections/IGH WebSocket` folder (Collection Format v3), or follow WEBSOCKETS.md.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
    "version": "1.1.0"
  },
  "item": [
    {
      "id": "69d956ad-51a8-4ebf-8b7c-d41fc6e7d8c6",
      "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.",
      "item": [
        {
          "id": "d6f847ba-f4fe-430a-899b-f34b95e0e921",
          "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}}`.",
          "item": [
            {
              "id": "cfe87d10-8fed-49d7-85ac-1290bab0c454",
              "name": "Use the key as {{token}}",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "2276b571-7f1c-4a65-80b5-b800db490b0e",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"200\");",
                      "pm.variables.set(\"igh.code\", \"\");",
                      "pm.variables.set(\"igh.kind\", \"api\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"\");",
                      "pm.variables.set(\"igh.what\", \"\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "",
                      "if (!pm.variables.get(\"apiKey\")) {",
                      "  throw new Error(\"Paste the key into the environment's apiKey first.\");",
                      "}"
                    ]
                  }
                },
                {
                  "listen": "test",
                  "script": {
                    "id": "724ba488-4a40-4718-8489-6a53b631c86d",
                    "type": "text/javascript",
                    "exec": [
                      "// Shared by every request's test script: save() writes the environment when it",
                      "// declares the key (an empty environment value hides a collection variable) and",
                      "// the collection; fill() only while empty; answer() is the JSON body or null.",
                      "",
                      "function save(key, value) {",
                      "  var text = value === undefined || value === null ? \"\" : String(value);",
                      "  if (pm.environment.has(key)) { pm.environment.set(key, text); }",
                      "  pm.collectionVariables.set(key, text);",
                      "}",
                      "function fill(key, value) {",
                      "  if (!pm.variables.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }",
                      "}",
                      "function answer() {",
                      "  try { return pm.response.json(); } catch (e) { return null; }",
                      "}",
                      "",
                      "var body = answer();",
                      "if (body && body.integration) {",
                      "  var credential = body.integration.credential || {};",
                      "  console.log(\"House \" + body.house + \", integration \" + body.integration.name + \" (\" + body.integration.id + \"), credential \" + credential.kind + \".\");",
                      "  console.log(\"Settings: \" + JSON.stringify(body.integration.settings));",
                      "  fill(\"serial\", String(body.gateway || \"\").toLowerCase());",
                      "}",
                      "",
                      "if (pm.response.code === 200) {",
                      "  save(\"token\", pm.variables.get(\"apiKey\"));",
                      "  save(\"tokenKind\", \"key\");",
                      "  pm.test(\"{{token}} is the key\", function () { pm.expect(pm.variables.get(\"token\")).to.match(/^igh_key_/); });",
                      "}"
                    ]
                  }
                }
              ],
              "request": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/integration",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "integration"
                  ]
                },
                "auth": {
                  "type": "bearer",
                  "bearer": [
                    {
                      "key": "token",
                      "value": "{{apiKey}}",
                      "type": "string"
                    }
                  ]
                },
                "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)"
              },
              "response": []
            }
          ]
        },
        {
          "id": "b1af1f0f-911c-483f-8877-14fa853e4a9a",
          "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).",
          "item": [
            {
              "id": "cd1997e3-b0c5-448a-85a7-de612cf1491a",
              "name": "1. Ask for access (cloud only)",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "2fa08876-117d-4b72-815b-a5b7226f3bad",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"200\");",
                      "pm.variables.set(\"igh.code\", \"\");",
                      "pm.variables.set(\"igh.kind\", \"plain\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"\");",
                      "pm.variables.set(\"igh.what\", \"\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "",
                      "if (!pm.variables.get(\"recordUrl\")) {",
                      "  throw new Error(\"Set recordUrl in the environment: the integration's MCP address (https://mcp.igh.app/r/<id>), copied from its page in the IGH app.\");",
                      "}"
                    ]
                  }
                },
                {
                  "listen": "test",
                  "script": {
                    "id": "c99cb2b2-88d4-4820-89cf-1a547a540b1d",
                    "type": "text/javascript",
                    "exec": [
                      "// Shared by every request's test script: save() writes the environment when it",
                      "// declares the key (an empty environment value hides a collection variable) and",
                      "// the collection; fill() only while empty; answer() is the JSON body or null.",
                      "",
                      "function save(key, value) {",
                      "  var text = value === undefined || value === null ? \"\" : String(value);",
                      "  if (pm.environment.has(key)) { pm.environment.set(key, text); }",
                      "  pm.collectionVariables.set(key, text);",
                      "}",
                      "function fill(key, value) {",
                      "  if (!pm.variables.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }",
                      "}",
                      "function answer() {",
                      "  try { return pm.response.json(); } catch (e) { return null; }",
                      "}",
                      "",
                      "var body = answer() || {};",
                      "if (pm.response.code === 200) {",
                      "  save(\"pairCode\", body.code);",
                      "  save(\"pairUserCode\", body.userCode);",
                      "  save(\"pairInterval\", body.interval || 5);",
                      "  save(\"pairPolls\", 0);",
                      "  console.log(\"Matching code: \" + body.userCode + \". Tell the administrator: the IGH app shows the same code; Accept (or Accept for this session) it there.\");",
                      "  pm.test(\"matching code to compare in the IGH app: \" + body.userCode, function () {",
                      "    pm.expect(body.userCode).to.match(/^[2-9A-Z]{3}-[2-9A-Z]{2}$/);",
                      "  });",
                      "  pm.visualizer.set(\"<div style='font-family:sans-serif'><p>Matching code</p><h1 style='font-size:48px;letter-spacing:4px'>{{code}}</h1><p>The administrator sees the same code in the IGH app and taps Accept. Then send the next request.</p></div>\", { code: body.userCode });",
                      "}"
                    ]
                  }
                }
              ],
              "request": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json",
                    "type": "text"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"record\": \"{{recordUrl}}\",\n  \"name\": \"Postman\",\n  \"platform\": \"Postman\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{pairBase}}/pair",
                  "host": [
                    "{{pairBase}}"
                  ],
                  "path": [
                    "pair"
                  ]
                },
                "auth": {
                  "type": "noauth"
                },
                "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, the test name and the Visualize tab 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)"
              },
              "response": []
            },
            {
              "id": "8a8d93e9-566c-4063-8b03-7658407e9f50",
              "name": "2. Wait for approval (repeats in the Runner) (cloud only)",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "ec50ccae-f2e1-4a27-88cc-596156911482",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"200,400\");",
                      "pm.variables.set(\"igh.code\", \"\");",
                      "pm.variables.set(\"igh.kind\", \"oauth\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"\");",
                      "pm.variables.set(\"igh.what\", \"\");",
                      "pm.variables.set(\"igh.credential\", \"agent\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "",
                      "if (!pm.variables.get(\"pairCode\")) {",
                      "  throw new Error(\"No pairCode: send the request before this one (POST /v1/pair) first.\");",
                      "}",
                      "var seconds = Number(pm.variables.get(\"pairInterval\") || 5);",
                      "if (Number(pm.variables.get(\"pairPolls\") || 0) > 0) {",
                      "  setTimeout(function () {}, seconds * 1000);",
                      "}"
                    ]
                  }
                },
                {
                  "listen": "test",
                  "script": {
                    "id": "c9d5583a-199c-4237-8567-c8e601784da1",
                    "type": "text/javascript",
                    "exec": [
                      "// Shared by every request's test script: save() writes the environment when it",
                      "// declares the key (an empty environment value hides a collection variable) and",
                      "// the collection; fill() only while empty; answer() is the JSON body or null.",
                      "",
                      "function save(key, value) {",
                      "  var text = value === undefined || value === null ? \"\" : String(value);",
                      "  if (pm.environment.has(key)) { pm.environment.set(key, text); }",
                      "  pm.collectionVariables.set(key, text);",
                      "}",
                      "function fill(key, value) {",
                      "  if (!pm.variables.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }",
                      "}",
                      "function answer() {",
                      "  try { return pm.response.json(); } catch (e) { return null; }",
                      "}",
                      "",
                      "var body = answer() || {};",
                      "var polls = Number(pm.variables.get(\"pairPolls\") || 0) + 1;",
                      "save(\"pairPolls\", polls);",
                      "if (pm.response.code === 200 && body.access_token) {",
                      "  save(\"token\", body.access_token);",
                      "  save(\"tokenKind\", body.kind);",
                      "  save(\"pairCode\", \"\");",
                      "  save(\"pairPolls\", 0);",
                      "  pm.test(\"approved: {{token}} is set (\" + body.kind + \")\", function () {",
                      "    pm.expect(body.access_token).to.match(/^igh_(agent|session)_/);",
                      "  });",
                      "  pm.test(\"the credential kind is \" + pm.variables.get(\"igh.credential\"), function () {",
                      "    pm.expect(body.kind).to.eql(pm.variables.get(\"igh.credential\"));",
                      "  });",
                      "} else if (body.error === \"authorization_pending\" || body.error === \"slow_down\") {",
                      "  if (body.error === \"slow_down\") { save(\"pairInterval\", Number(pm.variables.get(\"pairInterval\") || 5) + 5); }",
                      "  console.log(\"Waiting for an administrator to answer in the IGH app (poll \" + polls + \"). Matching code: \" + (pm.variables.get(\"pairUserCode\") || \"?\"));",
                      "  if (polls < 150) {",
                      "    // In the Collection Runner this request runs again (after pairInterval seconds); sent by hand, send it again yourself.",
                      "    pm.execution.setNextRequest(pm.info.requestId);",
                      "  }",
                      "} else {",
                      "  console.log(\"The request ended: \" + (body.error || pm.response.code) + (body.error_description ? \" (\" + body.error_description + \")\" : \"\"));",
                      "  save(\"pairCode\", \"\");",
                      "}"
                    ]
                  }
                }
              ],
              "request": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json",
                    "type": "text"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"code\": \"{{pairCode}}\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{pairBase}}/pair/token",
                  "host": [
                    "{{pairBase}}"
                  ],
                  "path": [
                    "pair",
                    "token"
                  ]
                },
                "auth": {
                  "type": "noauth"
                },
                "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\nSent by hand, send it again after a few seconds. In the Collection Runner (run this folder) it repeats itself with `pm.execution.setNextRequest`, waiting `pairInterval` seconds between polls, until approved, denied (`access_denied`) or expired (`expired_token`, after 10 minutes).\n\n[Authentication and Accept agent](https://developers.igh.app/authentication.html) · [Getting started](https://developers.igh.app/getting-started.html)"
              },
              "response": []
            },
            {
              "id": "947ca142-5281-4879-8664-034d0c9d1161",
              "name": "3. Check the agent credential",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "54ff8d5a-db24-4588-8d52-ceb1e09d94d9",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"200\");",
                      "pm.variables.set(\"igh.code\", \"\");",
                      "pm.variables.set(\"igh.kind\", \"api\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"\");",
                      "pm.variables.set(\"igh.what\", \"\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}"
                    ]
                  }
                },
                {
                  "listen": "test",
                  "script": {
                    "id": "497bfe15-dfc7-479b-80db-f9f7577ec627",
                    "type": "text/javascript",
                    "exec": [
                      "// Shared by every request's test script: save() writes the environment when it",
                      "// declares the key (an empty environment value hides a collection variable) and",
                      "// the collection; fill() only while empty; answer() is the JSON body or null.",
                      "",
                      "function save(key, value) {",
                      "  var text = value === undefined || value === null ? \"\" : String(value);",
                      "  if (pm.environment.has(key)) { pm.environment.set(key, text); }",
                      "  pm.collectionVariables.set(key, text);",
                      "}",
                      "function fill(key, value) {",
                      "  if (!pm.variables.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }",
                      "}",
                      "function answer() {",
                      "  try { return pm.response.json(); } catch (e) { return null; }",
                      "}",
                      "",
                      "var body = answer();",
                      "if (body && body.integration) {",
                      "  var credential = body.integration.credential || {};",
                      "  console.log(\"House \" + body.house + \", integration \" + body.integration.name + \" (\" + body.integration.id + \"), credential \" + credential.kind + \".\");",
                      "  console.log(\"Settings: \" + JSON.stringify(body.integration.settings));",
                      "  fill(\"serial\", String(body.gateway || \"\").toLowerCase());",
                      "}"
                    ]
                  }
                }
              ],
              "request": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/integration",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "integration"
                  ]
                },
                "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)"
              },
              "response": []
            }
          ]
        },
        {
          "id": "7f3f9bed-6737-4b67-82d5-8e0f263836a2",
          "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.",
          "item": [
            {
              "id": "2d7d5e8a-eaca-4d1d-8070-db1d5894bcde",
              "name": "1. Ask for access (cloud only)",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "ce5a62ed-ddc9-4114-8498-4d62e6459fe0",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"200\");",
                      "pm.variables.set(\"igh.code\", \"\");",
                      "pm.variables.set(\"igh.kind\", \"plain\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"\");",
                      "pm.variables.set(\"igh.what\", \"\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "",
                      "if (!pm.variables.get(\"recordUrl\")) {",
                      "  throw new Error(\"Set recordUrl in the environment: the integration's MCP address (https://mcp.igh.app/r/<id>), copied from its page in the IGH app.\");",
                      "}"
                    ]
                  }
                },
                {
                  "listen": "test",
                  "script": {
                    "id": "5a813d1f-3e9b-4c48-882d-fc98ae046049",
                    "type": "text/javascript",
                    "exec": [
                      "// Shared by every request's test script: save() writes the environment when it",
                      "// declares the key (an empty environment value hides a collection variable) and",
                      "// the collection; fill() only while empty; answer() is the JSON body or null.",
                      "",
                      "function save(key, value) {",
                      "  var text = value === undefined || value === null ? \"\" : String(value);",
                      "  if (pm.environment.has(key)) { pm.environment.set(key, text); }",
                      "  pm.collectionVariables.set(key, text);",
                      "}",
                      "function fill(key, value) {",
                      "  if (!pm.variables.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }",
                      "}",
                      "function answer() {",
                      "  try { return pm.response.json(); } catch (e) { return null; }",
                      "}",
                      "",
                      "var body = answer() || {};",
                      "if (pm.response.code === 200) {",
                      "  save(\"pairCode\", body.code);",
                      "  save(\"pairUserCode\", body.userCode);",
                      "  save(\"pairInterval\", body.interval || 5);",
                      "  save(\"pairPolls\", 0);",
                      "  console.log(\"Matching code: \" + body.userCode + \". Tell the administrator: the IGH app shows the same code; Accept (or Accept for this session) it there.\");",
                      "  pm.test(\"matching code to compare in the IGH app: \" + body.userCode, function () {",
                      "    pm.expect(body.userCode).to.match(/^[2-9A-Z]{3}-[2-9A-Z]{2}$/);",
                      "  });",
                      "  pm.visualizer.set(\"<div style='font-family:sans-serif'><p>Matching code</p><h1 style='font-size:48px;letter-spacing:4px'>{{code}}</h1><p>The administrator sees the same code in the IGH app and taps Accept. Then send the next request.</p></div>\", { code: body.userCode });",
                      "}"
                    ]
                  }
                }
              ],
              "request": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json",
                    "type": "text"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"record\": \"{{recordUrl}}\",\n  \"name\": \"Postman (session)\",\n  \"platform\": \"Postman\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{pairBase}}/pair",
                  "host": [
                    "{{pairBase}}"
                  ],
                  "path": [
                    "pair"
                  ]
                },
                "auth": {
                  "type": "noauth"
                },
                "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, the test name and the Visualize tab 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)"
              },
              "response": []
            },
            {
              "id": "36a9e901-9b5b-456c-8e36-e304329ed8fb",
              "name": "2. Wait for approval (repeats in the Runner) (cloud only)",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "9f3ffb3c-701b-49a1-8c35-2e26ea8a8084",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"200,400\");",
                      "pm.variables.set(\"igh.code\", \"\");",
                      "pm.variables.set(\"igh.kind\", \"oauth\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"\");",
                      "pm.variables.set(\"igh.what\", \"\");",
                      "pm.variables.set(\"igh.credential\", \"session\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "",
                      "if (!pm.variables.get(\"pairCode\")) {",
                      "  throw new Error(\"No pairCode: send the request before this one (POST /v1/pair) first.\");",
                      "}",
                      "var seconds = Number(pm.variables.get(\"pairInterval\") || 5);",
                      "if (Number(pm.variables.get(\"pairPolls\") || 0) > 0) {",
                      "  setTimeout(function () {}, seconds * 1000);",
                      "}"
                    ]
                  }
                },
                {
                  "listen": "test",
                  "script": {
                    "id": "7406125f-9041-4964-81a2-5d353477ef4b",
                    "type": "text/javascript",
                    "exec": [
                      "// Shared by every request's test script: save() writes the environment when it",
                      "// declares the key (an empty environment value hides a collection variable) and",
                      "// the collection; fill() only while empty; answer() is the JSON body or null.",
                      "",
                      "function save(key, value) {",
                      "  var text = value === undefined || value === null ? \"\" : String(value);",
                      "  if (pm.environment.has(key)) { pm.environment.set(key, text); }",
                      "  pm.collectionVariables.set(key, text);",
                      "}",
                      "function fill(key, value) {",
                      "  if (!pm.variables.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }",
                      "}",
                      "function answer() {",
                      "  try { return pm.response.json(); } catch (e) { return null; }",
                      "}",
                      "",
                      "var body = answer() || {};",
                      "var polls = Number(pm.variables.get(\"pairPolls\") || 0) + 1;",
                      "save(\"pairPolls\", polls);",
                      "if (pm.response.code === 200 && body.access_token) {",
                      "  save(\"token\", body.access_token);",
                      "  save(\"tokenKind\", body.kind);",
                      "  save(\"pairCode\", \"\");",
                      "  save(\"pairPolls\", 0);",
                      "  pm.test(\"approved: {{token}} is set (\" + body.kind + \")\", function () {",
                      "    pm.expect(body.access_token).to.match(/^igh_(agent|session)_/);",
                      "  });",
                      "  pm.test(\"the credential kind is \" + pm.variables.get(\"igh.credential\"), function () {",
                      "    pm.expect(body.kind).to.eql(pm.variables.get(\"igh.credential\"));",
                      "  });",
                      "} else if (body.error === \"authorization_pending\" || body.error === \"slow_down\") {",
                      "  if (body.error === \"slow_down\") { save(\"pairInterval\", Number(pm.variables.get(\"pairInterval\") || 5) + 5); }",
                      "  console.log(\"Waiting for an administrator to answer in the IGH app (poll \" + polls + \"). Matching code: \" + (pm.variables.get(\"pairUserCode\") || \"?\"));",
                      "  if (polls < 150) {",
                      "    // In the Collection Runner this request runs again (after pairInterval seconds); sent by hand, send it again yourself.",
                      "    pm.execution.setNextRequest(pm.info.requestId);",
                      "  }",
                      "} else {",
                      "  console.log(\"The request ended: \" + (body.error || pm.response.code) + (body.error_description ? \" (\" + body.error_description + \")\" : \"\"));",
                      "  save(\"pairCode\", \"\");",
                      "}"
                    ]
                  }
                }
              ],
              "request": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json",
                    "type": "text"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"code\": \"{{pairCode}}\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{pairBase}}/pair/token",
                  "host": [
                    "{{pairBase}}"
                  ],
                  "path": [
                    "pair",
                    "token"
                  ]
                },
                "auth": {
                  "type": "noauth"
                },
                "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\nSent by hand, send it again after a few seconds. In the Collection Runner (run this folder) it repeats itself with `pm.execution.setNextRequest`, waiting `pairInterval` seconds between polls, until approved, denied (`access_denied`) or expired (`expired_token`, after 10 minutes).\n\n[Authentication and Accept agent](https://developers.igh.app/authentication.html) · [Getting started](https://developers.igh.app/getting-started.html)"
              },
              "response": []
            },
            {
              "id": "d2f15b83-3258-4486-8f6c-b187cb68177e",
              "name": "3. Check the session credential",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "140492f9-b702-4861-8eab-36558ee9965c",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"200\");",
                      "pm.variables.set(\"igh.code\", \"\");",
                      "pm.variables.set(\"igh.kind\", \"api\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"\");",
                      "pm.variables.set(\"igh.what\", \"\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}"
                    ]
                  }
                },
                {
                  "listen": "test",
                  "script": {
                    "id": "a06169a3-de0b-4de5-8b82-484d147ca0e9",
                    "type": "text/javascript",
                    "exec": [
                      "// Shared by every request's test script: save() writes the environment when it",
                      "// declares the key (an empty environment value hides a collection variable) and",
                      "// the collection; fill() only while empty; answer() is the JSON body or null.",
                      "",
                      "function save(key, value) {",
                      "  var text = value === undefined || value === null ? \"\" : String(value);",
                      "  if (pm.environment.has(key)) { pm.environment.set(key, text); }",
                      "  pm.collectionVariables.set(key, text);",
                      "}",
                      "function fill(key, value) {",
                      "  if (!pm.variables.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }",
                      "}",
                      "function answer() {",
                      "  try { return pm.response.json(); } catch (e) { return null; }",
                      "}",
                      "",
                      "var body = answer();",
                      "if (body && body.integration) {",
                      "  var credential = body.integration.credential || {};",
                      "  console.log(\"House \" + body.house + \", integration \" + body.integration.name + \" (\" + body.integration.id + \"), credential \" + credential.kind + \".\");",
                      "  console.log(\"Settings: \" + JSON.stringify(body.integration.settings));",
                      "  fill(\"serial\", String(body.gateway || \"\").toLowerCase());",
                      "}"
                    ]
                  }
                }
              ],
              "request": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/integration",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "integration"
                  ]
                },
                "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)"
              },
              "response": []
            }
          ]
        },
        {
          "id": "fe20801c-8ede-4a66-83aa-f5c9b3aece33",
          "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\nThis folder's Authorization tab holds Postman's OAuth 2.0 helper, filled from the variables the first three requests save. After them, open the folder's Authorization tab (or request 4's) → **Get New Access Token**: the browser opens IGH's approval page (no sign-in for the integration's own address), an administrator approves in the IGH app, and Postman receives the token → **Use Token**. Then send 4, which copies it into `{{token}}`.\n\nIf Postman asks: client authentication is \"Send client credentials in body\" (a public client, no secret).",
          "item": [
            {
              "id": "26f522cd-97f2-4366-8478-32094098068f",
              "name": "1. Protected-resource metadata",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "2b171cc1-a95b-4bcf-8fab-9914eeb192a9",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"200\");",
                      "pm.variables.set(\"igh.code\", \"\");",
                      "pm.variables.set(\"igh.kind\", \"plain\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"\");",
                      "pm.variables.set(\"igh.what\", \"\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "",
                      "if (!pm.variables.get(\"recordUrl\")) {",
                      "  throw new Error(\"Set recordUrl in the environment: the integration's MCP address (https://mcp.igh.app/r/<id>), copied from its page in the IGH app.\");",
                      "}",
                      "",
                      "var record = pm.variables.replaceIn(\"{{recordUrl}}\").replace(/\\/+$/, \"\");",
                      "var parts = /^(https?:\\/\\/[^/]+)(\\/.*)?$/.exec(record);",
                      "pm.collectionVariables.set(\"oauthResourceMetadata\", parts[1] + \"/.well-known/oauth-protected-resource\" + (parts[2] || \"\"));"
                    ]
                  }
                },
                {
                  "listen": "test",
                  "script": {
                    "id": "351feee7-1544-4fb8-89ac-57327e115282",
                    "type": "text/javascript",
                    "exec": [
                      "// Shared by every request's test script: save() writes the environment when it",
                      "// declares the key (an empty environment value hides a collection variable) and",
                      "// the collection; fill() only while empty; answer() is the JSON body or null.",
                      "",
                      "function save(key, value) {",
                      "  var text = value === undefined || value === null ? \"\" : String(value);",
                      "  if (pm.environment.has(key)) { pm.environment.set(key, text); }",
                      "  pm.collectionVariables.set(key, text);",
                      "}",
                      "function fill(key, value) {",
                      "  if (!pm.variables.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }",
                      "}",
                      "function answer() {",
                      "  try { return pm.response.json(); } catch (e) { return null; }",
                      "}",
                      "",
                      "var body = answer() || {};",
                      "save(\"oauthIssuer\", (body.authorization_servers || [])[0]);",
                      "pm.test(\"the resource is {{recordUrl}}\", function () { pm.expect(body.resource).to.eql(pm.variables.replaceIn(\"{{recordUrl}}\").replace(/\\/+$/, \"\")); });",
                      "pm.test(\"bearer in the header\", function () { pm.expect(body.bearer_methods_supported).to.include(\"header\"); });"
                    ]
                  }
                }
              ],
              "request": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{oauthResourceMetadata}}",
                  "host": [
                    "{{oauthResourceMetadata}}"
                  ],
                  "path": []
                },
                "auth": {
                  "type": "noauth"
                },
                "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)"
              },
              "response": []
            },
            {
              "id": "04c00a24-28f8-420c-8b50-01d3f0481e24",
              "name": "2. Authorization-server metadata",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "5e7149c0-f9b5-467d-8086-4bd693704d98",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"200\");",
                      "pm.variables.set(\"igh.code\", \"\");",
                      "pm.variables.set(\"igh.kind\", \"plain\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"\");",
                      "pm.variables.set(\"igh.what\", \"\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}"
                    ]
                  }
                },
                {
                  "listen": "test",
                  "script": {
                    "id": "ae93eb95-d2af-48a7-8b7a-cfb22b661a26",
                    "type": "text/javascript",
                    "exec": [
                      "// Shared by every request's test script: save() writes the environment when it",
                      "// declares the key (an empty environment value hides a collection variable) and",
                      "// the collection; fill() only while empty; answer() is the JSON body or null.",
                      "",
                      "function save(key, value) {",
                      "  var text = value === undefined || value === null ? \"\" : String(value);",
                      "  if (pm.environment.has(key)) { pm.environment.set(key, text); }",
                      "  pm.collectionVariables.set(key, text);",
                      "}",
                      "function fill(key, value) {",
                      "  if (!pm.variables.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }",
                      "}",
                      "function answer() {",
                      "  try { return pm.response.json(); } catch (e) { return null; }",
                      "}",
                      "",
                      "var body = answer() || {};",
                      "save(\"oauthAuthUrl\", body.authorization_endpoint);",
                      "save(\"oauthTokenUrl\", body.token_endpoint);",
                      "save(\"oauthRegisterUrl\", body.registration_endpoint);",
                      "save(\"oauthRevokeUrl\", body.revocation_endpoint);",
                      "var scopes = body.scopes_supported || [];",
                      "save(\"oauthScope\", scopes.indexOf(\"offline_access\") === -1 ? \"igh\" : \"igh offline_access\");",
                      "pm.test(\"PKCE with S256\", function () { pm.expect(body.code_challenge_methods_supported).to.include(\"S256\"); });",
                      "pm.test(\"public clients (none)\", function () { pm.expect(body.token_endpoint_auth_methods_supported).to.include(\"none\"); });"
                    ]
                  }
                }
              ],
              "request": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{oauthIssuer}}/.well-known/oauth-authorization-server",
                  "host": [
                    "{{oauthIssuer}}"
                  ],
                  "path": [
                    ".well-known",
                    "oauth-authorization-server"
                  ]
                },
                "auth": {
                  "type": "noauth"
                },
                "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)"
              },
              "response": []
            },
            {
              "id": "b81ab003-ebf1-4630-828d-a5d1510ac492",
              "name": "3. Register Postman (dynamic client registration)",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "f5d082cf-ad1f-4174-8046-9036237efb31",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"201\");",
                      "pm.variables.set(\"igh.code\", \"\");",
                      "pm.variables.set(\"igh.kind\", \"oauth\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"\");",
                      "pm.variables.set(\"igh.what\", \"\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}"
                    ]
                  }
                },
                {
                  "listen": "test",
                  "script": {
                    "id": "eb2e968e-c0e4-47c5-8017-465370d86120",
                    "type": "text/javascript",
                    "exec": [
                      "// Shared by every request's test script: save() writes the environment when it",
                      "// declares the key (an empty environment value hides a collection variable) and",
                      "// the collection; fill() only while empty; answer() is the JSON body or null.",
                      "",
                      "function save(key, value) {",
                      "  var text = value === undefined || value === null ? \"\" : String(value);",
                      "  if (pm.environment.has(key)) { pm.environment.set(key, text); }",
                      "  pm.collectionVariables.set(key, text);",
                      "}",
                      "function fill(key, value) {",
                      "  if (!pm.variables.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }",
                      "}",
                      "function answer() {",
                      "  try { return pm.response.json(); } catch (e) { return null; }",
                      "}",
                      "",
                      "var body = answer() || {};",
                      "save(\"oauthClientId\", body.client_id);",
                      "pm.test(\"a public client\", function () { pm.expect(body.token_endpoint_auth_method).to.eql(\"none\"); });"
                    ]
                  }
                }
              ],
              "request": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json",
                    "type": "text"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"client_name\": \"Postman\",\n  \"redirect_uris\": [\n    \"https://oauth.pstmn.io/v1/callback\",\n    \"https://oauth.pstmn.io/v1/browser-callback\"\n  ],\n  \"token_endpoint_auth_method\": \"none\",\n  \"grant_types\": [\n    \"authorization_code\",\n    \"refresh_token\"\n  ],\n  \"response_types\": [\n    \"code\"\n  ]\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{oauthRegisterUrl}}",
                  "host": [
                    "{{oauthRegisterUrl}}"
                  ],
                  "path": []
                },
                "auth": {
                  "type": "noauth"
                },
                "description": "Registers Postman's two callbacks (in the app and with \"Authorize using browser\"): any https redirect 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)"
              },
              "response": []
            },
            {
              "id": "5d9362ba-bb0e-4b02-883d-2cb9f8c60702",
              "name": "4. Get a token with the OAuth 2.0 helper, then copy it to {{token}}",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "cde8bc47-a5d4-4264-88a5-103a056fd813",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"200\");",
                      "pm.variables.set(\"igh.code\", \"\");",
                      "pm.variables.set(\"igh.kind\", \"api\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"\");",
                      "pm.variables.set(\"igh.what\", \"\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}"
                    ]
                  }
                },
                {
                  "listen": "test",
                  "script": {
                    "id": "80e4cefb-d00d-446d-8eb0-2bdbd3925a7b",
                    "type": "text/javascript",
                    "exec": [
                      "// Shared by every request's test script: save() writes the environment when it",
                      "// declares the key (an empty environment value hides a collection variable) and",
                      "// the collection; fill() only while empty; answer() is the JSON body or null.",
                      "",
                      "function save(key, value) {",
                      "  var text = value === undefined || value === null ? \"\" : String(value);",
                      "  if (pm.environment.has(key)) { pm.environment.set(key, text); }",
                      "  pm.collectionVariables.set(key, text);",
                      "}",
                      "function fill(key, value) {",
                      "  if (!pm.variables.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }",
                      "}",
                      "function answer() {",
                      "  try { return pm.response.json(); } catch (e) { return null; }",
                      "}",
                      "",
                      "var body = answer();",
                      "if (body && body.integration) {",
                      "  var credential = body.integration.credential || {};",
                      "  console.log(\"House \" + body.house + \", integration \" + body.integration.name + \" (\" + body.integration.id + \"), credential \" + credential.kind + \".\");",
                      "  console.log(\"Settings: \" + JSON.stringify(body.integration.settings));",
                      "  fill(\"serial\", String(body.gateway || \"\").toLowerCase());",
                      "}",
                      "",
                      "var access = \"\";",
                      "try { var p = pm.request.auth && pm.request.auth.parameters ? pm.request.auth.parameters() : null; access = p ? (p.get(\"accessToken\") || \"\") : \"\"; } catch (e) { access = \"\"; }",
                      "if (!access) { access = String(pm.request.headers.get(\"Authorization\") || \"\").replace(/^Bearer\\s+/i, \"\"); }",
                      "if (pm.response.code === 200 && /^igh_/.test(access)) {",
                      "  save(\"token\", access);",
                      "  save(\"tokenKind\", \"oauth\");",
                      "  pm.test(\"{{token}} is the OAuth access token\", function () { pm.expect(access).to.match(/^igh_agent_/); });",
                      "} else {",
                      "  console.log(\"Couldn't read the token here: copy it from Manage Tokens into {{token}}.\");",
                      "}"
                    ]
                  }
                }
              ],
              "request": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/integration",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "integration"
                  ]
                },
                "description": "Authorization tab (inherited from the folder) → **Get New Access Token** → approve → **Use Token** → Send. The test copies the access token into `{{token}}`; copy the refresh token from Manage Tokens into the environment's `oauthRefreshToken` for 5 and 6. The access token lasts about an hour.\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)"
              },
              "response": []
            },
            {
              "id": "b153c36d-8203-45d6-8ac8-7e01f336739e",
              "name": "5. Refresh the token",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "7ae829fa-5e7c-4ba1-8945-c40c0ebcaadd",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"200\");",
                      "pm.variables.set(\"igh.code\", \"\");",
                      "pm.variables.set(\"igh.kind\", \"oauth\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"\");",
                      "pm.variables.set(\"igh.what\", \"\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "",
                      "if (!pm.variables.get(\"oauthRefreshToken\")) {",
                      "  throw new Error(\"Copy the refresh token from Manage Tokens into the environment's oauthRefreshToken first.\");",
                      "}"
                    ]
                  }
                },
                {
                  "listen": "test",
                  "script": {
                    "id": "87c1b001-5607-4681-892c-ade9c9168121",
                    "type": "text/javascript",
                    "exec": [
                      "// Shared by every request's test script: save() writes the environment when it",
                      "// declares the key (an empty environment value hides a collection variable) and",
                      "// the collection; fill() only while empty; answer() is the JSON body or null.",
                      "",
                      "function save(key, value) {",
                      "  var text = value === undefined || value === null ? \"\" : String(value);",
                      "  if (pm.environment.has(key)) { pm.environment.set(key, text); }",
                      "  pm.collectionVariables.set(key, text);",
                      "}",
                      "function fill(key, value) {",
                      "  if (!pm.variables.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }",
                      "}",
                      "function answer() {",
                      "  try { return pm.response.json(); } catch (e) { return null; }",
                      "}",
                      "",
                      "var body = answer() || {};",
                      "if (body.access_token) {",
                      "  save(\"token\", body.access_token);",
                      "  save(\"oauthRefreshToken\", body.refresh_token);",
                      "  pm.test(\"a new access token in {{token}}\", function () { pm.expect(body.access_token).to.match(/^igh_agent_/); });",
                      "}"
                    ]
                  }
                }
              ],
              "request": {
                "method": "POST",
                "header": [],
                "body": {
                  "mode": "urlencoded",
                  "urlencoded": [
                    {
                      "key": "grant_type",
                      "value": "refresh_token",
                      "type": "text"
                    },
                    {
                      "key": "refresh_token",
                      "value": "{{oauthRefreshToken}}",
                      "type": "text"
                    },
                    {
                      "key": "client_id",
                      "value": "{{oauthClientId}}",
                      "type": "text"
                    },
                    {
                      "key": "resource",
                      "value": "{{recordUrl}}",
                      "type": "text"
                    }
                  ]
                },
                "url": {
                  "raw": "{{oauthTokenUrl}}",
                  "host": [
                    "{{oauthTokenUrl}}"
                  ],
                  "path": []
                },
                "auth": {
                  "type": "noauth"
                },
                "description": "Refresh tokens rotate: the answer's refresh token replaces the old one. Sets `{{token}}` and `oauthRefreshToken`.\n\n[Authentication and Accept agent](https://developers.igh.app/authentication.html) · [Getting started](https://developers.igh.app/getting-started.html)"
              },
              "response": []
            },
            {
              "id": "d9a301ef-fe36-492f-8365-621452ea607d",
              "name": "6. Revoke (the refresh token)",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "4db15afc-c283-480b-8c31-308a48bae391",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"200\");",
                      "pm.variables.set(\"igh.code\", \"\");",
                      "pm.variables.set(\"igh.kind\", \"oauth\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"\");",
                      "pm.variables.set(\"igh.what\", \"\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}"
                    ]
                  }
                },
                {
                  "listen": "test",
                  "script": {
                    "id": "bd4aaba5-72c9-4f10-89e4-86976d6723b6",
                    "type": "text/javascript",
                    "exec": [
                      "// Shared by every request's test script: save() writes the environment when it",
                      "// declares the key (an empty environment value hides a collection variable) and",
                      "// the collection; fill() only while empty; answer() is the JSON body or null.",
                      "",
                      "function save(key, value) {",
                      "  var text = value === undefined || value === null ? \"\" : String(value);",
                      "  if (pm.environment.has(key)) { pm.environment.set(key, text); }",
                      "  pm.collectionVariables.set(key, text);",
                      "}",
                      "function fill(key, value) {",
                      "  if (!pm.variables.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }",
                      "}",
                      "function answer() {",
                      "  try { return pm.response.json(); } catch (e) { return null; }",
                      "}",
                      "",
                      "if (pm.response.code === 200) { save(\"oauthRefreshToken\", \"\"); console.log(\"Revoked: {{token}} answers 401 from now on.\"); }"
                    ]
                  }
                }
              ],
              "request": {
                "method": "POST",
                "header": [],
                "body": {
                  "mode": "urlencoded",
                  "urlencoded": [
                    {
                      "key": "token",
                      "value": "{{oauthRefreshToken}}",
                      "type": "text"
                    },
                    {
                      "key": "client_id",
                      "value": "{{oauthClientId}}",
                      "type": "text"
                    }
                  ]
                },
                "url": {
                  "raw": "{{oauthRevokeUrl}}",
                  "host": [
                    "{{oauthRevokeUrl}}"
                  ],
                  "path": []
                },
                "auth": {
                  "type": "noauth"
                },
                "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)"
              },
              "response": []
            }
          ],
          "auth": {
            "type": "oauth2",
            "oauth2": [
              {
                "key": "grant_type",
                "value": "authorization_code_with_pkce",
                "type": "string"
              },
              {
                "key": "authUrl",
                "value": "{{oauthAuthUrl}}",
                "type": "string"
              },
              {
                "key": "accessTokenUrl",
                "value": "{{oauthTokenUrl}}",
                "type": "string"
              },
              {
                "key": "clientId",
                "value": "{{oauthClientId}}",
                "type": "string"
              },
              {
                "key": "clientSecret",
                "value": "",
                "type": "string"
              },
              {
                "key": "redirect_uri",
                "value": "https://oauth.pstmn.io/v1/callback",
                "type": "string"
              },
              {
                "key": "useBrowser",
                "value": true,
                "type": "boolean"
              },
              {
                "key": "challengeAlgorithm",
                "value": "S256",
                "type": "string"
              },
              {
                "key": "scope",
                "value": "{{oauthScope}}",
                "type": "string"
              },
              {
                "key": "client_authentication",
                "value": "body",
                "type": "string"
              },
              {
                "key": "tokenName",
                "value": "IGH (OAuth)",
                "type": "string"
              },
              {
                "key": "addTokenTo",
                "value": "header",
                "type": "string"
              },
              {
                "key": "headerPrefix",
                "value": "Bearer",
                "type": "string"
              },
              {
                "key": "authRequestParams",
                "value": [
                  {
                    "key": "resource",
                    "value": "{{recordUrl}}",
                    "enabled": true,
                    "send_as": "request_url"
                  }
                ],
                "type": "any"
              },
              {
                "key": "tokenRequestParams",
                "value": [
                  {
                    "key": "resource",
                    "value": "{{recordUrl}}",
                    "enabled": true,
                    "send_as": "request_body"
                  }
                ],
                "type": "any"
              },
              {
                "key": "refreshRequestParams",
                "value": [
                  {
                    "key": "resource",
                    "value": "{{recordUrl}}",
                    "enabled": true,
                    "send_as": "request_body"
                  }
                ],
                "type": "any"
              }
            ]
          }
        },
        {
          "id": "99e54823-d71b-44fa-8dec-41955ea93412",
          "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.",
          "item": [
            {
              "id": "3ad89f07-4b85-4662-8f81-8e7181a0abd4",
              "name": "1. initialize (no credential)",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "4fadd7e4-1a10-47bd-8311-b7e6085e5875",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"200\");",
                      "pm.variables.set(\"igh.code\", \"\");",
                      "pm.variables.set(\"igh.kind\", \"mcp\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"\");",
                      "pm.variables.set(\"igh.what\", \"\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "",
                      "if (!pm.variables.get(\"recordUrl\")) {",
                      "  throw new Error(\"Set recordUrl in the environment: the integration's MCP address (https://mcp.igh.app/r/<id>), copied from its page in the IGH app.\");",
                      "}"
                    ]
                  }
                },
                {
                  "listen": "test",
                  "script": {
                    "id": "74c33d9c-36b9-4aa3-8dae-36962ed3af12",
                    "type": "text/javascript",
                    "exec": [
                      "// Shared by every request's test script: save() writes the environment when it",
                      "// declares the key (an empty environment value hides a collection variable) and",
                      "// the collection; fill() only while empty; answer() is the JSON body or null.",
                      "",
                      "function save(key, value) {",
                      "  var text = value === undefined || value === null ? \"\" : String(value);",
                      "  if (pm.environment.has(key)) { pm.environment.set(key, text); }",
                      "  pm.collectionVariables.set(key, text);",
                      "}",
                      "function fill(key, value) {",
                      "  if (!pm.variables.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }",
                      "}",
                      "function answer() {",
                      "  try { return pm.response.json(); } catch (e) { return null; }",
                      "}",
                      "",
                      "var body = answer() || {};",
                      "var result = body.result || {};",
                      "var content = result.structuredContent || {};",
                      "console.log(\"Server \" + JSON.stringify(result.serverInfo) + \", protocol \" + result.protocolVersion);",
                      "pm.test(\"initialized\", function () { pm.expect(result.protocolVersion).to.be.a(\"string\"); });"
                    ]
                  }
                }
              ],
              "request": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json",
                    "type": "text"
                  },
                  {
                    "key": "Accept",
                    "value": "application/json, text/event-stream",
                    "type": "text"
                  },
                  {
                    "key": "MCP-Protocol-Version",
                    "value": "2025-11-25",
                    "type": "text"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"jsonrpc\": \"2.0\",\n  \"id\": \"initialize\",\n  \"method\": \"initialize\",\n  \"params\": {\n    \"protocolVersion\": \"2025-11-25\",\n    \"capabilities\": {},\n    \"clientInfo\": {\n      \"name\": \"Postman\",\n      \"version\": \"1\"\n    }\n  }\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{recordUrl}}",
                  "host": [
                    "{{recordUrl}}"
                  ],
                  "path": []
                },
                "auth": {
                  "type": "noauth"
                },
                "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)"
              },
              "response": []
            },
            {
              "id": "a74b4ab8-0e14-4e4c-8b49-8f4ee3f0db78",
              "name": "2. tools/list (no credential)",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "50b2646c-7be2-43ed-89d4-b52f9693b3f0",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"200\");",
                      "pm.variables.set(\"igh.code\", \"\");",
                      "pm.variables.set(\"igh.kind\", \"mcp\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"\");",
                      "pm.variables.set(\"igh.what\", \"\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "",
                      "if (!pm.variables.get(\"recordUrl\")) {",
                      "  throw new Error(\"Set recordUrl in the environment: the integration's MCP address (https://mcp.igh.app/r/<id>), copied from its page in the IGH app.\");",
                      "}"
                    ]
                  }
                },
                {
                  "listen": "test",
                  "script": {
                    "id": "229a98b9-2299-401a-8c08-4865051d1f83",
                    "type": "text/javascript",
                    "exec": [
                      "// Shared by every request's test script: save() writes the environment when it",
                      "// declares the key (an empty environment value hides a collection variable) and",
                      "// the collection; fill() only while empty; answer() is the JSON body or null.",
                      "",
                      "function save(key, value) {",
                      "  var text = value === undefined || value === null ? \"\" : String(value);",
                      "  if (pm.environment.has(key)) { pm.environment.set(key, text); }",
                      "  pm.collectionVariables.set(key, text);",
                      "}",
                      "function fill(key, value) {",
                      "  if (!pm.variables.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }",
                      "}",
                      "function answer() {",
                      "  try { return pm.response.json(); } catch (e) { return null; }",
                      "}",
                      "",
                      "var body = answer() || {};",
                      "var result = body.result || {};",
                      "var content = result.structuredContent || {};",
                      "var names = (result.tools || []).map(function (t) { return t.name; });",
                      "console.log(\"Tools: \" + names.join(\", \"));",
                      "pm.test(\"about, request_access and check_access\", function () { pm.expect(names).to.include.members([\"about\", \"request_access\", \"check_access\"]); });"
                    ]
                  }
                }
              ],
              "request": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json",
                    "type": "text"
                  },
                  {
                    "key": "Accept",
                    "value": "application/json, text/event-stream",
                    "type": "text"
                  },
                  {
                    "key": "MCP-Protocol-Version",
                    "value": "2025-11-25",
                    "type": "text"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"jsonrpc\": \"2.0\",\n  \"id\": \"tools-list\",\n  \"method\": \"tools/list\",\n  \"params\": {}\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{recordUrl}}",
                  "host": [
                    "{{recordUrl}}"
                  ],
                  "path": []
                },
                "auth": {
                  "type": "noauth"
                },
                "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)"
              },
              "response": []
            },
            {
              "id": "eefe7db6-3902-42a6-8091-1b2bae99fc81",
              "name": "3. tools/call request_access",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "c4a3a6b5-1793-4fa5-839b-33522ddf005c",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"200\");",
                      "pm.variables.set(\"igh.code\", \"\");",
                      "pm.variables.set(\"igh.kind\", \"mcp\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"\");",
                      "pm.variables.set(\"igh.what\", \"\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "",
                      "if (!pm.variables.get(\"recordUrl\")) {",
                      "  throw new Error(\"Set recordUrl in the environment: the integration's MCP address (https://mcp.igh.app/r/<id>), copied from its page in the IGH app.\");",
                      "}"
                    ]
                  }
                },
                {
                  "listen": "test",
                  "script": {
                    "id": "78bc811a-0eeb-492d-85ba-03cc0dd0cbf5",
                    "type": "text/javascript",
                    "exec": [
                      "// Shared by every request's test script: save() writes the environment when it",
                      "// declares the key (an empty environment value hides a collection variable) and",
                      "// the collection; fill() only while empty; answer() is the JSON body or null.",
                      "",
                      "function save(key, value) {",
                      "  var text = value === undefined || value === null ? \"\" : String(value);",
                      "  if (pm.environment.has(key)) { pm.environment.set(key, text); }",
                      "  pm.collectionVariables.set(key, text);",
                      "}",
                      "function fill(key, value) {",
                      "  if (!pm.variables.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }",
                      "}",
                      "function answer() {",
                      "  try { return pm.response.json(); } catch (e) { return null; }",
                      "}",
                      "",
                      "var body = answer() || {};",
                      "var result = body.result || {};",
                      "var content = result.structuredContent || {};",
                      "if (content.code) {",
                      "  save(\"mcpCode\", content.code);",
                      "  save(\"pairInterval\", content.interval || 5);",
                      "  save(\"pairPolls\", 0);",
                      "  save(\"pairUserCode\", content.userCode);",
                      "  console.log(\"Matching code: \" + content.userCode + \". The administrator accepts it in the IGH app.\");",
                      "  pm.test(\"matching code to compare in the IGH app: \" + content.userCode, function () { pm.expect(content.userCode).to.be.a(\"string\"); });",
                      "} else {",
                      "  console.log(\"request_access: \" + JSON.stringify(content));",
                      "}"
                    ]
                  }
                }
              ],
              "request": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json",
                    "type": "text"
                  },
                  {
                    "key": "Accept",
                    "value": "application/json, text/event-stream",
                    "type": "text"
                  },
                  {
                    "key": "MCP-Protocol-Version",
                    "value": "2025-11-25",
                    "type": "text"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"jsonrpc\": \"2.0\",\n  \"id\": \"request-access\",\n  \"method\": \"tools/call\",\n  \"params\": {\n    \"name\": \"request_access\",\n    \"arguments\": {\n      \"name\": \"Postman (MCP)\",\n      \"platform\": \"Postman\"\n    }\n  }\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{recordUrl}}",
                  "host": [
                    "{{recordUrl}}"
                  ],
                  "path": []
                },
                "auth": {
                  "type": "noauth"
                },
                "description": "Shows the matching code (console, test name). 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)"
              },
              "response": []
            },
            {
              "id": "d871c9b7-d526-43cf-85cc-4822ec82c24d",
              "name": "4. tools/call check_access (repeats in the Runner)",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "1cc2d426-cbea-4f0e-8c65-4ef31f90750d",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"200\");",
                      "pm.variables.set(\"igh.code\", \"\");",
                      "pm.variables.set(\"igh.kind\", \"mcp\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"\");",
                      "pm.variables.set(\"igh.what\", \"\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "",
                      "if (!pm.variables.get(\"recordUrl\")) {",
                      "  throw new Error(\"Set recordUrl in the environment: the integration's MCP address (https://mcp.igh.app/r/<id>), copied from its page in the IGH app.\");",
                      "}",
                      "",
                      "if (!pm.variables.get(\"mcpCode\")) { throw new Error(\"Send 3 (request_access) first.\"); }",
                      "if (Number(pm.variables.get(\"pairPolls\") || 0) > 0) { setTimeout(function () {}, Number(pm.variables.get(\"pairInterval\") || 5) * 1000); }"
                    ]
                  }
                },
                {
                  "listen": "test",
                  "script": {
                    "id": "8e01204d-afb7-418d-895a-c89bc4a93654",
                    "type": "text/javascript",
                    "exec": [
                      "// Shared by every request's test script: save() writes the environment when it",
                      "// declares the key (an empty environment value hides a collection variable) and",
                      "// the collection; fill() only while empty; answer() is the JSON body or null.",
                      "",
                      "function save(key, value) {",
                      "  var text = value === undefined || value === null ? \"\" : String(value);",
                      "  if (pm.environment.has(key)) { pm.environment.set(key, text); }",
                      "  pm.collectionVariables.set(key, text);",
                      "}",
                      "function fill(key, value) {",
                      "  if (!pm.variables.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }",
                      "}",
                      "function answer() {",
                      "  try { return pm.response.json(); } catch (e) { return null; }",
                      "}",
                      "",
                      "var body = answer() || {};",
                      "var result = body.result || {};",
                      "var content = result.structuredContent || {};",
                      "var polls = Number(pm.variables.get(\"pairPolls\") || 0) + 1;",
                      "save(\"pairPolls\", polls);",
                      "if (content.status === \"accepted\") {",
                      "  save(\"token\", content.credential);",
                      "  save(\"tokenKind\", content.kind);",
                      "  save(\"mcpCode\", \"\");",
                      "  console.log(\"Configure a client: \" + content.command);",
                      "  pm.test(\"accepted: {{token}} is set (\" + content.kind + \")\", function () { pm.expect(content.credential).to.match(/^igh_(agent|session)_/); });",
                      "} else if (content.status === \"pending\") {",
                      "  if (content.slowDown) { save(\"pairInterval\", Number(pm.variables.get(\"pairInterval\") || 5) + 5); }",
                      "  console.log(\"Waiting for an administrator (poll \" + polls + \"). Matching code: \" + pm.variables.get(\"pairUserCode\"));",
                      "  if (polls < 150) { pm.execution.setNextRequest(pm.info.requestId); }",
                      "} else {",
                      "  console.log(\"check_access: \" + JSON.stringify(content));",
                      "}"
                    ]
                  }
                }
              ],
              "request": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json",
                    "type": "text"
                  },
                  {
                    "key": "Accept",
                    "value": "application/json, text/event-stream",
                    "type": "text"
                  },
                  {
                    "key": "MCP-Protocol-Version",
                    "value": "2025-11-25",
                    "type": "text"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\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}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{recordUrl}}",
                  "host": [
                    "{{recordUrl}}"
                  ],
                  "path": []
                },
                "auth": {
                  "type": "noauth"
                },
                "description": "`pending` until an administrator answers; `accepted` carries the credential (set as `{{token}}`) and the `claude mcp add` command that configures a client. In the Collection Runner it repeats itself every `pairInterval` seconds.\n\n[Authentication and Accept agent](https://developers.igh.app/authentication.html) · [Getting started](https://developers.igh.app/getting-started.html)"
              },
              "response": []
            },
            {
              "id": "939016b4-604d-4248-813c-0f5f93b58d11",
              "name": "5. tools/list (with {{token}})",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "2f0cf043-cc12-4bb3-810b-565213229072",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"200\");",
                      "pm.variables.set(\"igh.code\", \"\");",
                      "pm.variables.set(\"igh.kind\", \"mcp\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"\");",
                      "pm.variables.set(\"igh.what\", \"\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "",
                      "if (!pm.variables.get(\"recordUrl\")) {",
                      "  throw new Error(\"Set recordUrl in the environment: the integration's MCP address (https://mcp.igh.app/r/<id>), copied from its page in the IGH app.\");",
                      "}"
                    ]
                  }
                },
                {
                  "listen": "test",
                  "script": {
                    "id": "69f361c6-1549-42af-868f-ce00b0231604",
                    "type": "text/javascript",
                    "exec": [
                      "// Shared by every request's test script: save() writes the environment when it",
                      "// declares the key (an empty environment value hides a collection variable) and",
                      "// the collection; fill() only while empty; answer() is the JSON body or null.",
                      "",
                      "function save(key, value) {",
                      "  var text = value === undefined || value === null ? \"\" : String(value);",
                      "  if (pm.environment.has(key)) { pm.environment.set(key, text); }",
                      "  pm.collectionVariables.set(key, text);",
                      "}",
                      "function fill(key, value) {",
                      "  if (!pm.variables.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }",
                      "}",
                      "function answer() {",
                      "  try { return pm.response.json(); } catch (e) { return null; }",
                      "}",
                      "",
                      "var body = answer() || {};",
                      "var result = body.result || {};",
                      "var content = result.structuredContent || {};",
                      "var names = (result.tools || []).map(function (t) { return t.name; });",
                      "console.log(\"Tools: \" + names.join(\", \"));",
                      "pm.test(\"describe_home is offered\", function () { pm.expect(names).to.include(\"describe_home\"); });"
                    ]
                  }
                }
              ],
              "request": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json",
                    "type": "text"
                  },
                  {
                    "key": "Accept",
                    "value": "application/json, text/event-stream",
                    "type": "text"
                  },
                  {
                    "key": "MCP-Protocol-Version",
                    "value": "2025-11-25",
                    "type": "text"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"jsonrpc\": \"2.0\",\n  \"id\": \"tools-list-token\",\n  \"method\": \"tools/list\",\n  \"params\": {}\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{recordUrl}}",
                  "host": [
                    "{{recordUrl}}"
                  ],
                  "path": []
                },
                "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)"
              },
              "response": []
            },
            {
              "id": "c09130f5-e53f-47e6-8ddc-ac9e9dd3b99c",
              "name": "6. tools/call describe_home",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "09637493-de64-406c-8333-95d4b0b1ce75",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"200\");",
                      "pm.variables.set(\"igh.code\", \"\");",
                      "pm.variables.set(\"igh.kind\", \"mcp\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"\");",
                      "pm.variables.set(\"igh.what\", \"\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "",
                      "if (!pm.variables.get(\"recordUrl\")) {",
                      "  throw new Error(\"Set recordUrl in the environment: the integration's MCP address (https://mcp.igh.app/r/<id>), copied from its page in the IGH app.\");",
                      "}"
                    ]
                  }
                },
                {
                  "listen": "test",
                  "script": {
                    "id": "60afed17-78ec-41e3-8bdd-4c91b3baf88d",
                    "type": "text/javascript",
                    "exec": [
                      "// Shared by every request's test script: save() writes the environment when it",
                      "// declares the key (an empty environment value hides a collection variable) and",
                      "// the collection; fill() only while empty; answer() is the JSON body or null.",
                      "",
                      "function save(key, value) {",
                      "  var text = value === undefined || value === null ? \"\" : String(value);",
                      "  if (pm.environment.has(key)) { pm.environment.set(key, text); }",
                      "  pm.collectionVariables.set(key, text);",
                      "}",
                      "function fill(key, value) {",
                      "  if (!pm.variables.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }",
                      "}",
                      "function answer() {",
                      "  try { return pm.response.json(); } catch (e) { return null; }",
                      "}",
                      "",
                      "var body = answer() || {};",
                      "var result = body.result || {};",
                      "var content = result.structuredContent || {};",
                      "pm.test(\"not an error\", function () { pm.expect(result.isError).to.not.eql(true); });",
                      "console.log(\"House: \" + (content.house || \"?\"));"
                    ]
                  }
                }
              ],
              "request": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json",
                    "type": "text"
                  },
                  {
                    "key": "Accept",
                    "value": "application/json, text/event-stream",
                    "type": "text"
                  },
                  {
                    "key": "MCP-Protocol-Version",
                    "value": "2025-11-25",
                    "type": "text"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"jsonrpc\": \"2.0\",\n  \"id\": \"describe-home\",\n  \"method\": \"tools/call\",\n  \"params\": {\n    \"name\": \"describe_home\",\n    \"arguments\": {}\n  }\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{recordUrl}}",
                  "host": [
                    "{{recordUrl}}"
                  ],
                  "path": []
                },
                "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)"
              },
              "response": []
            }
          ]
        },
        {
          "id": "c5764a1a-71a7-4920-8753-7258ea3b9587",
          "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.",
          "item": [
            {
              "id": "964f9dba-21ca-4c59-801b-c222bfcc5f86",
              "name": "No token → 401",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "a5cdc6ac-8a36-4dd5-8332-156a9aee35b8",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"401\");",
                      "pm.variables.set(\"igh.code\", \"unauthorized\");",
                      "pm.variables.set(\"igh.kind\", \"api\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"\");",
                      "pm.variables.set(\"igh.what\", \"\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}"
                    ]
                  }
                },
                {
                  "listen": "test",
                  "script": {
                    "id": "11ff6c0c-0d43-48ec-821e-e86dee4bc794",
                    "type": "text/javascript",
                    "exec": [
                      "// Shared by every request's test script: save() writes the environment when it",
                      "// declares the key (an empty environment value hides a collection variable) and",
                      "// the collection; fill() only while empty; answer() is the JSON body or null.",
                      "",
                      "function save(key, value) {",
                      "  var text = value === undefined || value === null ? \"\" : String(value);",
                      "  if (pm.environment.has(key)) { pm.environment.set(key, text); }",
                      "  pm.collectionVariables.set(key, text);",
                      "}",
                      "function fill(key, value) {",
                      "  if (!pm.variables.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }",
                      "}",
                      "function answer() {",
                      "  try { return pm.response.json(); } catch (e) { return null; }",
                      "}",
                      "",
                      "var challenge = pm.response.headers.get(\"WWW-Authenticate\");",
                      "console.log(\"WWW-Authenticate: \" + (challenge || \"(none)\"));"
                    ]
                  }
                }
              ],
              "request": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/house",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "house"
                  ]
                },
                "auth": {
                  "type": "noauth"
                },
                "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)"
              },
              "response": []
            },
            {
              "id": "2e5d02f8-3085-45af-87f5-9c1180af3fbd",
              "name": "No token on an MCP tool → 401 with WWW-Authenticate (cloud only)",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "5a823d93-ac32-42d5-8342-e09dffb8e083",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"401\");",
                      "pm.variables.set(\"igh.code\", \"\");",
                      "pm.variables.set(\"igh.kind\", \"plain\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"\");",
                      "pm.variables.set(\"igh.what\", \"\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "",
                      "if (!pm.variables.get(\"recordUrl\")) {",
                      "  throw new Error(\"Set recordUrl in the environment: the integration's MCP address (https://mcp.igh.app/r/<id>), copied from its page in the IGH app.\");",
                      "}"
                    ]
                  }
                },
                {
                  "listen": "test",
                  "script": {
                    "id": "18670791-c2d4-4940-86cf-149c48da0ab4",
                    "type": "text/javascript",
                    "exec": [
                      "// Shared by every request's test script: save() writes the environment when it",
                      "// declares the key (an empty environment value hides a collection variable) and",
                      "// the collection; fill() only while empty; answer() is the JSON body or null.",
                      "",
                      "function save(key, value) {",
                      "  var text = value === undefined || value === null ? \"\" : String(value);",
                      "  if (pm.environment.has(key)) { pm.environment.set(key, text); }",
                      "  pm.collectionVariables.set(key, text);",
                      "}",
                      "function fill(key, value) {",
                      "  if (!pm.variables.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }",
                      "}",
                      "function answer() {",
                      "  try { return pm.response.json(); } catch (e) { return null; }",
                      "}",
                      "",
                      "var challenge = pm.response.headers.get(\"WWW-Authenticate\");",
                      "console.log(\"WWW-Authenticate: \" + (challenge || \"(none)\"));",
                      "",
                      "pm.test(\"the challenge names the resource metadata\", function () { pm.expect(String(challenge)).to.include(\"resource_metadata=\"); });"
                    ]
                  }
                }
              ],
              "request": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json",
                    "type": "text"
                  },
                  {
                    "key": "Accept",
                    "value": "application/json, text/event-stream",
                    "type": "text"
                  },
                  {
                    "key": "MCP-Protocol-Version",
                    "value": "2025-11-25",
                    "type": "text"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"jsonrpc\": \"2.0\",\n  \"id\": \"call\",\n  \"method\": \"tools/call\",\n  \"params\": {\n    \"name\": \"describe_home\",\n    \"arguments\": {}\n  }\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{recordUrl}}",
                  "host": [
                    "{{recordUrl}}"
                  ],
                  "path": []
                },
                "auth": {
                  "type": "noauth"
                },
                "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)"
              },
              "response": []
            },
            {
              "id": "a86a3499-eb67-4ff8-8999-751a04c1a50e",
              "name": "Garbage token → 401",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "d21c608b-227d-47fb-8817-2f4bbefdffbc",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"401\");",
                      "pm.variables.set(\"igh.code\", \"unauthorized\");",
                      "pm.variables.set(\"igh.kind\", \"api\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"\");",
                      "pm.variables.set(\"igh.what\", \"\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}"
                    ]
                  }
                },
                {
                  "listen": "test",
                  "script": {
                    "id": "2bfd9d3c-d9b6-49e2-89b2-cb660c6e66a2",
                    "type": "text/javascript",
                    "exec": [
                      "// Shared by every request's test script: save() writes the environment when it",
                      "// declares the key (an empty environment value hides a collection variable) and",
                      "// the collection; fill() only while empty; answer() is the JSON body or null.",
                      "",
                      "function save(key, value) {",
                      "  var text = value === undefined || value === null ? \"\" : String(value);",
                      "  if (pm.environment.has(key)) { pm.environment.set(key, text); }",
                      "  pm.collectionVariables.set(key, text);",
                      "}",
                      "function fill(key, value) {",
                      "  if (!pm.variables.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }",
                      "}",
                      "function answer() {",
                      "  try { return pm.response.json(); } catch (e) { return null; }",
                      "}",
                      "",
                      "var challenge = pm.response.headers.get(\"WWW-Authenticate\");",
                      "console.log(\"WWW-Authenticate: \" + (challenge || \"(none)\"));"
                    ]
                  }
                }
              ],
              "request": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/house",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "house"
                  ]
                },
                "auth": {
                  "type": "bearer",
                  "bearer": [
                    {
                      "key": "token",
                      "value": "postman-garbage",
                      "type": "string"
                    }
                  ]
                },
                "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)"
              },
              "response": []
            },
            {
              "id": "a20e5e7f-0cd7-48cc-8463-0185bf67d3ca",
              "name": "Revoked token → 401",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "b1a06de1-8ebb-4d3c-8d93-f9c15180dc34",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"401\");",
                      "pm.variables.set(\"igh.code\", \"\");",
                      "pm.variables.set(\"igh.kind\", \"api\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"revokedToken\");",
                      "pm.variables.set(\"igh.what\", \"a revoked credential\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}"
                    ]
                  }
                },
                {
                  "listen": "test",
                  "script": {
                    "id": "27cd6b4f-4aa1-4eb2-8ee6-6ac665a696d4",
                    "type": "text/javascript",
                    "exec": [
                      "// Shared by every request's test script: save() writes the environment when it",
                      "// declares the key (an empty environment value hides a collection variable) and",
                      "// the collection; fill() only while empty; answer() is the JSON body or null.",
                      "",
                      "function save(key, value) {",
                      "  var text = value === undefined || value === null ? \"\" : String(value);",
                      "  if (pm.environment.has(key)) { pm.environment.set(key, text); }",
                      "  pm.collectionVariables.set(key, text);",
                      "}",
                      "function fill(key, value) {",
                      "  if (!pm.variables.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }",
                      "}",
                      "function answer() {",
                      "  try { return pm.response.json(); } catch (e) { return null; }",
                      "}",
                      "",
                      "var challenge = pm.response.headers.get(\"WWW-Authenticate\");",
                      "console.log(\"WWW-Authenticate: \" + (challenge || \"(none)\"));"
                    ]
                  }
                }
              ],
              "request": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/house",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "house"
                  ]
                },
                "auth": {
                  "type": "bearer",
                  "bearer": [
                    {
                      "key": "token",
                      "value": "{{revokedToken}}",
                      "type": "string"
                    }
                  ]
                },
                "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)"
              },
              "response": []
            },
            {
              "id": "c63f3a07-bbd9-4262-8716-6d25df934dfa",
              "name": "Agent access off → 403 agent_access_disabled",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "f844607c-f68c-4d7c-8793-b16307c859fc",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"403\");",
                      "pm.variables.set(\"igh.code\", \"agent_access_disabled\");",
                      "pm.variables.set(\"igh.kind\", \"api\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"\");",
                      "pm.variables.set(\"igh.what\", \"\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}"
                    ]
                  }
                }
              ],
              "request": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/integration",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "integration"
                  ]
                },
                "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)"
              },
              "response": []
            },
            {
              "id": "e77915b4-214d-46eb-8141-61fc8ea6af29",
              "name": "Cloud access off → 403 cloud_access_disabled (cloud only)",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "2fbcbabd-853a-4b92-8de7-ac06fccb531c",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"403\");",
                      "pm.variables.set(\"igh.code\", \"cloud_access_disabled\");",
                      "pm.variables.set(\"igh.kind\", \"api\");",
                      "pm.variables.set(\"igh.only\", \"cloud\");",
                      "pm.variables.set(\"igh.needs\", \"\");",
                      "pm.variables.set(\"igh.what\", \"\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}"
                    ]
                  }
                }
              ],
              "request": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/integration",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "integration"
                  ]
                },
                "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)"
              },
              "response": []
            },
            {
              "id": "db5d2610-3760-4b35-84bb-86b2dce18072",
              "name": "Another integration's token on /r/{id} → 403 (cloud only)",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "269bc9f7-c4ba-4f23-81ba-7430a6f5ff34",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"403\");",
                      "pm.variables.set(\"igh.code\", \"\");",
                      "pm.variables.set(\"igh.kind\", \"plain\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"otherRecordToken\");",
                      "pm.variables.set(\"igh.what\", \"another integration's credential\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "",
                      "if (!pm.variables.get(\"recordUrl\")) {",
                      "  throw new Error(\"Set recordUrl in the environment: the integration's MCP address (https://mcp.igh.app/r/<id>), copied from its page in the IGH app.\");",
                      "}"
                    ]
                  }
                }
              ],
              "request": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json",
                    "type": "text"
                  },
                  {
                    "key": "Accept",
                    "value": "application/json, text/event-stream",
                    "type": "text"
                  },
                  {
                    "key": "MCP-Protocol-Version",
                    "value": "2025-11-25",
                    "type": "text"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"jsonrpc\": \"2.0\",\n  \"id\": \"initialize\",\n  \"method\": \"initialize\",\n  \"params\": {\n    \"protocolVersion\": \"2025-11-25\",\n    \"capabilities\": {},\n    \"clientInfo\": {\n      \"name\": \"Postman\",\n      \"version\": \"1\"\n    }\n  }\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{recordUrl}}",
                  "host": [
                    "{{recordUrl}}"
                  ],
                  "path": []
                },
                "auth": {
                  "type": "bearer",
                  "bearer": [
                    {
                      "key": "token",
                      "value": "{{otherRecordToken}}",
                      "type": "string"
                    }
                  ]
                },
                "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)"
              },
              "response": []
            }
          ]
        }
      ]
    },
    {
      "id": "dacc78d1-e905-4dac-8515-712556624e36",
      "name": "Getting started",
      "description": "Run these first: who the credential is, the house (it fills the id variables), the state now.",
      "item": [
        {
          "id": "f9dd2a8e-d507-4254-894b-878a565893dd",
          "name": "About",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "b4eb4f70-d119-423a-81fe-ee9fb48b1847",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"\");",
                  "pm.variables.set(\"igh.what\", \"\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/about",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "about"
              ]
            },
            "auth": {
              "type": "noauth"
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "a77529a7-8024-43a2-8dbd-1935a7605c71",
          "name": "Integration (who am I)",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "1b6414ce-23f3-4d39-8c0b-15896f193532",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"\");",
                  "pm.variables.set(\"igh.what\", \"\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "id": "a8950d89-f76a-4d19-8212-e09c5c710dbd",
                "type": "text/javascript",
                "exec": [
                  "// Shared by every request's test script: save() writes the environment when it",
                  "// declares the key (an empty environment value hides a collection variable) and",
                  "// the collection; fill() only while empty; answer() is the JSON body or null.",
                  "",
                  "function save(key, value) {",
                  "  var text = value === undefined || value === null ? \"\" : String(value);",
                  "  if (pm.environment.has(key)) { pm.environment.set(key, text); }",
                  "  pm.collectionVariables.set(key, text);",
                  "}",
                  "function fill(key, value) {",
                  "  if (!pm.variables.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }",
                  "}",
                  "function answer() {",
                  "  try { return pm.response.json(); } catch (e) { return null; }",
                  "}",
                  "",
                  "var body = answer();",
                  "if (body && body.integration) {",
                  "  var credential = body.integration.credential || {};",
                  "  console.log(\"House \" + body.house + \", integration \" + body.integration.name + \" (\" + body.integration.id + \"), credential \" + credential.kind + \".\");",
                  "  console.log(\"Settings: \" + JSON.stringify(body.integration.settings));",
                  "  fill(\"serial\", String(body.gateway || \"\").toLowerCase());",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/integration",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "integration"
              ]
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "2c6d79be-3fe3-4ed0-8924-b0b59d4d497a",
          "name": "House",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "1bd7ec5c-3b37-4762-8143-5d422db54ab2",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"\");",
                  "pm.variables.set(\"igh.what\", \"\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "id": "4f8c762b-9aa6-4679-84b1-80159fa46104",
                "type": "text/javascript",
                "exec": [
                  "// Shared by every request's test script: save() writes the environment when it",
                  "// declares the key (an empty environment value hides a collection variable) and",
                  "// the collection; fill() only while empty; answer() is the JSON body or null.",
                  "",
                  "function save(key, value) {",
                  "  var text = value === undefined || value === null ? \"\" : String(value);",
                  "  if (pm.environment.has(key)) { pm.environment.set(key, text); }",
                  "  pm.collectionVariables.set(key, text);",
                  "}",
                  "function fill(key, value) {",
                  "  if (!pm.variables.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }",
                  "}",
                  "function answer() {",
                  "  try { return pm.response.json(); } catch (e) { return null; }",
                  "}",
                  "",
                  "var house = answer();",
                  "if (house && house.floors) {",
                  "  var units = [];",
                  "  var rooms = [];",
                  "  house.floors.forEach(function (floor) { floor.rooms.forEach(function (room) { rooms.push(room); }); });",
                  "  (house.rooms || []).forEach(function (room) { rooms.push(room); });",
                  "  rooms.forEach(function (room) { (room.units || []).forEach(function (unit) { units.push(unit); }); });",
                  "  (house.units || []).forEach(function (unit) { units.push(unit); });",
                  "  var devices = [];",
                  "  units.forEach(function (unit) {",
                  "    devices.push(unit);",
                  "    (unit.gangs || []).forEach(function (gang) { devices.push(gang); });",
                  "  });",
                  "  var has = function (device, op) { return (device.operations || []).indexOf(op) !== -1; };",
                  "  var first = function (test) { for (var i = 0; i < devices.length; i += 1) { if (test(devices[i])) { return devices[i]; } } return null; };",
                  "  var light = first(function (d) { return has(d, \"set_power\") && !has(d, \"set_temp\") && !has(d, \"key\") && (!d.class || d.class.indexOf(\"light\") === 0); });",
                  "  var pick = {",
                  "    lightId: light,",
                  "    dimmerId: first(function (d) { return has(d, \"set_brightness\"); }),",
                  "    shutterId: first(function (d) { return has(d, \"set_position\"); }),",
                  "    acId: first(function (d) { return has(d, \"set_ac_mode\"); }),",
                  "    irId: first(function (d) { return has(d, \"key\"); }),",
                  "    lockId: first(function (d) { return has(d, \"unlock\"); }),",
                  "    gateId: first(function (d) { return d.class === \"gate\"; })",
                  "  };",
                  "  Object.keys(pick).forEach(function (key) { if (pick[key]) { fill(key, pick[key].id); } });",
                  "  var device = light || first(function (d) { return (d.operations || []).length > 0; }) || devices[0];",
                  "  if (device) {",
                  "    fill(\"deviceId\", device.id);",
                  "    fill(\"deviceName\", device.name);",
                  "  }",
                  "  if (pm.variables.get(\"deviceId\")) { save(\"unitRef\", pm.variables.get(\"deviceId\").split(\":\")[2]); }",
                  "  if (rooms[0]) { fill(\"roomId\", rooms[0].id); fill(\"roomName\", rooms[0].name); }",
                  "  if (house.scenes && house.scenes[0]) { fill(\"sceneId\", house.scenes[0].id); }",
                  "  console.log(\"House \" + house.house + \" (gateway \" + house.gateway + \"): \" + devices.length + \" devices, \" + rooms.length + \" rooms, \" + (house.scenes || []).length + \" scenes.\");",
                  "  [\"deviceId\", \"lightId\", \"dimmerId\", \"shutterId\", \"acId\", \"irId\", \"lockId\", \"gateId\", \"roomId\", \"sceneId\"].forEach(function (key) {",
                  "    console.log(key + \" = \" + (pm.variables.get(key) || \"(none in this house)\"));",
                  "  });",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/house",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "house"
              ]
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "3bfc4c93-4ddb-46a5-8a2c-ef6b3228adb1",
          "name": "State",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "8493c23d-7dd8-47f0-832e-61ef3676f1a5",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"\");",
                  "pm.variables.set(\"igh.what\", \"\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/state",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "state"
              ]
            },
            "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)"
          },
          "response": []
        }
      ]
    },
    {
      "id": "db653f42-0c87-4e8c-88be-7459d877e3fd",
      "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.",
      "item": [
        {
          "id": "6ce25244-ac6b-467e-8c1f-a4cd4cfdac61",
          "name": "Device",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "3f887cb5-bf44-4c9e-86e3-333ebb5a2c09",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"deviceId\");",
                  "pm.variables.set(\"igh.what\", \"device\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/devices/{{deviceId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "devices",
                "{{deviceId}}"
              ]
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "98e38cc4-4db7-4500-88e1-d107a273899f",
          "name": "Lights",
          "description": "**Switches a real device in the home.** Send it only when that is fine.",
          "item": [
            {
              "id": "ad5020f2-2065-462f-80fb-145eb179c960",
              "name": "Light on [switches a device]",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "e246209e-719d-4649-851c-94a0b71619c9",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"200\");",
                      "pm.variables.set(\"igh.code\", \"\");",
                      "pm.variables.set(\"igh.kind\", \"api\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"lightId\");",
                      "pm.variables.set(\"igh.what\", \"light\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}"
                    ]
                  }
                }
              ],
              "request": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json",
                    "type": "text"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"op\": \"set_power\",\n  \"args\": {\n    \"on\": true\n  }\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{baseUrl}}/devices/{{lightId}}/control",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "devices",
                    "{{lightId}}",
                    "control"
                  ]
                },
                "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)"
              },
              "response": []
            },
            {
              "id": "4a758e36-8215-4a7f-8795-92504a90bf10",
              "name": "Light off [switches a device]",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "865578b6-7a1a-41e9-825c-c9b635d50034",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"200\");",
                      "pm.variables.set(\"igh.code\", \"\");",
                      "pm.variables.set(\"igh.kind\", \"api\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"lightId\");",
                      "pm.variables.set(\"igh.what\", \"light\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}"
                    ]
                  }
                }
              ],
              "request": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json",
                    "type": "text"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"op\": \"set_power\",\n  \"args\": {\n    \"on\": false\n  }\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{baseUrl}}/devices/{{lightId}}/control",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "devices",
                    "{{lightId}}",
                    "control"
                  ]
                },
                "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)"
              },
              "response": []
            },
            {
              "id": "1b840ca0-5926-44ea-86fa-650694ecdb8b",
              "name": "Light level 40% [switches a device]",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "bc7ec968-c386-4113-8daa-fb78848e4d1e",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"200\");",
                      "pm.variables.set(\"igh.code\", \"\");",
                      "pm.variables.set(\"igh.kind\", \"api\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"dimmerId\");",
                      "pm.variables.set(\"igh.what\", \"dimmer\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}"
                    ]
                  }
                }
              ],
              "request": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json",
                    "type": "text"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"op\": \"set_brightness\",\n  \"args\": {\n    \"bri\": 40\n  }\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{baseUrl}}/devices/{{dimmerId}}/control",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "devices",
                    "{{dimmerId}}",
                    "control"
                  ]
                },
                "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)"
              },
              "response": []
            }
          ]
        },
        {
          "id": "272ebfef-b765-40d4-8513-2425c9b2221c",
          "name": "Shutters",
          "description": "**Switches a real device in the home.** Send it only when that is fine.",
          "item": [
            {
              "id": "954792be-3271-4c4b-8886-dd5d5ebc7783",
              "name": "Shutter to 50% [switches a device]",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "d5f47ac9-719d-48d3-8091-5f3f5ad3f622",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"200\");",
                      "pm.variables.set(\"igh.code\", \"\");",
                      "pm.variables.set(\"igh.kind\", \"api\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"shutterId\");",
                      "pm.variables.set(\"igh.what\", \"shutter\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}"
                    ]
                  }
                }
              ],
              "request": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json",
                    "type": "text"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"op\": \"set_position\",\n  \"args\": {\n    \"pos\": 50\n  }\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{baseUrl}}/devices/{{shutterId}}/control",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "devices",
                    "{{shutterId}}",
                    "control"
                  ]
                },
                "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)"
              },
              "response": []
            },
            {
              "id": "b0f7929f-89de-499c-8db3-86d2f7146421",
              "name": "Shutter close [switches a device]",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "1571316b-636b-4330-8e77-3a05121c49ff",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"200\");",
                      "pm.variables.set(\"igh.code\", \"\");",
                      "pm.variables.set(\"igh.kind\", \"api\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"shutterId\");",
                      "pm.variables.set(\"igh.what\", \"shutter\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}"
                    ]
                  }
                }
              ],
              "request": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json",
                    "type": "text"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"op\": \"close\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{baseUrl}}/devices/{{shutterId}}/control",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "devices",
                    "{{shutterId}}",
                    "control"
                  ]
                },
                "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)"
              },
              "response": []
            },
            {
              "id": "0f1b1495-3429-4d97-813b-e0c50755c094",
              "name": "Shutter stop [switches a device]",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "b7beacf4-354c-40b7-8d1e-90d8fd5f94fe",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"200\");",
                      "pm.variables.set(\"igh.code\", \"\");",
                      "pm.variables.set(\"igh.kind\", \"api\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"shutterId\");",
                      "pm.variables.set(\"igh.what\", \"shutter\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}"
                    ]
                  }
                }
              ],
              "request": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json",
                    "type": "text"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"op\": \"stop\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{baseUrl}}/devices/{{shutterId}}/control",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "devices",
                    "{{shutterId}}",
                    "control"
                  ]
                },
                "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)"
              },
              "response": []
            }
          ]
        },
        {
          "id": "23298b18-bbe3-4758-8a7e-adec04017323",
          "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.",
          "item": [
            {
              "id": "20377322-5deb-457b-817f-f84b0dd0cb6f",
              "name": "AC mode cool [switches a device]",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "87cee2c5-c238-4376-8f07-3cc2c1ccbbab",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"200\");",
                      "pm.variables.set(\"igh.code\", \"\");",
                      "pm.variables.set(\"igh.kind\", \"api\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"acId\");",
                      "pm.variables.set(\"igh.what\", \"air conditioner\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}"
                    ]
                  }
                }
              ],
              "request": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json",
                    "type": "text"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"op\": \"set_ac_mode\",\n  \"args\": {\n    \"mode\": \"cool\"\n  }\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{baseUrl}}/devices/{{acId}}/control",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "devices",
                    "{{acId}}",
                    "control"
                  ]
                },
                "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)"
              },
              "response": []
            },
            {
              "id": "774e7233-7cb9-44eb-805b-08382b98e8b7",
              "name": "AC setpoint 24 °C [switches a device]",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "78b69206-476d-4827-8ce6-fd1c3ce7e6f4",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"200\");",
                      "pm.variables.set(\"igh.code\", \"\");",
                      "pm.variables.set(\"igh.kind\", \"api\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"acId\");",
                      "pm.variables.set(\"igh.what\", \"air conditioner\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}"
                    ]
                  }
                }
              ],
              "request": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json",
                    "type": "text"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"op\": \"set_temp\",\n  \"args\": {\n    \"temp\": 24\n  }\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{baseUrl}}/devices/{{acId}}/control",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "devices",
                    "{{acId}}",
                    "control"
                  ]
                },
                "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)"
              },
              "response": []
            },
            {
              "id": "661530d7-2eb7-44ab-8bfb-1411b7648b97",
              "name": "AC fan auto [switches a device]",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "4daabcc8-7bea-4f29-80cf-fb20228a571c",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"200\");",
                      "pm.variables.set(\"igh.code\", \"\");",
                      "pm.variables.set(\"igh.kind\", \"api\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"acId\");",
                      "pm.variables.set(\"igh.what\", \"air conditioner\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}"
                    ]
                  }
                }
              ],
              "request": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json",
                    "type": "text"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"op\": \"set_fan\",\n  \"args\": {\n    \"fan\": \"auto\"\n  }\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{baseUrl}}/devices/{{acId}}/control",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "devices",
                    "{{acId}}",
                    "control"
                  ]
                },
                "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)"
              },
              "response": []
            }
          ]
        },
        {
          "id": "addb737f-60ba-46c5-8b6e-020d13d4faa4",
          "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).",
          "item": [
            {
              "id": "685ac024-564d-45b7-8526-e22f6aa05729",
              "name": "IR key [switches a device]",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "2dc24387-70e1-450c-8711-006580b71c69",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"200\");",
                      "pm.variables.set(\"igh.code\", \"\");",
                      "pm.variables.set(\"igh.kind\", \"api\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"irId\");",
                      "pm.variables.set(\"igh.what\", \"IR remote\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}"
                    ]
                  }
                }
              ],
              "request": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json",
                    "type": "text"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"op\": \"key\",\n  \"args\": {\n    \"key\": \"{{irKey}}\"\n  }\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{baseUrl}}/devices/{{irId}}/control",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "devices",
                    "{{irId}}",
                    "control"
                  ]
                },
                "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)"
              },
              "response": []
            }
          ]
        },
        {
          "id": "8a2a9b5e-9200-410a-8ac6-8d35b740e61f",
          "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`.",
          "item": [
            {
              "id": "8ca8911f-d276-4bf1-8268-b4faeb921fca",
              "name": "Lock [switches a device]",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "8d84e53e-ea5f-475e-82a4-90ae215773c9",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"200\");",
                      "pm.variables.set(\"igh.code\", \"\");",
                      "pm.variables.set(\"igh.kind\", \"api\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"lockId\");",
                      "pm.variables.set(\"igh.what\", \"lock\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}"
                    ]
                  }
                }
              ],
              "request": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json",
                    "type": "text"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"op\": \"lock\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{baseUrl}}/devices/{{lockId}}/control",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "devices",
                    "{{lockId}}",
                    "control"
                  ]
                },
                "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)"
              },
              "response": []
            },
            {
              "id": "720bd51b-a1b4-4bd0-8869-3868f565ffe4",
              "name": "Unlock [switches a device]",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "4afd6a86-c30e-475d-8f75-e2a9fe1887ce",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"200,403\");",
                      "pm.variables.set(\"igh.code\", \"\");",
                      "pm.variables.set(\"igh.kind\", \"api\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"lockId\");",
                      "pm.variables.set(\"igh.what\", \"lock\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}"
                    ]
                  }
                }
              ],
              "request": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json",
                    "type": "text"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"op\": \"unlock\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{baseUrl}}/devices/{{lockId}}/control",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "devices",
                    "{{lockId}}",
                    "control"
                  ]
                },
                "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)"
              },
              "response": []
            },
            {
              "id": "ed46a363-3903-4cd7-89e9-277053866b03",
              "name": "Gate open [switches a device]",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "4e0b2f0c-af12-4f07-883e-c06b0d3bc62d",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"200,403\");",
                      "pm.variables.set(\"igh.code\", \"\");",
                      "pm.variables.set(\"igh.kind\", \"api\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"gateId\");",
                      "pm.variables.set(\"igh.what\", \"gate\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}"
                    ]
                  }
                }
              ],
              "request": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json",
                    "type": "text"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"op\": \"open\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{baseUrl}}/devices/{{gateId}}/control",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "devices",
                    "{{gateId}}",
                    "control"
                  ]
                },
                "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)"
              },
              "response": []
            }
          ]
        },
        {
          "id": "4297c68b-cbb0-41c2-8d31-25b74b236323",
          "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).",
          "item": [
            {
              "id": "6a16d099-5af4-4ca5-85e4-e9b5526e5596",
              "name": "Unit mode eco [switches a device]",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "12e33359-7dac-4fef-8bb2-3c3e7bd9c3ae",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"200\");",
                      "pm.variables.set(\"igh.code\", \"\");",
                      "pm.variables.set(\"igh.kind\", \"api\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"lightId\");",
                      "pm.variables.set(\"igh.what\", \"light\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}"
                    ]
                  }
                }
              ],
              "request": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json",
                    "type": "text"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"op\": \"set_mode\",\n  \"args\": {\n    \"mode\": \"eco\"\n  }\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{baseUrl}}/devices/{{lightId}}/control",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "devices",
                    "{{lightId}}",
                    "control"
                  ]
                },
                "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)"
              },
              "response": []
            },
            {
              "id": "88ba2fd0-d70a-4b44-84d6-d44b85faf909",
              "name": "Unit mode auto [switches a device]",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "deee00aa-4194-4fc9-89b9-3df779976ea6",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"200\");",
                      "pm.variables.set(\"igh.code\", \"\");",
                      "pm.variables.set(\"igh.kind\", \"api\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"lightId\");",
                      "pm.variables.set(\"igh.what\", \"light\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}"
                    ]
                  }
                }
              ],
              "request": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json",
                    "type": "text"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"op\": \"set_mode\",\n  \"args\": {\n    \"mode\": \"auto\"\n  }\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{baseUrl}}/devices/{{lightId}}/control",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "devices",
                    "{{lightId}}",
                    "control"
                  ]
                },
                "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)"
              },
              "response": []
            }
          ]
        },
        {
          "id": "629d4855-fb54-4621-8075-9fcfa9784ce6",
          "name": "Explain",
          "description": "Why a device did what it did (the last 24 h unless `from` and `to` say otherwise).",
          "item": [
            {
              "id": "c398481e-b9b3-4eb9-8148-b526994ee4c6",
              "name": "Explain (body)",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "217b6b3f-50fe-434c-8a8a-12d2d5280b93",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"200\");",
                      "pm.variables.set(\"igh.code\", \"\");",
                      "pm.variables.set(\"igh.kind\", \"api\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"deviceId\");",
                      "pm.variables.set(\"igh.what\", \"device\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}"
                    ]
                  }
                }
              ],
              "request": {
                "method": "POST",
                "header": [
                  {
                    "key": "Content-Type",
                    "value": "application/json",
                    "type": "text"
                  }
                ],
                "body": {
                  "mode": "raw",
                  "raw": "{\n  \"device\": \"{{deviceId}}\"\n}",
                  "options": {
                    "raw": {
                      "language": "json"
                    }
                  }
                },
                "url": {
                  "raw": "{{baseUrl}}/explain",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "explain"
                  ]
                },
                "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)"
              },
              "response": []
            },
            {
              "id": "e3db4299-ecfd-4602-8f48-3290f41b5493",
              "name": "Explain (device path)",
              "event": [
                {
                  "listen": "prerequest",
                  "script": {
                    "id": "51b4c7f3-6ff9-4be0-8edd-57155af7ef05",
                    "type": "text/javascript",
                    "exec": [
                      "// What the collection's scripts check and skip for this request.",
                      "pm.variables.set(\"igh.expect\", \"200\");",
                      "pm.variables.set(\"igh.code\", \"\");",
                      "pm.variables.set(\"igh.kind\", \"api\");",
                      "pm.variables.set(\"igh.only\", \"\");",
                      "pm.variables.set(\"igh.needs\", \"deviceId\");",
                      "pm.variables.set(\"igh.what\", \"device\");",
                      "// Every request's own pre-request script runs this after setting igh.only",
                      "// (local or cloud: skip in an environment of the other transport) and",
                      "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                      "var only = pm.variables.get(\"igh.only\");",
                      "var transport = pm.environment.get(\"transport\");",
                      "if (only && transport && transport !== only) {",
                      "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                      "  pm.execution.skipRequest();",
                      "}",
                      "var needs = pm.variables.get(\"igh.needs\");",
                      "if (needs && !pm.variables.get(needs)) {",
                      "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                      "  pm.execution.skipRequest();",
                      "}"
                    ]
                  }
                }
              ],
              "request": {
                "method": "GET",
                "header": [],
                "url": {
                  "raw": "{{baseUrl}}/devices/{{deviceId}}/explain",
                  "host": [
                    "{{baseUrl}}"
                  ],
                  "path": [
                    "devices",
                    "{{deviceId}}",
                    "explain"
                  ],
                  "query": [
                    {
                      "key": "from",
                      "value": "",
                      "description": "epoch ms",
                      "disabled": true
                    },
                    {
                      "key": "to",
                      "value": "",
                      "description": "epoch ms",
                      "disabled": true
                    }
                  ]
                },
                "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)"
              },
              "response": []
            }
          ]
        }
      ]
    },
    {
      "id": "787119e7-b30e-49c0-8bd4-c5e1fe0752f2",
      "name": "Scenes",
      "description": "List, then create a test scene (\"Postman 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.",
      "item": [
        {
          "id": "e7d4c56a-2060-4f41-8260-99b09d2ac42e",
          "name": "Scenes",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "5fc0f2e0-8c18-4f0e-81e1-50c2aa469227",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"\");",
                  "pm.variables.set(\"igh.what\", \"\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "id": "9c5540a2-bc52-4c3f-8ca6-4e5e9184df9f",
                "type": "text/javascript",
                "exec": [
                  "// Shared by every request's test script: save() writes the environment when it",
                  "// declares the key (an empty environment value hides a collection variable) and",
                  "// the collection; fill() only while empty; answer() is the JSON body or null.",
                  "",
                  "function save(key, value) {",
                  "  var text = value === undefined || value === null ? \"\" : String(value);",
                  "  if (pm.environment.has(key)) { pm.environment.set(key, text); }",
                  "  pm.collectionVariables.set(key, text);",
                  "}",
                  "function fill(key, value) {",
                  "  if (!pm.variables.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }",
                  "}",
                  "function answer() {",
                  "  try { return pm.response.json(); } catch (e) { return null; }",
                  "}",
                  "",
                  "var body = answer();",
                  "if (body) {",
                  "  var first = (body.scenes || [])[0];",
                  "  if (first) { fill(\"sceneId\", first.id); }",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/scenes",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "scenes"
              ]
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "672a5b13-ac34-48bd-8a4f-8cc88b95a6af",
          "name": "Create a test scene",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "ea038b8a-c206-4bf8-8179-20a0282f8b0e",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"201\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"\");",
                  "pm.variables.set(\"igh.what\", \"\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "id": "d0b5e07d-70ad-4b67-83de-72b62f4048a5",
                "type": "text/javascript",
                "exec": [
                  "// Shared by every request's test script: save() writes the environment when it",
                  "// declares the key (an empty environment value hides a collection variable) and",
                  "// the collection; fill() only while empty; answer() is the JSON body or null.",
                  "",
                  "function save(key, value) {",
                  "  var text = value === undefined || value === null ? \"\" : String(value);",
                  "  if (pm.environment.has(key)) { pm.environment.set(key, text); }",
                  "  pm.collectionVariables.set(key, text);",
                  "}",
                  "function fill(key, value) {",
                  "  if (!pm.variables.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }",
                  "}",
                  "function answer() {",
                  "  try { return pm.response.json(); } catch (e) { return null; }",
                  "}",
                  "",
                  "var body = answer();",
                  "if (body) {",
                  "  save(\"createdSceneId\", body.id);",
                  "  save(\"sceneRevision\", body.scene && body.scene.revision !== undefined ? body.scene.revision : 0);",
                  "  console.log(\"Created \" + body.id + \" at revision \" + pm.variables.get(\"sceneRevision\"));",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Postman test scene\",\n  \"steps\": [\n    {\n      \"kind\": \"delay\",\n      \"ms\": 500\n    }\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/scenes",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "scenes"
              ]
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "e929a4d9-caee-4bf5-88ca-0a2dd2e8d15e",
          "name": "The test scene",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "1d0fd372-6908-41f2-8999-66d9f8049837",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"createdSceneId\");",
                  "pm.variables.set(\"igh.what\", \"test scene\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/scenes/{{createdSceneId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "scenes",
                "{{createdSceneId}}"
              ]
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "9712a1a5-44db-43f1-8a04-c144d2990f67",
          "name": "Edit the test scene (ifRevision)",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "0dfe6edf-49d6-4e40-8709-165393325a5b",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"createdSceneId\");",
                  "pm.variables.set(\"igh.what\", \"test scene\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "id": "af1251be-54c8-4c49-8826-462e4a970bea",
                "type": "text/javascript",
                "exec": [
                  "// Shared by every request's test script: save() writes the environment when it",
                  "// declares the key (an empty environment value hides a collection variable) and",
                  "// the collection; fill() only while empty; answer() is the JSON body or null.",
                  "",
                  "function save(key, value) {",
                  "  var text = value === undefined || value === null ? \"\" : String(value);",
                  "  if (pm.environment.has(key)) { pm.environment.set(key, text); }",
                  "  pm.collectionVariables.set(key, text);",
                  "}",
                  "function fill(key, value) {",
                  "  if (!pm.variables.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }",
                  "}",
                  "function answer() {",
                  "  try { return pm.response.json(); } catch (e) { return null; }",
                  "}",
                  "",
                  "var body = answer();",
                  "if (body) {",
                  "  if (body.scene) { save(\"sceneRevision\", body.scene.revision); }",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"ifRevision\": {{sceneRevision}},\n  \"name\": \"Postman test scene (edited)\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/scenes/{{createdSceneId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "scenes",
                "{{createdSceneId}}"
              ]
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "0bc0311c-4412-407f-816b-c104730fddc0",
          "name": "Run the test scene",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "3070b42d-b308-46f0-8b90-d78745869e69",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"createdSceneId\");",
                  "pm.variables.set(\"igh.what\", \"test scene\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/scenes/{{createdSceneId}}/run",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "scenes",
                "{{createdSceneId}}",
                "run"
              ]
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "5e6248be-823d-45c5-8bcb-19c261012dba",
          "name": "Run a scene of the house [switches devices]",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "6645d58f-abde-4035-8727-86d237ad5e7b",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"sceneId\");",
                  "pm.variables.set(\"igh.what\", \"scene\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/scenes/{{sceneId}}/run",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "scenes",
                "{{sceneId}}",
                "run"
              ]
            },
            "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)"
          },
          "response": []
        }
      ]
    },
    {
      "id": "c90202c2-d6d8-4ac7-8436-36ea83e44afe",
      "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.",
      "item": [
        {
          "id": "79ce1da7-45e3-4f68-8c5e-693a819c4709",
          "name": "Automations",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "620e6908-3201-4913-80ed-a5823fc258c2",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200,404\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"\");",
                  "pm.variables.set(\"igh.what\", \"\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/automations",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "automations"
              ]
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "c5e23ee7-7082-4980-801a-93ef59573b64",
          "name": "Create a test automation (disabled)",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "04c88b58-b256-4098-8d42-f56c25779ea4",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"201\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"unitRef\");",
                  "pm.variables.set(\"igh.what\", \"unit (Getting started → House fills unitRef)\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "id": "8ffe1afd-59db-49fb-862a-c08186be5e4c",
                "type": "text/javascript",
                "exec": [
                  "// Shared by every request's test script: save() writes the environment when it",
                  "// declares the key (an empty environment value hides a collection variable) and",
                  "// the collection; fill() only while empty; answer() is the JSON body or null.",
                  "",
                  "function save(key, value) {",
                  "  var text = value === undefined || value === null ? \"\" : String(value);",
                  "  if (pm.environment.has(key)) { pm.environment.set(key, text); }",
                  "  pm.collectionVariables.set(key, text);",
                  "}",
                  "function fill(key, value) {",
                  "  if (!pm.variables.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }",
                  "}",
                  "function answer() {",
                  "  try { return pm.response.json(); } catch (e) { return null; }",
                  "}",
                  "",
                  "var body = answer();",
                  "if (body) {",
                  "  var a = body.automation || {};",
                  "  save(\"automationId\", a.id);",
                  "  save(\"automationRevision\", a.revision !== undefined ? a.revision : 0);",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Postman 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}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/automations",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "automations"
              ]
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "e60bab87-b9d2-4605-8ebc-9c7d8218213a",
          "name": "The test automation",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "287432ac-f649-40a8-8bbd-13ca6f65a8ae",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"automationId\");",
                  "pm.variables.set(\"igh.what\", \"automation\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/automations/{{automationId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "automations",
                "{{automationId}}"
              ]
            },
            "description": "**`GET /v1/automations/{id}`** — One automation.\n\n[Reference](https://developers.igh.app/reference.html#op-automation)"
          },
          "response": []
        },
        {
          "id": "790dbf71-fded-46af-86ea-9f1d64fb9a3b",
          "name": "Edit the test automation (ifRevision)",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "939592c6-d4c1-4362-8272-cbf68aa680c2",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"automationId\");",
                  "pm.variables.set(\"igh.what\", \"automation\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "id": "ea620bab-6947-49e3-853c-0aafa7a46177",
                "type": "text/javascript",
                "exec": [
                  "// Shared by every request's test script: save() writes the environment when it",
                  "// declares the key (an empty environment value hides a collection variable) and",
                  "// the collection; fill() only while empty; answer() is the JSON body or null.",
                  "",
                  "function save(key, value) {",
                  "  var text = value === undefined || value === null ? \"\" : String(value);",
                  "  if (pm.environment.has(key)) { pm.environment.set(key, text); }",
                  "  pm.collectionVariables.set(key, text);",
                  "}",
                  "function fill(key, value) {",
                  "  if (!pm.variables.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }",
                  "}",
                  "function answer() {",
                  "  try { return pm.response.json(); } catch (e) { return null; }",
                  "}",
                  "",
                  "var body = answer();",
                  "if (body) {",
                  "  if (body.automation) { save(\"automationRevision\", body.automation.revision); }",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"ifRevision\": {{automationRevision}},\n  \"name\": \"Postman test automation (edited)\",\n  \"enabled\": false\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/automations/{{automationId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "automations",
                "{{automationId}}"
              ]
            },
            "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)"
          },
          "response": []
        }
      ]
    },
    {
      "id": "80ccd293-02d8-4a3f-800f-0d40b23b5233",
      "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.",
      "item": [
        {
          "id": "2d75d41b-de54-4930-89af-cf823221c740",
          "name": "Schedules",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "323abc7d-406e-4931-8feb-7cf88c71bab7",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200,404\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"\");",
                  "pm.variables.set(\"igh.what\", \"\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/schedules",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "schedules"
              ]
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "c3ebcc3a-4ef3-4629-8b36-fbbe68d9ebed",
          "name": "Create a test schedule (paused)",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "c7199375-189b-4fb8-8297-b41f12d04491",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"201\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"\");",
                  "pm.variables.set(\"igh.what\", \"\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "id": "3c5e965e-8d7c-46d1-84e9-096d9f3a3601",
                "type": "text/javascript",
                "exec": [
                  "// Shared by every request's test script: save() writes the environment when it",
                  "// declares the key (an empty environment value hides a collection variable) and",
                  "// the collection; fill() only while empty; answer() is the JSON body or null.",
                  "",
                  "function save(key, value) {",
                  "  var text = value === undefined || value === null ? \"\" : String(value);",
                  "  if (pm.environment.has(key)) { pm.environment.set(key, text); }",
                  "  pm.collectionVariables.set(key, text);",
                  "}",
                  "function fill(key, value) {",
                  "  if (!pm.variables.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }",
                  "}",
                  "function answer() {",
                  "  try { return pm.response.json(); } catch (e) { return null; }",
                  "}",
                  "",
                  "var body = answer();",
                  "if (body) {",
                  "  var s = body.schedule || {};",
                  "  save(\"scheduleId\", s.id);",
                  "  save(\"scheduleRevision\", s.revision !== undefined ? s.revision : 0);",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Postman 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}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/schedules",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "schedules"
              ]
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "dd23f3f5-105c-4da9-87df-322a811af99f",
          "name": "The test schedule",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "954a2806-c647-4a39-8e67-0a9d9b4b9098",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"scheduleId\");",
                  "pm.variables.set(\"igh.what\", \"schedule\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/schedules/{{scheduleId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "schedules",
                "{{scheduleId}}"
              ]
            },
            "description": "**`GET /v1/schedules/{id}`** — One gateway schedule.\n\n[Reference](https://developers.igh.app/reference.html#op-schedule)"
          },
          "response": []
        },
        {
          "id": "24025dcf-644a-4b10-8568-a95a7ae3e059",
          "name": "Edit the test schedule (ifRevision)",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "0a7825d0-5ebd-413f-8d16-6a93acec8d1e",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"scheduleId\");",
                  "pm.variables.set(\"igh.what\", \"schedule\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "id": "417df77e-70a4-42b7-8a5e-9fa00015e2f9",
                "type": "text/javascript",
                "exec": [
                  "// Shared by every request's test script: save() writes the environment when it",
                  "// declares the key (an empty environment value hides a collection variable) and",
                  "// the collection; fill() only while empty; answer() is the JSON body or null.",
                  "",
                  "function save(key, value) {",
                  "  var text = value === undefined || value === null ? \"\" : String(value);",
                  "  if (pm.environment.has(key)) { pm.environment.set(key, text); }",
                  "  pm.collectionVariables.set(key, text);",
                  "}",
                  "function fill(key, value) {",
                  "  if (!pm.variables.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }",
                  "}",
                  "function answer() {",
                  "  try { return pm.response.json(); } catch (e) { return null; }",
                  "}",
                  "",
                  "var body = answer();",
                  "if (body) {",
                  "  if (body.schedule) { save(\"scheduleRevision\", body.schedule.revision); }",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"ifRevision\": {{scheduleRevision}},\n  \"name\": \"Postman test schedule (edited)\",\n  \"paused\": true\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/schedules/{{scheduleId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "schedules",
                "{{scheduleId}}"
              ]
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "3915a368-d6c2-4b5c-8369-1e4bc7ce5465",
          "name": "A unit's own schedules",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "3e1ca13a-b3d7-4d07-8048-ca0185d9a761",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200,404\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"deviceId\");",
                  "pm.variables.set(\"igh.what\", \"device\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/devices/{{deviceId}}/schedules",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "devices",
                "{{deviceId}}",
                "schedules"
              ]
            },
            "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)"
          },
          "response": []
        }
      ]
    },
    {
      "id": "c6e09ebc-db3c-4716-8620-dc91b3bfb962",
      "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`).",
      "item": [
        {
          "id": "17c5181f-43df-4257-8eab-b11556125256",
          "name": "Intent table",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "e86b67f5-492a-450e-861f-b4d7173ddc10",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"\");",
                  "pm.variables.set(\"igh.what\", \"\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/intents",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "intents"
              ]
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "8833b1e5-46fb-4aa3-866d-196a142cec31",
          "name": "Device intents",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "b6938843-e679-42f2-8b74-98ffbc806ece",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200,403\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"deviceId\");",
                  "pm.variables.set(\"igh.what\", \"device\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "id": "e83c5d90-e834-4f10-8bee-77c0ac346e1e",
                "type": "text/javascript",
                "exec": [
                  "// Shared by every request's test script: save() writes the environment when it",
                  "// declares the key (an empty environment value hides a collection variable) and",
                  "// the collection; fill() only while empty; answer() is the JSON body or null.",
                  "",
                  "function save(key, value) {",
                  "  var text = value === undefined || value === null ? \"\" : String(value);",
                  "  if (pm.environment.has(key)) { pm.environment.set(key, text); }",
                  "  pm.collectionVariables.set(key, text);",
                  "}",
                  "function fill(key, value) {",
                  "  if (!pm.variables.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }",
                  "}",
                  "function answer() {",
                  "  try { return pm.response.json(); } catch (e) { return null; }",
                  "}",
                  "",
                  "var body = answer();",
                  "if (body) {",
                  "  var writable = (body.intents || []).filter(function (i) { return i.access === \"read_write\" && i.value !== null && i.value !== undefined; })[0];",
                  "  if (writable) { fill(\"intentId\", writable.intent); save(\"intentValue\", JSON.stringify(writable.value)); }",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/devices/{{deviceId}}/intents",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "devices",
                "{{deviceId}}",
                "intents"
              ]
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "baae2976-f7a0-4fa0-8345-dba3e06bfb4b",
          "name": "Set an intent [changes a unit setting]",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "a4c187f7-6923-41ef-8a7e-6f9407d4b155",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200,403\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"deviceId\");",
                  "pm.variables.set(\"igh.what\", \"device\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "",
                  "if (!pm.variables.get(\"intentId\") || !pm.variables.get(\"intentValue\")) {",
                  "  console.log(\"No intentId or intentValue: send Device intents first. Using the doc example off_after_no_motion = 10.\");",
                  "  pm.variables.set(\"intentId\", \"off_after_no_motion\");",
                  "  pm.variables.set(\"intentValue\", \"10\");",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "PUT",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"value\": {{intentValue}}\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/devices/{{deviceId}}/intents/{{intentId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "devices",
                "{{deviceId}}",
                "intents",
                "{{intentId}}"
              ]
            },
            "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)"
          },
          "response": []
        }
      ]
    },
    {
      "id": "75165574-a8c8-430f-8280-622b2629944d",
      "name": "Sensors and energy",
      "description": "Readings, a sensor's detections, and energy and cost by scope.",
      "item": [
        {
          "id": "e70c812d-1637-4afa-8928-761fe42a1093",
          "name": "Sensors",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "4bae25d8-4116-496d-83e4-a487732be329",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"\");",
                  "pm.variables.set(\"igh.what\", \"\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "id": "b49baf09-7077-4031-8d40-abce078b39b3",
                "type": "text/javascript",
                "exec": [
                  "// Shared by every request's test script: save() writes the environment when it",
                  "// declares the key (an empty environment value hides a collection variable) and",
                  "// the collection; fill() only while empty; answer() is the JSON body or null.",
                  "",
                  "function save(key, value) {",
                  "  var text = value === undefined || value === null ? \"\" : String(value);",
                  "  if (pm.environment.has(key)) { pm.environment.set(key, text); }",
                  "  pm.collectionVariables.set(key, text);",
                  "}",
                  "function fill(key, value) {",
                  "  if (!pm.variables.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }",
                  "}",
                  "function answer() {",
                  "  try { return pm.response.json(); } catch (e) { return null; }",
                  "}",
                  "",
                  "var body = answer();",
                  "if (body) {",
                  "  var first = (body.sensors || [])[0];",
                  "  if (first) { fill(\"sensorId\", first.id || first.device); }",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/sensors",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "sensors"
              ]
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "d94d19de-51d2-45da-8434-f2c9c1591517",
          "name": "Detections",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "09df6991-6cf7-4300-8482-9596859212dd",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"sensorId\");",
                  "pm.variables.set(\"igh.what\", \"sensor\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/sensors/{{sensorId}}/detections?limit=20",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "sensors",
                "{{sensorId}}",
                "detections"
              ],
              "query": [
                {
                  "key": "limit",
                  "value": "20",
                  "description": "how many"
                },
                {
                  "key": "cursor",
                  "value": "",
                  "description": "nextCursor of the previous page",
                  "disabled": true
                }
              ]
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "2f48fb48-777e-4eba-83da-900599275500",
          "name": "Energy (house, today)",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "75554be2-9216-497f-8c18-066766cbbca5",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"\");",
                  "pm.variables.set(\"igh.what\", \"\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/energy?scope=house&period=day",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "energy"
              ],
              "query": [
                {
                  "key": "scope",
                  "value": "house",
                  "description": "house, room:<id>, floor:<id> or device:<device id>"
                },
                {
                  "key": "period",
                  "value": "day",
                  "description": "day, week, month or year (or from and to)"
                }
              ]
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "dcc7e90b-e642-4974-8318-1b14c7fadaec",
          "name": "Energy (a room, this month)",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "e8ca6d25-6b1c-49b7-87bd-a64592e5a6f6",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"roomId\");",
                  "pm.variables.set(\"igh.what\", \"room\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/energy?scope=room:{{roomId}}&period=month",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "energy"
              ],
              "query": [
                {
                  "key": "scope",
                  "value": "room:{{roomId}}",
                  "description": "house, room:<id>, floor:<id> or device:<device id>"
                },
                {
                  "key": "period",
                  "value": "month",
                  "description": "day, week, month or year"
                }
              ]
            },
            "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)"
          },
          "response": []
        }
      ]
    },
    {
      "id": "dd0f92cc-a683-43a7-8162-972916443a4c",
      "name": "Activity",
      "description": "The activity log of exposed items.",
      "item": [
        {
          "id": "5243a348-b3e6-42b2-80a9-c5775f4b72a4",
          "name": "Activity",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "9b0eb372-d76f-42b4-8e0e-d4519b50fdfd",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"\");",
                  "pm.variables.set(\"igh.what\", \"\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/activity?device={{deviceId}}&limit=20",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "activity"
              ],
              "query": [
                {
                  "key": "device",
                  "value": "{{deviceId}}",
                  "description": "one exposed device (leave out for every one)"
                },
                {
                  "key": "limit",
                  "value": "20",
                  "description": "1–200 (default 50)"
                },
                {
                  "key": "from",
                  "value": "",
                  "description": "epoch ms (default: 30 days before to)",
                  "disabled": true
                },
                {
                  "key": "to",
                  "value": "",
                  "description": "epoch ms (default: now)",
                  "disabled": true
                }
              ]
            },
            "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)"
          },
          "response": []
        }
      ]
    },
    {
      "id": "26d9769e-0799-433f-83ec-f68865254aae",
      "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.",
      "item": [
        {
          "id": "56fd7868-3608-44cc-8ad3-97e0a318896a",
          "name": "Rename a room",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "4f5a68b2-41cd-4139-8a81-ca2b3cd45515",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200,403\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"roomId\");",
                  "pm.variables.set(\"igh.what\", \"room\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"{{roomName}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/rooms/{{roomId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "rooms",
                "{{roomId}}"
              ]
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "8f072297-039c-47d5-8546-6f015230afdc",
          "name": "Rename a unit or gang",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "22817e7b-e144-4977-8114-eca101b87b06",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200,403\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"deviceId\");",
                  "pm.variables.set(\"igh.what\", \"device\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"{{deviceName}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/devices/{{deviceId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "devices",
                "{{deviceId}}"
              ]
            },
            "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)"
          },
          "response": []
        }
      ]
    },
    {
      "id": "0e251045-6721-464c-806c-5a54ab7b79ee",
      "name": "TLS",
      "description": "The gateway's local CA, to pin. Fetch it through a cloud environment first; on the home network Postman must trust it already.",
      "item": [
        {
          "id": "2d86f076-7894-4bcd-893d-6419463cac31",
          "name": "Gateway CA (to pin)",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "463fc8be-8e8f-46cd-83d8-6552e34374c8",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"\");",
                  "pm.variables.set(\"igh.what\", \"\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "id": "5711b399-9c91-454a-89ef-9dc73f1a1f30",
                "type": "text/javascript",
                "exec": [
                  "// Shared by every request's test script: save() writes the environment when it",
                  "// declares the key (an empty environment value hides a collection variable) and",
                  "// the collection; fill() only while empty; answer() is the JSON body or null.",
                  "",
                  "function save(key, value) {",
                  "  var text = value === undefined || value === null ? \"\" : String(value);",
                  "  if (pm.environment.has(key)) { pm.environment.set(key, text); }",
                  "  pm.collectionVariables.set(key, text);",
                  "}",
                  "function fill(key, value) {",
                  "  if (!pm.variables.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }",
                  "}",
                  "function answer() {",
                  "  try { return pm.response.json(); } catch (e) { return null; }",
                  "}",
                  "",
                  "var body = answer();",
                  "if (body && body.ca) {",
                  "  save(\"caFingerprint\", body.fingerprint);",
                  "  console.log(\"CA fingerprint (\" + body.algorithm + \"): \" + body.fingerprint);",
                  "  pm.visualizer.set(\"<p style='font-family:sans-serif'>Save this as ca.pem, then Settings → Certificates → CA certificates.<br>SHA-256 {{fingerprint}}</p><pre>{{ca}}</pre>\", { ca: body.ca, fingerprint: body.fingerprint });",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/gateway/tls",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "gateway",
                "tls"
              ]
            },
            "description": "Works on both transports, but the first time fetch it through the cloud environment: on the home network Postman must already trust the CA. Save `ca` as `ca.pem` (the Visualize tab shows it to copy), compare `fingerprint`, then select the file in Postman Settings → Certificates → CA certificates.\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)"
          },
          "response": []
        }
      ]
    },
    {
      "id": "c4a9724e-8f17-4357-8494-459a3d290fa2",
      "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).",
      "item": [
        {
          "id": "b5ef3469-66d6-46db-8e09-7745d02727bb",
          "name": "Unknown id → 404 not_found",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "74d0eb80-eacf-419e-8e57-533d4b6dc1ae",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"404\");",
                  "pm.variables.set(\"igh.code\", \"not_found\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"\");",
                  "pm.variables.set(\"igh.what\", \"\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/devices/postman-unknown-device",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "devices",
                "postman-unknown-device"
              ]
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "eb917fbc-829b-4bcd-8e89-2600821276ef",
          "name": "Unknown operation → 422 validation",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "330dc104-832b-4002-81a1-c7f73d2cb1b3",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"422\");",
                  "pm.variables.set(\"igh.code\", \"validation\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"deviceId\");",
                  "pm.variables.set(\"igh.what\", \"device\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"op\": \"postman_unknown_operation\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/devices/{{deviceId}}/control",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "devices",
                "{{deviceId}}",
                "control"
              ]
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "5fb0cd71-8931-4a5f-8a65-afbee44c38da",
          "name": "Unlock not allowed → 403 unlock_not_allowed",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "638ab907-5e17-4844-815b-db42d4b6cbfc",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"403\");",
                  "pm.variables.set(\"igh.code\", \"unlock_not_allowed\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"lockId\");",
                  "pm.variables.set(\"igh.what\", \"lock\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"op\": \"unlock\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/devices/{{lockId}}/control",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "devices",
                "{{lockId}}",
                "control"
              ]
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "a0b94c64-514b-4685-8de2-4da8a77d8be9",
          "name": "Config not allowed → 403 config_not_allowed",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "206edff7-fab5-4c07-86ec-016f6d6ae466",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"403\");",
                  "pm.variables.set(\"igh.code\", \"config_not_allowed\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"deviceId\");",
                  "pm.variables.set(\"igh.what\", \"device\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/devices/{{deviceId}}/intents",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "devices",
                "{{deviceId}}",
                "intents"
              ]
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "5e588e8c-66a5-4b2e-82a7-ac8728e500b9",
          "name": "Read-only intent → 403 not_offered",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "0b7bb242-040a-4635-8e06-731364a2d7d5",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"403\");",
                  "pm.variables.set(\"igh.code\", \"not_offered\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"deviceId\");",
                  "pm.variables.set(\"igh.what\", \"device\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "PUT",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"value\": 1\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/devices/{{deviceId}}/intents/key_immunity",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "devices",
                "{{deviceId}}",
                "intents",
                "key_immunity"
              ]
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "d7280fdb-1b04-4f1c-8f81-bda2a05563cd",
          "name": "Structural not allowed → 403 structural_not_allowed",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "8a36de5b-0e2e-4dfb-8541-9215a56eb077",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"403\");",
                  "pm.variables.set(\"igh.code\", \"structural_not_allowed\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"roomId\");",
                  "pm.variables.set(\"igh.what\", \"room\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"{{roomName}}\",\n  \"floor\": \"postman\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/rooms/{{roomId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "rooms",
                "{{roomId}}"
              ]
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "462854d2-8703-40b2-8574-98c2ecf32b30",
          "name": "Revision mismatch → 409 revision_mismatch",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "f0ef78f5-7e15-45b4-8822-50946a718053",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"409\");",
                  "pm.variables.set(\"igh.code\", \"revision_mismatch\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"createdSceneId\");",
                  "pm.variables.set(\"igh.what\", \"test scene (Scenes → Create a test scene)\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json",
                "type": "text"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"ifRevision\": 999999,\n  \"name\": \"Postman test scene (edited)\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/scenes/{{createdSceneId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "scenes",
                "{{createdSceneId}}"
              ]
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "5cb2b728-aa14-45c8-8b05-b99da015d1dd",
          "name": "Another API version → 410 version_unsupported (local only)",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "1bc8f9d4-86df-47c9-86a0-ed11eea69a81",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"410\");",
                  "pm.variables.set(\"igh.code\", \"version_unsupported\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"local\");",
                  "pm.variables.set(\"igh.needs\", \"\");",
                  "pm.variables.set(\"igh.what\", \"\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "",
                  "pm.request.url = pm.variables.replaceIn(pm.request.url.toString()).replace(/\\/v1\\/house$/, \"/v2/house\");"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/house",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "house"
              ]
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "f85a4b4a-6c81-4765-8844-e83b58dd354d",
          "name": "Bad token → 401 unauthorized",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "62747887-b328-48ce-8f21-c0f9407368ef",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"401\");",
                  "pm.variables.set(\"igh.code\", \"unauthorized\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"\");",
                  "pm.variables.set(\"igh.what\", \"\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/house",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "house"
              ]
            },
            "auth": {
              "type": "bearer",
              "bearer": [
                {
                  "key": "token",
                  "value": "igh_key_postman-not-a-credential",
                  "type": "string"
                }
              ]
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "e2de263e-d79d-4598-838e-0a891b5728c6",
          "name": "Rate limit burst → 429 rate_limited (cloud only)",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "cb9ff97a-408d-460b-8307-aeb3bebee941",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200,429\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"cloud\");",
                  "pm.variables.set(\"igh.needs\", \"\");",
                  "pm.variables.set(\"igh.what\", \"\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/state",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "state"
              ]
            },
            "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 in the Collection Runner with 650 iterations and no delay: 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)"
          },
          "response": []
        }
      ]
    },
    {
      "id": "4bda853a-8350-47f5-8eb4-6f5ba0a44a19",
      "name": "Smoke run (read-only)",
      "description": "Run this folder in the Collection Runner (Run folder) after setting `{{token}}`: every read, top to bottom, with nothing switched or created. Automations and schedules may answer 404 and device intents 403 when the integration has them off.",
      "item": [
        {
          "id": "34050013-ffbb-4ed3-8850-882042c27caf",
          "name": "About",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "e0a3d7a0-f5a6-440d-8aed-4ce061fb0f28",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"\");",
                  "pm.variables.set(\"igh.what\", \"\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/about",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "about"
              ]
            },
            "auth": {
              "type": "noauth"
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "8c66c098-63bb-4b79-80f2-5fae602f3f9e",
          "name": "Integration (who am I)",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "128fd319-e693-442b-8043-36e3e17d0fc8",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"\");",
                  "pm.variables.set(\"igh.what\", \"\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "id": "957cc2d7-cee3-4203-88fa-59bf03c05a24",
                "type": "text/javascript",
                "exec": [
                  "// Shared by every request's test script: save() writes the environment when it",
                  "// declares the key (an empty environment value hides a collection variable) and",
                  "// the collection; fill() only while empty; answer() is the JSON body or null.",
                  "",
                  "function save(key, value) {",
                  "  var text = value === undefined || value === null ? \"\" : String(value);",
                  "  if (pm.environment.has(key)) { pm.environment.set(key, text); }",
                  "  pm.collectionVariables.set(key, text);",
                  "}",
                  "function fill(key, value) {",
                  "  if (!pm.variables.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }",
                  "}",
                  "function answer() {",
                  "  try { return pm.response.json(); } catch (e) { return null; }",
                  "}",
                  "",
                  "var body = answer();",
                  "if (body && body.integration) {",
                  "  var credential = body.integration.credential || {};",
                  "  console.log(\"House \" + body.house + \", integration \" + body.integration.name + \" (\" + body.integration.id + \"), credential \" + credential.kind + \".\");",
                  "  console.log(\"Settings: \" + JSON.stringify(body.integration.settings));",
                  "  fill(\"serial\", String(body.gateway || \"\").toLowerCase());",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/integration",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "integration"
              ]
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "d12e42a4-3aab-4b2f-8173-8459fb057acd",
          "name": "House",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "626186c3-6cb9-4c0c-8a81-29ffd9607a51",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"\");",
                  "pm.variables.set(\"igh.what\", \"\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "id": "2e395336-2bf6-44c8-8948-41a0a290e825",
                "type": "text/javascript",
                "exec": [
                  "// Shared by every request's test script: save() writes the environment when it",
                  "// declares the key (an empty environment value hides a collection variable) and",
                  "// the collection; fill() only while empty; answer() is the JSON body or null.",
                  "",
                  "function save(key, value) {",
                  "  var text = value === undefined || value === null ? \"\" : String(value);",
                  "  if (pm.environment.has(key)) { pm.environment.set(key, text); }",
                  "  pm.collectionVariables.set(key, text);",
                  "}",
                  "function fill(key, value) {",
                  "  if (!pm.variables.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }",
                  "}",
                  "function answer() {",
                  "  try { return pm.response.json(); } catch (e) { return null; }",
                  "}",
                  "",
                  "var house = answer();",
                  "if (house && house.floors) {",
                  "  var units = [];",
                  "  var rooms = [];",
                  "  house.floors.forEach(function (floor) { floor.rooms.forEach(function (room) { rooms.push(room); }); });",
                  "  (house.rooms || []).forEach(function (room) { rooms.push(room); });",
                  "  rooms.forEach(function (room) { (room.units || []).forEach(function (unit) { units.push(unit); }); });",
                  "  (house.units || []).forEach(function (unit) { units.push(unit); });",
                  "  var devices = [];",
                  "  units.forEach(function (unit) {",
                  "    devices.push(unit);",
                  "    (unit.gangs || []).forEach(function (gang) { devices.push(gang); });",
                  "  });",
                  "  var has = function (device, op) { return (device.operations || []).indexOf(op) !== -1; };",
                  "  var first = function (test) { for (var i = 0; i < devices.length; i += 1) { if (test(devices[i])) { return devices[i]; } } return null; };",
                  "  var light = first(function (d) { return has(d, \"set_power\") && !has(d, \"set_temp\") && !has(d, \"key\") && (!d.class || d.class.indexOf(\"light\") === 0); });",
                  "  var pick = {",
                  "    lightId: light,",
                  "    dimmerId: first(function (d) { return has(d, \"set_brightness\"); }),",
                  "    shutterId: first(function (d) { return has(d, \"set_position\"); }),",
                  "    acId: first(function (d) { return has(d, \"set_ac_mode\"); }),",
                  "    irId: first(function (d) { return has(d, \"key\"); }),",
                  "    lockId: first(function (d) { return has(d, \"unlock\"); }),",
                  "    gateId: first(function (d) { return d.class === \"gate\"; })",
                  "  };",
                  "  Object.keys(pick).forEach(function (key) { if (pick[key]) { fill(key, pick[key].id); } });",
                  "  var device = light || first(function (d) { return (d.operations || []).length > 0; }) || devices[0];",
                  "  if (device) {",
                  "    fill(\"deviceId\", device.id);",
                  "    fill(\"deviceName\", device.name);",
                  "  }",
                  "  if (pm.variables.get(\"deviceId\")) { save(\"unitRef\", pm.variables.get(\"deviceId\").split(\":\")[2]); }",
                  "  if (rooms[0]) { fill(\"roomId\", rooms[0].id); fill(\"roomName\", rooms[0].name); }",
                  "  if (house.scenes && house.scenes[0]) { fill(\"sceneId\", house.scenes[0].id); }",
                  "  console.log(\"House \" + house.house + \" (gateway \" + house.gateway + \"): \" + devices.length + \" devices, \" + rooms.length + \" rooms, \" + (house.scenes || []).length + \" scenes.\");",
                  "  [\"deviceId\", \"lightId\", \"dimmerId\", \"shutterId\", \"acId\", \"irId\", \"lockId\", \"gateId\", \"roomId\", \"sceneId\"].forEach(function (key) {",
                  "    console.log(key + \" = \" + (pm.variables.get(key) || \"(none in this house)\"));",
                  "  });",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/house",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "house"
              ]
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "d54426a8-1da6-4640-8a70-6a6c3cd6be20",
          "name": "State",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "b1ac3c35-71da-4e46-82db-160a19aa43d0",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"\");",
                  "pm.variables.set(\"igh.what\", \"\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/state",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "state"
              ]
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "d1b878d9-1f58-4c99-8cff-d9a2ae2e8366",
          "name": "Device",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "35c2c2dd-e4dc-4756-81b1-439748b2a991",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"deviceId\");",
                  "pm.variables.set(\"igh.what\", \"device\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/devices/{{deviceId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "devices",
                "{{deviceId}}"
              ]
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "36916cda-93e7-4421-85b6-890fda6592c6",
          "name": "Scenes",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "18c080aa-f192-4502-8ad4-fd24fdbccbc9",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"\");",
                  "pm.variables.set(\"igh.what\", \"\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "id": "1f71e5d3-7c8d-48cd-8c43-988db138774e",
                "type": "text/javascript",
                "exec": [
                  "// Shared by every request's test script: save() writes the environment when it",
                  "// declares the key (an empty environment value hides a collection variable) and",
                  "// the collection; fill() only while empty; answer() is the JSON body or null.",
                  "",
                  "function save(key, value) {",
                  "  var text = value === undefined || value === null ? \"\" : String(value);",
                  "  if (pm.environment.has(key)) { pm.environment.set(key, text); }",
                  "  pm.collectionVariables.set(key, text);",
                  "}",
                  "function fill(key, value) {",
                  "  if (!pm.variables.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }",
                  "}",
                  "function answer() {",
                  "  try { return pm.response.json(); } catch (e) { return null; }",
                  "}",
                  "",
                  "var body = answer();",
                  "if (body) {",
                  "  var first = (body.scenes || [])[0];",
                  "  if (first) { fill(\"sceneId\", first.id); }",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/scenes",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "scenes"
              ]
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "67ad11b4-6403-45ca-887d-08dcd21545c4",
          "name": "Sensors",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "59fc837b-b70d-4e2c-8fdb-8c7cd83bd78b",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"\");",
                  "pm.variables.set(\"igh.what\", \"\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "id": "9f72eb22-9090-48e6-80d6-365596c7c858",
                "type": "text/javascript",
                "exec": [
                  "// Shared by every request's test script: save() writes the environment when it",
                  "// declares the key (an empty environment value hides a collection variable) and",
                  "// the collection; fill() only while empty; answer() is the JSON body or null.",
                  "",
                  "function save(key, value) {",
                  "  var text = value === undefined || value === null ? \"\" : String(value);",
                  "  if (pm.environment.has(key)) { pm.environment.set(key, text); }",
                  "  pm.collectionVariables.set(key, text);",
                  "}",
                  "function fill(key, value) {",
                  "  if (!pm.variables.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }",
                  "}",
                  "function answer() {",
                  "  try { return pm.response.json(); } catch (e) { return null; }",
                  "}",
                  "",
                  "var body = answer();",
                  "if (body) {",
                  "  var first = (body.sensors || [])[0];",
                  "  if (first) { fill(\"sensorId\", first.id || first.device); }",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/sensors",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "sensors"
              ]
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "5427b093-b7eb-43d9-8e60-5cd6b50640e0",
          "name": "Energy (house, today)",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "a8d36c35-185f-4c52-82d1-2afbdd5b461a",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"\");",
                  "pm.variables.set(\"igh.what\", \"\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/energy?scope=house&period=day",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "energy"
              ],
              "query": [
                {
                  "key": "scope",
                  "value": "house",
                  "description": "house, room:<id>, floor:<id> or device:<device id>"
                },
                {
                  "key": "period",
                  "value": "day",
                  "description": "day, week, month or year (or from and to)"
                }
              ]
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "89e1cfbf-4ba3-4730-85ef-5269e7f4f34a",
          "name": "Activity",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "837549aa-7363-4b81-854e-1655d4a71777",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"\");",
                  "pm.variables.set(\"igh.what\", \"\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/activity?device={{deviceId}}&limit=20",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "activity"
              ],
              "query": [
                {
                  "key": "device",
                  "value": "{{deviceId}}",
                  "description": "one exposed device (leave out for every one)"
                },
                {
                  "key": "limit",
                  "value": "20",
                  "description": "1–200 (default 50)"
                },
                {
                  "key": "from",
                  "value": "",
                  "description": "epoch ms (default: 30 days before to)",
                  "disabled": true
                },
                {
                  "key": "to",
                  "value": "",
                  "description": "epoch ms (default: now)",
                  "disabled": true
                }
              ]
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "4568f98a-c9ca-44af-88f4-a1b691388b43",
          "name": "Intent table",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "d621d707-d813-4fda-8f3b-1a9c936f63d1",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"\");",
                  "pm.variables.set(\"igh.what\", \"\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/intents",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "intents"
              ]
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "0b6b01d1-02f4-4fc8-8a1a-f6719459eedb",
          "name": "Device intents",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "caf82b7c-1892-4d98-8b77-a29213d8ef3b",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200,403\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"deviceId\");",
                  "pm.variables.set(\"igh.what\", \"device\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "id": "707a6e3d-1f4a-45ca-83de-d360bd7bda5a",
                "type": "text/javascript",
                "exec": [
                  "// Shared by every request's test script: save() writes the environment when it",
                  "// declares the key (an empty environment value hides a collection variable) and",
                  "// the collection; fill() only while empty; answer() is the JSON body or null.",
                  "",
                  "function save(key, value) {",
                  "  var text = value === undefined || value === null ? \"\" : String(value);",
                  "  if (pm.environment.has(key)) { pm.environment.set(key, text); }",
                  "  pm.collectionVariables.set(key, text);",
                  "}",
                  "function fill(key, value) {",
                  "  if (!pm.variables.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }",
                  "}",
                  "function answer() {",
                  "  try { return pm.response.json(); } catch (e) { return null; }",
                  "}",
                  "",
                  "var body = answer();",
                  "if (body) {",
                  "  var writable = (body.intents || []).filter(function (i) { return i.access === \"read_write\" && i.value !== null && i.value !== undefined; })[0];",
                  "  if (writable) { fill(\"intentId\", writable.intent); save(\"intentValue\", JSON.stringify(writable.value)); }",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/devices/{{deviceId}}/intents",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "devices",
                "{{deviceId}}",
                "intents"
              ]
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "314c5e47-f8e9-4443-8f5a-6f877315fb64",
          "name": "Automations",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "ac914525-4dce-4f8b-8816-dd5b83fcb846",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200,404\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"\");",
                  "pm.variables.set(\"igh.what\", \"\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/automations",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "automations"
              ]
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "d222eec0-a689-480b-82e0-5e4247455544",
          "name": "Schedules",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "877a6ff6-9feb-44a1-88ce-662918a5160c",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200,404\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"\");",
                  "pm.variables.set(\"igh.what\", \"\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/schedules",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "schedules"
              ]
            },
            "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)"
          },
          "response": []
        },
        {
          "id": "36bf9a8f-8de2-484c-812b-8f7aea18fe8a",
          "name": "Gateway CA (to pin)",
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "id": "acdb0181-6a9e-4546-8c9e-87135a247a32",
                "type": "text/javascript",
                "exec": [
                  "// What the collection's scripts check and skip for this request.",
                  "pm.variables.set(\"igh.expect\", \"200\");",
                  "pm.variables.set(\"igh.code\", \"\");",
                  "pm.variables.set(\"igh.kind\", \"api\");",
                  "pm.variables.set(\"igh.only\", \"\");",
                  "pm.variables.set(\"igh.needs\", \"\");",
                  "pm.variables.set(\"igh.what\", \"\");",
                  "// Every request's own pre-request script runs this after setting igh.only",
                  "// (local or cloud: skip in an environment of the other transport) and",
                  "// igh.needs / igh.what (a variable the request needs: skip while it is empty).",
                  "var only = pm.variables.get(\"igh.only\");",
                  "var transport = pm.environment.get(\"transport\");",
                  "if (only && transport && transport !== only) {",
                  "  console.log(\"Skipped: \" + pm.info.requestName + \" runs only in an environment with transport \" + only + \".\");",
                  "  pm.execution.skipRequest();",
                  "}",
                  "var needs = pm.variables.get(\"igh.needs\");",
                  "if (needs && !pm.variables.get(needs)) {",
                  "  console.log(\"Skipped: {{\" + needs + \"}} is empty (\" + pm.variables.get(\"igh.what\") + \"): set it in the environment (Getting started → House fills the device ids).\");",
                  "  pm.execution.skipRequest();",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "id": "157185b9-9933-40e0-87c0-8b2c034be166",
                "type": "text/javascript",
                "exec": [
                  "// Shared by every request's test script: save() writes the environment when it",
                  "// declares the key (an empty environment value hides a collection variable) and",
                  "// the collection; fill() only while empty; answer() is the JSON body or null.",
                  "",
                  "function save(key, value) {",
                  "  var text = value === undefined || value === null ? \"\" : String(value);",
                  "  if (pm.environment.has(key)) { pm.environment.set(key, text); }",
                  "  pm.collectionVariables.set(key, text);",
                  "}",
                  "function fill(key, value) {",
                  "  if (!pm.variables.get(key) && value !== undefined && value !== null && value !== \"\") { save(key, value); }",
                  "}",
                  "function answer() {",
                  "  try { return pm.response.json(); } catch (e) { return null; }",
                  "}",
                  "",
                  "var body = answer();",
                  "if (body && body.ca) {",
                  "  save(\"caFingerprint\", body.fingerprint);",
                  "  console.log(\"CA fingerprint (\" + body.algorithm + \"): \" + body.fingerprint);",
                  "  pm.visualizer.set(\"<p style='font-family:sans-serif'>Save this as ca.pem, then Settings → Certificates → CA certificates.<br>SHA-256 {{fingerprint}}</p><pre>{{ca}}</pre>\", { ca: body.ca, fingerprint: body.fingerprint });",
                  "}"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/gateway/tls",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "gateway",
                "tls"
              ]
            },
            "description": "Works on both transports, but the first time fetch it through the cloud environment: on the home network Postman must already trust the CA. Save `ca` as `ca.pem` (the Visualize tab shows it to copy), compare `fingerprint`, then select the file in Postman Settings → Certificates → CA certificates.\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)"
          },
          "response": []
        }
      ]
    }
  ],
  "auth": {
    "type": "bearer",
    "bearer": [
      {
        "key": "token",
        "value": "{{token}}",
        "type": "string"
      }
    ]
  },
  "event": [
    {
      "listen": "prerequest",
      "script": {
        "id": "6f708bd7-838c-4fdd-878d-6602c0a3ce79",
        "type": "text/javascript",
        "exec": [
          "// IGH home API: every request's own pre-request script sets what this",
          "// collection's test script checks (igh.expect, igh.code, igh.kind).",
          "pm.variables.set(\"igh.expect\", \"\");",
          "pm.variables.set(\"igh.code\", \"\");",
          "pm.variables.set(\"igh.kind\", \"api\");",
          "if (/<|igh-\\.local/.test(pm.variables.replaceIn(\"{{baseUrl}}\"))) {",
          "  console.warn(\"baseUrl has no gateway id: set serial in the environment (the gateway id, lower case).\");",
          "}"
        ]
      }
    },
    {
      "listen": "test",
      "script": {
        "id": "05b1a278-e980-4c06-87b3-55f76a58575c",
        "type": "text/javascript",
        "exec": [
          "// IGH home API: the status this request expects, and for a refusal the one",
          "// error shape { error: { code, message, appAction?, retryAfter? } }.",
          "var expected = String(pm.variables.get(\"igh.expect\") || \"\").split(\",\").filter(Boolean).map(Number);",
          "var kind = pm.variables.get(\"igh.kind\") || \"api\";",
          "var wanted = pm.variables.get(\"igh.code\") || \"\";",
          "var body = null;",
          "try { body = pm.response.json(); } catch (e) { body = null; }",
          "if (expected.length > 0) {",
          "  pm.test(\"status is \" + expected.join(\" or \"), function () {",
          "    pm.expect(expected).to.include(pm.response.code);",
          "  });",
          "}",
          "if (kind === \"api\" && pm.response.code >= 400) {",
          "  pm.test(\"the error is { error: { code, message } }\", function () {",
          "    pm.expect(body).to.be.an(\"object\");",
          "    pm.expect(body.error).to.be.an(\"object\");",
          "    pm.expect(body.error.code).to.be.a(\"string\");",
          "    pm.expect(body.error.message).to.be.a(\"string\");",
          "  });",
          "  if (body && body.error && typeof body.error === \"object\") {",
          "    console.log(\"IGH \" + pm.response.code + \" \" + body.error.code + \": \" + body.error.message);",
          "    if (body.error.appAction) { console.log(\"appAction (the app screen that does it): \" + body.error.appAction); }",
          "    if (body.error.retryAfter !== undefined) {",
          "      console.log(\"retryAfter: \" + body.error.retryAfter + \" s (Retry-After header: \" + (pm.response.headers.get(\"Retry-After\") || \"none\") + \")\");",
          "    }",
          "  }",
          "}",
          "if (kind === \"api\" && pm.response.code < 300 && body && !/\\/about$/.test(pm.request.url.getPath())) {",
          "  pm.test(\"the answer names the house\", function () { pm.expect(body.house).to.be.a(\"string\"); });",
          "}",
          "if (wanted) {",
          "  pm.test(\"error code is \" + wanted, function () {",
          "    var got = body && body.error ? (typeof body.error === \"string\" ? body.error : body.error.code) : undefined;",
          "    pm.expect(got).to.eql(wanted);",
          "  });",
          "}",
          "if (pm.response.code === 401 && !pm.variables.get(\"token\")) {",
          "  console.warn(\"{{token}} is empty: run one of the Authorization folders first.\");",
          "}"
        ]
      }
    }
  ],
  "variable": [
    {
      "key": "deviceId",
      "value": "",
      "type": "string",
      "description": "A device id (House fills it: the first light)."
    },
    {
      "key": "deviceName",
      "value": "",
      "type": "string",
      "description": "Its current name (for the rename request)."
    },
    {
      "key": "unitRef",
      "value": "",
      "type": "string",
      "description": "deviceId's unit: the third part of the device id."
    },
    {
      "key": "lightId",
      "value": "",
      "type": "string",
      "description": "The first light."
    },
    {
      "key": "dimmerId",
      "value": "",
      "type": "string",
      "description": "The first dimmer."
    },
    {
      "key": "shutterId",
      "value": "",
      "type": "string",
      "description": "The first shutter."
    },
    {
      "key": "acId",
      "value": "",
      "type": "string",
      "description": "The first air conditioner."
    },
    {
      "key": "irId",
      "value": "",
      "type": "string",
      "description": "The first IR remote."
    },
    {
      "key": "irKey",
      "value": "power",
      "type": "string",
      "description": "A key the IR remote learned."
    },
    {
      "key": "lockId",
      "value": "",
      "type": "string",
      "description": "The first lock."
    },
    {
      "key": "gateId",
      "value": "",
      "type": "string",
      "description": "The first gate."
    },
    {
      "key": "roomId",
      "value": "",
      "type": "string",
      "description": "The first room."
    },
    {
      "key": "roomName",
      "value": "",
      "type": "string",
      "description": "Its current name."
    },
    {
      "key": "sceneId",
      "value": "",
      "type": "string",
      "description": "The house's first scene."
    },
    {
      "key": "createdSceneId",
      "value": "",
      "type": "string",
      "description": "The test scene."
    },
    {
      "key": "sceneRevision",
      "value": "",
      "type": "string",
      "description": "Its revision."
    },
    {
      "key": "automationId",
      "value": "",
      "type": "string",
      "description": "The test automation."
    },
    {
      "key": "automationRevision",
      "value": "",
      "type": "string",
      "description": "Its revision."
    },
    {
      "key": "scheduleId",
      "value": "",
      "type": "string",
      "description": "The test schedule."
    },
    {
      "key": "scheduleRevision",
      "value": "",
      "type": "string",
      "description": "Its revision."
    },
    {
      "key": "intentId",
      "value": "",
      "type": "string",
      "description": "A writable intent of deviceId."
    },
    {
      "key": "intentValue",
      "value": "",
      "type": "string",
      "description": "Its value, as JSON."
    },
    {
      "key": "sensorId",
      "value": "",
      "type": "string",
      "description": "The first sensor."
    },
    {
      "key": "caFingerprint",
      "value": "",
      "type": "string",
      "description": "The gateway CA's SHA-256 fingerprint."
    },
    {
      "key": "tokenKind",
      "value": "",
      "type": "string",
      "description": "key, agent, session or oauth."
    },
    {
      "key": "pairCode",
      "value": "",
      "type": "string",
      "description": "The device flow's device code."
    },
    {
      "key": "pairUserCode",
      "value": "",
      "type": "string",
      "description": "Its matching code."
    },
    {
      "key": "pairInterval",
      "value": "5",
      "type": "string",
      "description": "Seconds between polls."
    },
    {
      "key": "pairPolls",
      "value": "0",
      "type": "string",
      "description": "Polls so far."
    },
    {
      "key": "mcpCode",
      "value": "",
      "type": "string",
      "description": "check_access's code."
    },
    {
      "key": "oauthResourceMetadata",
      "value": "",
      "type": "string",
      "description": "The protected-resource metadata URL."
    },
    {
      "key": "oauthIssuer",
      "value": "",
      "type": "string",
      "description": "The authorization server."
    },
    {
      "key": "oauthAuthUrl",
      "value": "",
      "type": "string"
    },
    {
      "key": "oauthTokenUrl",
      "value": "",
      "type": "string"
    },
    {
      "key": "oauthRegisterUrl",
      "value": "",
      "type": "string"
    },
    {
      "key": "oauthRevokeUrl",
      "value": "",
      "type": "string"
    },
    {
      "key": "oauthScope",
      "value": "igh",
      "type": "string"
    },
    {
      "key": "oauthClientId",
      "value": "",
      "type": "string",
      "description": "Postman's registered client id."
    }
  ]
}
