{
  "info": {
    "name": "Verse Engine API",
    "_postman_id": "7f4c2a10-9b3e-4d5f-8a21-verse2026api01",
    "description": "Turn a 3D file into a signed, streamed web walkthrough — and own, protect, version, review and monitor it.\n\n71 requests mirroring docs/api.md, grouped per section, generated from scripts/api-routes.mjs (scripts/gen-postman.mjs). Set `baseUrl` (defaults to the hosted service) and, in keyed mode, `apiKey`. Run **Create account** once: its token is saved to {{token}} and the owner-only requests use it. Upload a model: {{jobId}}/{{sceneId}} are saved for every later request. Every app error carries `X-Verse-Error: 1` + {\"error\"}; an error without that header came from the network edge.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "item": [
    {
      "name": "Ingest",
      "description": "Upload a 3D file, poll the compile, get a signed streaming link. docs/api.md#ingest",
      "item": [
        {
          "name": "Limits (what you can upload)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/v1/limits",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "limits"
              ]
            },
            "description": "Public, cached 60 s. {limits (enforced), recommended, textures, timing, queue, storage, notify}. The app's upload card and preflight are driven by this body."
          },
          "response": []
        },
        {
          "name": "Upload model (.glb)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('accepted (202)', () => pm.response.to.have.status(202));",
                  "const j = pm.response.json();",
                  "if (j.job) { pm.collectionVariables.set('jobId', j.job); pm.collectionVariables.set('sceneId', j.job); console.log('job id saved:', j.job); }"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "model/gltf-binary"
              },
              {
                "key": "X-Verse-Name",
                "value": "postman-demo",
                "description": "Optional id prefix: [a-z0-9-_]"
              },
              {
                "key": "X-Verse-Bake",
                "value": "preview",
                "description": "Optional (2.7): preview | final — bake the lighting of a live-lit scene into lightmaps before compiling",
                "disabled": true
              },
              {
                "key": "X-Api-Key",
                "value": "{{apiKey}}",
                "description": "Only needed in keyed mode"
              }
            ],
            "body": {
              "mode": "file",
              "file": {
                "src": ""
              }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/ingest",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "ingest"
              ]
            },
            "description": "POST the raw .glb bytes (no multipart — pick your file under Body → binary). Draco meshes and KTX2 textures inside the GLB are supported. Max 512 MB. Returns 202 {job} immediately; compilation continues server-side (~8 s per source MB). Add `Authorization: Bearer {{token}}` to own the scene; `X-Verse-Bake: preview|final` bakes a live-lit scene's lighting on the server first (docs/api.md § Bake for me). The test script stores the job id in {{jobId}} and {{sceneId}}."
          },
          "response": []
        },
        {
          "name": "Upload splat capture (.ply / .splat)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('accepted (202)', () => pm.response.to.have.status(202));",
                  "const j = pm.response.json();",
                  "if (j.job) { pm.collectionVariables.set('jobId', j.job); pm.collectionVariables.set('sceneId', j.job); }"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/octet-stream"
              },
              {
                "key": "X-Verse-Kind",
                "value": "splat",
                "description": "Forces splat handling (otherwise sniffed)"
              },
              {
                "key": "X-Verse-Name",
                "value": "postman-splat"
              },
              {
                "key": "X-Api-Key",
                "value": "{{apiKey}}",
                "description": "Only needed in keyed mode"
              }
            ],
            "body": {
              "mode": "file",
              "file": {
                "src": ""
              }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/ingest",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "ingest"
              ]
            },
            "description": "Gaussian-splat capture (antimatter15 .splat or INRIA 3DGS binary .ply) → streamed .vsp. Same limits and polling; stage is `packing`; the finished record carries vspUrl and stats {splats, chunks}."
          },
          "response": []
        },
        {
          "name": "Upload point cloud (.ply / .las / .laz / .e57)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('accepted (202)', () => pm.response.to.have.status(202));",
                  "const j = pm.response.json();",
                  "if (j.job) { pm.collectionVariables.set('jobId', j.job); pm.collectionVariables.set('sceneId', j.job); }"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/octet-stream"
              },
              {
                "key": "X-Verse-Kind",
                "value": "cloud",
                "description": "Forces point-cloud handling (otherwise sniffed: LASF / ASTM-E57 magic, a .ply without the 3DGS fields)"
              },
              {
                "key": "X-Verse-Name",
                "value": "postman-cloud"
              },
              {
                "key": "X-Api-Key",
                "value": "{{apiKey}}",
                "description": "Only needed in keyed mode"
              }
            ],
            "body": {
              "mode": "file",
              "file": {
                "src": ""
              }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/ingest",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "ingest"
              ]
            },
            "description": "Point cloud (xyz .ply, LAS 1.2–1.4 point formats 0–10, LAZ, E57; ≤ 200 M points) → streamed .vpc octree (docs/vpc-format.md), both viewer tiers. Same limits and polling; stage is `packing`; the finished record carries vpcUrl and stats {points, nodes}."
          },
          "response": []
        },
        {
          "name": "Upload IFC building model (.ifc)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('accepted (202)', () => pm.response.to.have.status(202));",
                  "const j = pm.response.json();",
                  "if (j.job) { pm.collectionVariables.set('jobId', j.job); pm.collectionVariables.set('sceneId', j.job); }"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/octet-stream"
              },
              {
                "key": "X-Verse-Kind",
                "value": "ifc",
                "description": "Forces IFC handling (otherwise sniffed: the ISO-10303-21 header)"
              },
              {
                "key": "X-Verse-Name",
                "value": "postman-ifc"
              },
              {
                "key": "X-Api-Key",
                "value": "{{apiKey}}",
                "description": "Only needed in keyed mode"
              }
            ],
            "body": {
              "mode": "file",
              "file": {
                "src": ""
              }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/ingest",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "ingest"
              ]
            },
            "description": "IFC2x3 / IFC4 (STEP) → converted on the server (stage `converting`, web-ifc) into one object per product with its GlobalId, IfcType, Name, storey and property sets, then compiled and streamed in both tiers. The finished record carries propsUrl (`<id>.props.json`: the object index the viewer's properties panel and the embed SDK's getProperties/setVisible read) and stats.products."
          },
          "response": []
        },
        {
          "name": "Upload USD scene (.usdz / .usd / .usda / .usdc)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('accepted (202)', () => pm.response.to.have.status(202));",
                  "const j = pm.response.json();",
                  "if (j.job) { pm.collectionVariables.set('jobId', j.job); pm.collectionVariables.set('sceneId', j.job); }"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/octet-stream"
              },
              {
                "key": "X-Verse-Kind",
                "value": "usd",
                "description": "Forces USD handling (otherwise sniffed: #usda, PXR-USDC, a usdz zip)"
              },
              {
                "key": "X-Verse-Name",
                "value": "postman-usd"
              },
              {
                "key": "X-Api-Key",
                "value": "{{apiKey}}",
                "description": "Only needed in keyed mode"
              }
            ],
            "body": {
              "mode": "file",
              "file": {
                "src": ""
              }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/ingest",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "ingest"
              ]
            },
            "description": "A single self-contained USD / USDZ (tinyusdz): Mesh prims with UsdPreviewSurface materials and embedded textures become objects (prim path as the id); references to files outside the upload are not resolved. Same polling; stage `converting`; the finished record carries propsUrl."
          },
          "response": []
        },
        {
          "name": "Poll compile job",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));",
                  "const j = pm.response.json();",
                  "console.log('status:', j.status, j.stage ?? '', j.url ?? '');"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/v1/ingest/{{jobId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "ingest",
                "{{jobId}}"
              ]
            },
            "description": "Poll every few seconds until status leaves `compiling`. While compiling: {status, stage, progress, etaS, etaText, queue?, attempt, preflight}. Done: {status:'done', url, vpkUrl|vspUrl, stageUrl?, planUrl?, stats, signed, verify}. Error: {status:'error', error, errorInfo}. 404 after the 2 h job-record retention (the compiled scene itself lives much longer)."
          },
          "response": []
        },
        {
          "name": "Attach contact (email me when it's live)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Asha Rao\",\n  \"phone\": \"+91 98765 43210\",\n  \"email\": \"asha@example.com\",\n  \"consent\": true\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/ingest/{{jobId}}/contact",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "ingest",
                "{{jobId}}",
                "contact"
              ]
            },
            "description": "name (2–80), email, phone (7–15 digits) and consent:true are all required; ≤4 KB. → {ok, emailConfigured, emailed, notify: 'when-live' | 'sent' | 'follow-up'}. 400 {error, field} on invalid input; 409 a different address after the email went out; 12/hour/IP. The contact is never served or logged in full."
          },
          "response": []
        },
        {
          "name": "Resumable: begin upload",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('begun (200)', () => pm.response.to.have.status(200));",
                  "const j = pm.response.json();",
                  "if (j.uploadId) { pm.collectionVariables.set('uploadId', j.uploadId); console.log('upload id saved:', j.uploadId); }"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              },
              {
                "key": "X-Api-Key",
                "value": "{{apiKey}}",
                "description": "Only needed in keyed mode"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"size\": 0,\n  \"name\": \"postman-resumable\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/ingest/begin",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "ingest",
                "begin"
              ]
            },
            "description": "Start a resumable upload for a large file: set `size` to the exact byte length (≤512 MB; ≤2048 MB with \"prepared\": true for a verse-prepare'd file; add \"kind\": \"splat\" for captures or \"cloud\" for point clouds). Rate/quota/storage gates run here. Returns {uploadId, received: 0, size}. Then ordered **Upload part** PUTs and **Commit**. Expires after 2 h idle."
          },
          "response": []
        },
        {
          "name": "Resumable: upload part",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('part accepted (200) or resumable 409', () => pm.expect([200, 409]).to.include(pm.response.code));",
                  "console.log(pm.response.json());"
                ]
              }
            }
          ],
          "request": {
            "method": "PUT",
            "header": [
              {
                "key": "Content-Range",
                "value": "bytes 0-1048575/0",
                "description": "bytes <start>-<end>/<total>; start must equal the server's current `received`"
              },
              {
                "key": "Content-Type",
                "value": "application/octet-stream"
              }
            ],
            "body": {
              "mode": "file",
              "file": {
                "src": ""
              }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/ingest/part/{{uploadId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "ingest",
                "part",
                "{{uploadId}}"
              ]
            },
            "description": "Append one part — strictly ordered, one at a time. 200 {uploadId, received, size}. 409 = out of order; the body carries the authoritative `received` offset to resume from."
          },
          "response": []
        },
        {
          "name": "Resumable: upload status",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/v1/ingest/part/{{uploadId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "ingest",
                "part",
                "{{uploadId}}"
              ]
            },
            "description": "Where to resume after a lost connection: {uploadId, received, size}; a committed upload also carries `job`."
          },
          "response": []
        },
        {
          "name": "Resumable: commit upload",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('accepted (202)', () => pm.response.to.have.status(202));",
                  "const j = pm.response.json();",
                  "if (j.job) { pm.collectionVariables.set('jobId', j.job); pm.collectionVariables.set('sceneId', j.job); }"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "X-Api-Key",
                "value": "{{apiKey}}",
                "description": "Only needed in keyed mode"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/ingest/commit/{{uploadId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "ingest",
                "commit",
                "{{uploadId}}"
              ]
            },
            "description": "Finish the upload → the same 202 {job} as the one-shot POST; poll as usual. 409 = incomplete (body carries received/size). Idempotent. Send `Authorization: Bearer {{token}}` to own the scene."
          },
          "response": []
        }
      ]
    },
    {
      "name": "Accounts & Studio",
      "description": "Token-only accounts: ownership, analytics, /studio. The token is shown ONCE. docs/api.md#accounts-studio",
      "item": [
        {
          "name": "Create account",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('created (200)', () => pm.response.to.have.status(200));",
                  "const j = pm.response.json();",
                  "if (j.token) { pm.collectionVariables.set('token', j.token); console.log('account token saved to {{token}} — it is shown ONCE'); }"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Postman Demo\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/account",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "account"
              ]
            },
            "description": "→ {accountId, name, createdAt, token}. The token is the credential and is shown only here (3/day/IP). The test script stores it in {{token}}."
          },
          "response": []
        },
        {
          "name": "Login (sets verse_session cookie)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"token\": \"{{token}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/account/login",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "account",
                "login"
              ]
            },
            "description": "Sets the httpOnly verse_session cookie (value = the token) → {accountId, name}. How /studio authenticates in a browser; API clients can simply send the Bearer header instead."
          },
          "response": []
        },
        {
          "name": "Whoami",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{token}}",
                "description": "Account token from 'Create account' (or sign in at /studio)"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/account",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "account"
              ]
            },
            "description": "→ {accountId, name, createdAt}. 401 without auth."
          },
          "response": []
        },
        {
          "name": "List owned scenes",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{token}}",
                "description": "Account token from 'Create account' (or sign in at /studio)"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/account/scenes",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "account",
                "scenes"
              ]
            },
            "description": "→ {accountId, scenes:[{id, kind, url, fileUrl, posterUrl?, visibility, sizeBytes, created, stats:{opens, uniqueSessions, totalMinutes}, reviews:{open, total}}]}, newest first."
          },
          "response": []
        },
        {
          "name": "Account stats rollup",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{token}}",
                "description": "Account token from 'Create account' (or sign in at /studio)"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/account/stats",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "account",
                "stats"
              ]
            },
            "description": "→ {scenes, opens, uniqueSessions, totalMinutes, last7d:[{day, opens}×7]}."
          },
          "response": []
        },
        {
          "name": "List webhooks",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{token}}",
                "description": "Account token from 'Create account' (or sign in at /studio)"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/account/webhooks",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "account",
                "webhooks"
              ]
            },
            "description": "→ {accountId, events, webhooks:[{id, url, events, created, secretHint, last?}]}. The secret is never returned after creation."
          },
          "response": []
        },
        {
          "name": "Create a webhook",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{token}}",
                "description": "Account token from 'Create account' (or sign in at /studio)"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"url\": \"https://example.com/verse-hook\",\n  \"events\": [\n    \"scene.ready\",\n    \"comment.added\"\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/account/webhooks",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "account",
                "webhooks"
              ]
            },
            "description": "{url, events?, secret?} → the hook + `secret` (shown ONCE; generated when omitted). Events: scene.ready, scene.failed, comment.added, version.promoted, config.changed. 10 per account."
          },
          "response": []
        },
        {
          "name": "Delete a webhook",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{token}}",
                "description": "Account token from 'Create account' (or sign in at /studio)"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/account/webhooks/{{sceneId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "account",
                "webhooks",
                "{{sceneId}}"
              ]
            },
            "description": "→ {deleted}. Its delivery log goes with it."
          },
          "response": []
        },
        {
          "name": "Delivery log (last 50)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{token}}",
                "description": "Account token from 'Create account' (or sign in at /studio)"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/account/webhooks/{{sceneId}}/deliveries",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "account",
                "webhooks",
                "{{sceneId}}",
                "deliveries"
              ]
            },
            "description": "→ {id, deliveries:[{id, event, t, ok, attempts:[{t, ms, status, error?}]}]}, newest first. No secrets, no receiver bodies."
          },
          "response": []
        },
        {
          "name": "Logout",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('2xx', () => pm.expect(pm.response.code).to.be.within(200, 299));"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/v1/account/logout",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "account",
                "logout"
              ]
            },
            "description": "Clears the verse_session cookie."
          },
          "response": []
        }
      ]
    },
    {
      "name": "Scenes",
      "description": "Owner-only scene management: claim, visibility, share tokens, stats, delete. docs/api.md#scenes",
      "item": [
        {
          "name": "Claim an unowned scene",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{token}}",
                "description": "Account token from 'Create account' (or sign in at /studio)"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/scenes/{{sceneId}}/claim",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "scenes",
                "{{sceneId}}",
                "claim"
              ]
            },
            "description": "Take ownership of an anonymous scene → {id, owner}. 409 if someone else owns it."
          },
          "response": []
        },
        {
          "name": "Set visibility",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{token}}",
                "description": "Account token from 'Create account' (or sign in at /studio)"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"visibility\": \"private\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/scenes/{{sceneId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "scenes",
                "{{sceneId}}"
              ]
            },
            "description": "\"public\" | \"unlisted\" (default) | \"private\". Private: every artifact and the review comments need the owner's auth or a share token; strangers get 404 + X-Verse-Private: 1. Private scenes never redirect to a CDN."
          },
          "response": []
        },
        {
          "name": "Mint share token",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));",
                  "const j = pm.response.json();",
                  "if (j.shareToken) { pm.collectionVariables.set('shareToken', j.shareToken); pm.collectionVariables.set('shareId', j.id); console.log('share token saved — shown ONCE'); }"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{token}}",
                "description": "Account token from 'Create account' (or sign in at /studio)"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/scenes/{{sceneId}}/share",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "scenes",
                "{{sceneId}}",
                "share"
              ]
            },
            "description": "→ {id, suffix, created, shareToken}. The 32-hex shareToken is shown ONCE (stored hashed); up to 32 per scene. Use it as ?share=<token> on links/artifact URLs or as X-Verse-Share. The test script stores it in {{shareToken}} and its id in {{shareId}}."
          },
          "response": []
        },
        {
          "name": "List share links",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{token}}",
                "description": "Account token from 'Create account' (or sign in at /studio)"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/scenes/{{sceneId}}/share",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "scenes",
                "{{sceneId}}",
                "share"
              ]
            },
            "description": "→ {scene, shares:[{id, suffix, created}]} — never the tokens themselves."
          },
          "response": []
        },
        {
          "name": "Fetch private artifact with share token",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('206', () => pm.response.to.have.status(206));"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Range",
                "value": "bytes=0-1023"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/ingest/{{sceneId}}.vpk?share={{shareToken}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "ingest",
                "{{sceneId}}.vpk"
              ],
              "query": [
                {
                  "key": "share",
                  "value": "{{shareToken}}"
                }
              ]
            },
            "description": "A ranged read of the compiled scene using the share token (206, Cache-Control: private, no-store). Without the token a private scene answers 404 + X-Verse-Private: 1."
          },
          "response": []
        },
        {
          "name": "AR Quick Look package (iOS)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('206', () => pm.response.to.have.status(206));"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Range",
                "value": "bytes=0-1023"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/ingest/{{sceneId}}.usdz",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "ingest",
                "{{sceneId}}.usdz"
              ]
            },
            "description": "(2.7) The scene's iOS AR Quick Look package, model/vnd.usdz+zip, built from the prepared GLB at compile time for product-scale scenes under the AR caps (150 MB source, 5 M triangles; never an interior). Same visibility and ?v= rules as the scene; a private scene needs ?share=. 404 when the finished record has no arUrl (its `ar.skipped` says why). docs/guides/view-in-ar.md"
          },
          "response": []
        },
        {
          "name": "Revoke share link",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{token}}",
                "description": "Account token from 'Create account' (or sign in at /studio)"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/scenes/{{sceneId}}/share/{{shareId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "scenes",
                "{{sceneId}}",
                "share",
                "{{shareId}}"
              ]
            },
            "description": "→ {revoked}. The holder gets 404s from then on; other links are unaffected."
          },
          "response": []
        },
        {
          "name": "Per-scene stats",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{token}}",
                "description": "Account token from 'Create account' (or sign in at /studio)"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/scenes/{{sceneId}}/stats",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "scenes",
                "{{sceneId}}",
                "stats"
              ]
            },
            "description": "→ {id, opens, uniqueSessions, totalMinutes, last7d}. Owner-only (401 unauth, 404 for others)."
          },
          "response": []
        },
        {
          "name": "Heatmap + funnel",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{token}}",
                "description": "Account token from 'Create account' (or sign in at /studio)"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/scenes/{{sceneId}}/heatmap",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "scenes",
                "{{sceneId}}",
                "heatmap"
              ]
            },
            "description": "Owner-only. Where visitors stood and where they waited for bytes, as a grid over the floor plan, plus the funnel: {id, plan, sampleEveryMs, sessions, samples, grid:{ox, oz, cell, nx, nz, stood[], stalled[], max, maxStalled}, funnel:{opens, entered, walked, s30, m2}}. Cached a minute; ?fresh=1 refolds."
          },
          "response": []
        },
        {
          "name": "Current version of a scene",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/v1/scenes/{{sceneId}}/current",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "scenes",
                "{{sceneId}}",
                "current"
              ]
            },
            "description": "Anyone who may read the scene (private: owner or ?share=). → {id, version, versions, file} — `file` is the viewer's cache key (`ingest/<id>.vpk` or `…?v=<N>`). no-store."
          },
          "response": []
        },
        {
          "name": "Delete scene",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{token}}",
                "description": "Account token from 'Create account' (or sign in at /studio)"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/scenes/{{sceneId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "scenes",
                "{{sceneId}}"
              ]
            },
            "description": "Owner-only. Removes the artifact, sidecars, poster, review file and versions; every share link stops working."
          },
          "response": []
        },
        {
          "name": "Configuration schema",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/v1/scenes/{{sceneId}}/config",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "scenes",
                "{{sceneId}}",
                "config"
              ]
            },
            "description": "Public (mirrors the scene's visibility) → {scene, config:{version, title?, currency?, base?, groups:[{id, name, default, options:[{id, name, variant?, materials?, hide?, sku?, price?, swatch?}]}]}, updated}; 204 when the scene has none."
          },
          "response": []
        },
        {
          "name": "Save the configuration schema",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "PUT",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{token}}",
                "description": "Account token from 'Create account' (or sign in at /studio)"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"currency\": \"USD\",\n  \"groups\": [\n    {\n      \"id\": \"fabric\",\n      \"name\": \"Fabric\",\n      \"options\": [\n        {\n          \"id\": \"oak\",\n          \"name\": \"Oak\",\n          \"variant\": \"Oak\",\n          \"price\": 0\n        },\n        {\n          \"id\": \"walnut\",\n          \"name\": \"Walnut\",\n          \"variant\": \"Walnut\",\n          \"price\": 120,\n          \"sku\": \"SOFA-WAL\"\n        }\n      ]\n    }\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/scenes/{{sceneId}}/config",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "scenes",
                "{{sceneId}}",
                "config"
              ]
            },
            "description": "Owner-only. Validated by config.js (the viewer's own code); `variant` names must exist in the scene; `{groups: []}` clears → {scene, config, warnings?}. Fires `config.changed`."
          },
          "response": []
        },
        {
          "name": "Upload the ground heightfield",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "PUT",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{token}}",
                "description": "Account token from 'Create account' (or sign in at /studio)"
              },
              {
                "key": "Content-Type",
                "value": "image/png"
              }
            ],
            "body": {
              "mode": "file",
              "file": {
                "src": ""
              }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/scenes/{{sceneId}}/ground",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "scenes",
                "{{sceneId}}",
                "ground"
              ]
            },
            "description": "Owner-only. A PNG (≤ 4096², ≤ 8 MB; red = height) stored as the scene's `<id>.ground.png` sidecar (same visibility / version / delete rules as the poster) → {scene, path, url, width, height, bytes}. The exterior kit's `ground.heightfield` points at it."
          },
          "response": []
        },
        {
          "name": "List a scene's experiences",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{token}}",
                "description": "Account token from 'Create account' (or sign in at /studio)"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/scenes/{{sceneId}}/experiences",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "scenes",
                "{{sceneId}}",
                "experiences"
              ]
            },
            "description": "Owner-only → {scene, default, experiences:[{id, name, created, updated, path, url, author}]}, newest first."
          },
          "response": []
        },
        {
          "name": "Set the share link's default experience",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "PUT",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{token}}",
                "description": "Account token from 'Create account' (or sign in at /studio)"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"experience\": \"exp-0123456789\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/scenes/{{sceneId}}/experience",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "scenes",
                "{{sceneId}}",
                "experience"
              ]
            },
            "description": "Owner-only. `experience` names one of this scene's experiences, or null to clear → {scene, experience}."
          },
          "response": []
        },
        {
          "name": "The share link's default experience",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/v1/scenes/{{sceneId}}/experience",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "scenes",
                "{{sceneId}}",
                "experience"
              ]
            },
            "description": "Public (mirrors the scene's visibility) → {id, path, experience}; 204 when none is set. What the viewer and embeds layer over a plainly loaded scene."
          },
          "response": []
        },
        {
          "name": "A scene's media surfaces + ambient sounds",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/v1/scenes/{{sceneId}}/media",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "scenes",
                "{{sceneId}}",
                "media"
              ]
            },
            "description": "Public (mirrors the scene's visibility) → {scene, media:[{material, url, loop, emissive, audio}], audio:[{pos, url, radius, loop, gain}]}; 204 when the scene has none. No-store."
          },
          "response": []
        },
        {
          "name": "Set a scene's media table",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "PUT",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{token}}",
                "description": "Account token from 'Create account' (or sign in at /studio)"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"media\": [\n    {\n      \"material\": 3,\n      \"url\": \"/ingest/{{sceneId}}.media/1.mp4\",\n      \"loop\": true,\n      \"emissive\": 1.5,\n      \"audio\": false\n    }\n  ],\n  \"audio\": [\n    {\n      \"pos\": [\n        2,\n        1.2,\n        -1\n      ],\n      \"url\": \"/ingest/{{sceneId}}.media/2.mp3\",\n      \"radius\": 6,\n      \"loop\": true,\n      \"gain\": 0.8\n    }\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/scenes/{{sceneId}}/media",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "scenes",
                "{{sceneId}}",
                "media"
              ]
            },
            "description": "Owner-only. Replaces the table (≤ 4 surfaces, ≤ 16 emitters, same-origin URLs) and writes it into the scene's staging manifest → the normalized table."
          },
          "response": []
        },
        {
          "name": "Upload a media file (video / audio)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{token}}",
                "description": "Account token from 'Create account' (or sign in at /studio)"
              },
              {
                "key": "Content-Type",
                "value": "video/mp4"
              }
            ],
            "body": {
              "mode": "file",
              "file": {
                "src": ""
              }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/scenes/{{sceneId}}/media/upload",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "scenes",
                "{{sceneId}}",
                "media",
                "upload"
              ]
            },
            "description": "Owner-only, raw body, container-sniffed: mp4 / webm video ≤ 200 MB, mp3 / m4a / ogg / wav / webm audio ≤ 20 MB → {path, url, bytes, kind, ext}; stored as ingest/<id>.media/<n>.<ext> under the scene's visibility."
          },
          "response": []
        }
      ]
    },
    {
      "name": "Scene versions",
      "description": "An owned streamed scene takes a new upload as a version; the share link never changes. docs/api.md#scene-versions-250",
      "item": [
        {
          "name": "Upload a new version (.glb)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('accepted (202)', () => pm.response.to.have.status(202));",
                  "const j = pm.response.json();",
                  "if (j.job) { pm.collectionVariables.set('jobId', j.job); }"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{token}}",
                "description": "Account token from 'Create account' (or sign in at /studio)"
              },
              {
                "key": "Content-Type",
                "value": "model/gltf-binary"
              },
              {
                "key": "X-Verse-Name",
                "value": "postman-v2",
                "description": "Optional"
              }
            ],
            "body": {
              "mode": "file",
              "file": {
                "src": ""
              }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/scenes/{{sceneId}}/versions",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "scenes",
                "{{sceneId}}",
                "versions"
              ]
            },
            "description": "Owner-only, streamed (.vpk) scenes. Compiles the upload as an ordinary job → 202 {id, job, pending: true, url}; when it finishes it is promoted: archived as version N, made current, diffed. Poll GET /v1/ingest/{{jobId}} — the finished record carries sceneId, version, diffSummary."
          },
          "response": []
        },
        {
          "name": "Attach an uploaded job as a version",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200 or 202', () => pm.expect([200, 202]).to.include(pm.response.code));"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{token}}",
                "description": "Account token from 'Create account' (or sign in at /studio)"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"job\": \"{{jobId}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/scenes/{{sceneId}}/versions",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "scenes",
                "{{sceneId}}",
                "versions"
              ]
            },
            "description": "Attach a job the same account uploaded through /v1/ingest — done (200 {version}) or still compiling (202 {pending: true}, promoted when done). The job stops being a scene of its own."
          },
          "response": []
        },
        {
          "name": "List versions",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{token}}",
                "description": "Account token from 'Create account' (or sign in at /studio)"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/scenes/{{sceneId}}/versions",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "scenes",
                "{{sceneId}}",
                "versions"
              ]
            },
            "description": "→ {id, current, versions:[{n, created, sizeBytes, tris, clusters, materials, textures, name, bounds, variants, current, diffSummary}], pending?, lastError?} — oldest first."
          },
          "response": []
        },
        {
          "name": "Diff a version against the one before",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{token}}",
                "description": "Account token from 'Create account' (or sign in at /studio)"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/scenes/{{sceneId}}/versions/{{versionN}}/diff",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "scenes",
                "{{sceneId}}",
                "versions",
                "{{versionN}}",
                "diff"
              ]
            },
            "description": "→ {from, to, summary, triangles/clusters/sizeBytes {before, after, delta}, bounds, materials {added, removed, changed}, textures}. Computed from the pre-compile GLB scan, the compiler's stats and the staging manifest."
          },
          "response": []
        },
        {
          "name": "Make a version current (rollback / forward)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{token}}",
                "description": "Account token from 'Create account' (or sign in at /studio)"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/scenes/{{sceneId}}/versions/{{versionN}}/activate",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "scenes",
                "{{sceneId}}",
                "versions",
                "{{versionN}}",
                "activate"
              ]
            },
            "description": "The scene's names serve version n → {id, current, changed}. The share link never changes; the viewer fetches the version under its own ?v= cache key."
          },
          "response": []
        },
        {
          "name": "Download a version's .vpk",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200 or 206', () => pm.expect([200, 206]).to.include(pm.response.code));"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{token}}",
                "description": "Account token from 'Create account' (or sign in at /studio)"
              },
              {
                "key": "Range",
                "value": "bytes=0-1023"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/scenes/{{sceneId}}/versions/{{versionN}}/file",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "scenes",
                "{{sceneId}}",
                "versions",
                "{{versionN}}",
                "file"
              ]
            },
            "description": "Owner-only. That version's compiled scene (Range supported) — Studio's \"Open v2\"."
          },
          "response": []
        },
        {
          "name": "A version's staging manifest",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{token}}",
                "description": "Account token from 'Create account' (or sign in at /studio)"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/scenes/{{sceneId}}/versions/{{versionN}}/stage",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "scenes",
                "{{sceneId}}",
                "versions",
                "{{versionN}}",
                "stage"
              ]
            },
            "description": "Owner-only. That version's .stage.json (spawn, tour, bounds, kind, view)."
          },
          "response": []
        },
        {
          "name": "Delete an archived version",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{token}}",
                "description": "Account token from 'Create account' (or sign in at /studio)"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/scenes/{{sceneId}}/versions/{{versionN}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "scenes",
                "{{sceneId}}",
                "versions",
                "{{versionN}}"
              ]
            },
            "description": "Never the current one (409). Numbers are never reused."
          },
          "response": []
        }
      ]
    },
    {
      "name": "Review comments",
      "description": "Comments pinned in 3D on a shared scene. Private scenes: add ?share={{shareToken}} or X-Verse-Share. docs/api.md#review-comments",
      "item": [
        {
          "name": "Post a comment",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));",
                  "const j = pm.response.json();",
                  "if (j.id) pm.collectionVariables.set('commentId', j.id);"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"pos\": [\n    1.2,\n    0.9,\n    -0.4\n  ],\n  \"text\": \"Can we see this wall in the oak finish?\",\n  \"author\": \"Priya\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/review/{{sceneId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "review",
                "{{sceneId}}"
              ]
            },
            "description": "Anonymous; 60/hour/IP. pos = world position [x,y,z] (the viewer gets it from a GPU pick), text ≤ 500 chars, author ≤ 40 optional. → {id, t}. 413 at 200 comments/scene. The test script stores {{commentId}}."
          },
          "response": []
        },
        {
          "name": "List comments",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/v1/review/{{sceneId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "review",
                "{{sceneId}}"
              ]
            },
            "description": "→ {scene, comments:[{id, t, pos, text, author?, resolved}]}, oldest first, resolved included. 404 for a private scene without access."
          },
          "response": []
        },
        {
          "name": "Resolve a comment (owner)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{token}}",
                "description": "Account token from 'Create account' (or sign in at /studio)"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/review/{{sceneId}}/{{commentId}}/resolve",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "review",
                "{{sceneId}}",
                "{{commentId}}",
                "resolve"
              ]
            },
            "description": "→ {id, resolved: true}. Owner of the scene only."
          },
          "response": []
        },
        {
          "name": "Delete a comment (owner)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "DELETE",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{token}}",
                "description": "Account token from 'Create account' (or sign in at /studio)"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/review/{{sceneId}}/{{commentId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "review",
                "{{sceneId}}",
                "{{commentId}}"
              ]
            },
            "description": "→ {deleted}. Owner of the scene only."
          },
          "response": []
        }
      ]
    },
    {
      "name": "Publishing",
      "description": "Experiences, tours, posters: each → {path, url}. 30/hour/IP. docs/api.md#publishing-experiences-tours-posters",
      "item": [
        {
          "name": "Publish experience",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));",
                  "console.log(pm.response.json().url);"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"Showroom\",\n  \"scene\": \"ingest/{{sceneId}}.vpk\",\n  \"spawn\": {\n    \"position\": [\n      0,\n      1.6,\n      3\n    ],\n    \"yaw\": 180\n  },\n  \"hotspots\": [\n    {\n      \"id\": \"front\",\n      \"label\": \"Front\",\n      \"position\": [\n        0,\n        1.2,\n        3\n      ],\n      \"look\": [\n        0,\n        0.5,\n        0\n      ]\n    }\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/experience",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "experience"
              ]
            },
            "description": "A .verse.json manifest (docs/experience-format.md), ≤128 KB, `scene` required → {path, url} — a ?experience= link."
          },
          "response": []
        },
        {
          "name": "Read experience",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/v1/experience/{{sceneId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "experience",
                "{{sceneId}}"
              ]
            },
            "description": "→ {id, path, experience}: the current manifest, no-store (edits read through here; the static ingest/ copy is immutable). Mirrors the scene's visibility."
          },
          "response": []
        },
        {
          "name": "Update experience",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "PUT",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{token}}",
                "description": "Account token from 'Create account' (or sign in at /studio)"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"version\": 2,\n  \"name\": \"postman tour\",\n  \"scene\": \"ingest/{{sceneId}}.vpk\",\n  \"hotspots\": [\n    {\n      \"id\": \"a\",\n      \"title\": \"Entry\",\n      \"position\": [\n        0,\n        1.6,\n        3\n      ],\n      \"look\": [\n        0,\n        1.2,\n        0\n      ],\n      \"anchor\": [\n        0,\n        1,\n        0\n      ]\n    }\n  ],\n  \"tour\": {\n    \"steps\": [\n      {\n        \"hotspot\": \"a\",\n        \"eye\": [\n          0,\n          1.6,\n          3\n        ],\n        \"look\": [\n          0,\n          1.2,\n          0\n        ],\n        \"dwell\": 3,\n        \"narration\": \"Welcome\"\n      }\n    ]\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/experience/{{sceneId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "experience",
                "{{sceneId}}"
              ]
            },
            "description": "Owner-only, in place, ≤128 KB → {id, path, url}. A stranger sees 404."
          },
          "response": []
        },
        {
          "name": "Upload narration clip",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{token}}",
                "description": "Account token from 'Create account' (or sign in at /studio)"
              },
              {
                "key": "Content-Type",
                "value": "audio/mpeg"
              }
            ],
            "body": {
              "mode": "file",
              "file": {
                "src": ""
              }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/experience/{{sceneId}}/media",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "experience",
                "{{sceneId}}",
                "media"
              ]
            },
            "description": "Owner-only; raw mp3/m4a/ogg/wav/webm ≤ 2 MB, container-sniffed → {path, url, bytes} (ingest/exp-<id>-media-<n>.<ext>). Reference it from a tour step's `audio`."
          },
          "response": []
        },
        {
          "name": "Publish camera tour",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));",
                  "console.log(pm.response.json().url);"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"postman tour\",\n  \"scene\": \"ingest/{{sceneId}}.vpk\",\n  \"samples\": [\n    [\n      0,\n      0,\n      1.6,\n      3,\n      0,\n      0,\n      -1\n    ],\n    [\n      4,\n      3,\n      1.6,\n      0,\n      -1,\n      0,\n      0\n    ]\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/tour",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "tour"
              ]
            },
            "description": "{name?, scene, samples:[[t,x,y,z,fx,fy,fz]×2..6000]} ≤512 KB → {path, url} — a ?tour= link the viewer loops."
          },
          "response": []
        },
        {
          "name": "Upload poster (PNG)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));",
                  "console.log(pm.response.json().url);"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "image/png"
              },
              {
                "key": "X-Verse-Scene",
                "value": "{{sceneId}}",
                "description": "With account auth: attach as that owned scene's dashboard thumbnail"
              }
            ],
            "body": {
              "mode": "file",
              "file": {
                "src": ""
              }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/poster",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "poster"
              ]
            },
            "description": "Raw PNG ≤4 MB (magic-checked) → {path, url}; ready for the embed's data-poster."
          },
          "response": []
        }
      ]
    },
    {
      "name": "Presence",
      "description": "Live-tour rooms: SSE roster out, tiny pose POSTs in. Memory-only, 10 s TTL. docs/api.md#presence-rooms",
      "item": [
        {
          "name": "Presence: room roster (SSE)",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/v1/room/{{roomId}}/events",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "room",
                "{{roomId}}",
                "events"
              ]
            },
            "description": "text/event-stream of {peers:[{id, name, color, pose}]} on connect and on change (≤~10 fps). Postman shows a few events then you cancel; in a browser use EventSource."
          },
          "response": []
        },
        {
          "name": "Presence: post pose",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('204', () => pm.response.to.have.status(204));"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"id\": \"postman-viewer-1\",\n  \"name\": \"Postman\",\n  \"color\": \"#78aaff\",\n  \"pose\": [\n    0,\n    1.6,\n    3,\n    0,\n    0,\n    -1\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/room/{{roomId}}/pose",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "room",
                "{{roomId}}",
                "pose"
              ]
            },
            "description": "≤2 KB: id 4–32 chars [A-Za-z0-9_-], name ≤24, color #rrggbb, pose up to 6 numbers, optional flags {presenter, hand, muted, voice, ptt, laser:[x,y,z]} (2.7). Post at ~8 Hz; peers expire after 10 s. → 204."
          },
          "response": []
        },
        {
          "name": "Presence: relay a signal (voice / presenter, 2.7)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('204', () => pm.response.to.have.status(204));"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"from\": \"postman-viewer-1\",\n  \"to\": \"*\",\n  \"kind\": \"hand\",\n  \"data\": {}\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/room/{{roomId}}/signal",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "room",
                "{{roomId}}",
                "signal"
              ]
            },
            "description": "≤16 KB: {from, to: peer id | \"*\", kind: offer|answer|ice|bye|mute|present|action|laser|hand|handover|chat, data}. Relayed verbatim as `event: signal` to the SSE streams opened with ?id=<to> (or every other tagged peer for \"*\"); never stored. 400 bad id/kind, 429 over 400 signals/s/room. → 204 with X-Verse-Delivered: <n>."
          },
          "response": []
        }
      ]
    },
    {
      "name": "Signing & operations",
      "description": "Public key, health, ingest stats, client config, telemetry. docs/api.md#operations",
      "item": [
        {
          "name": "Get signing public key",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/v1/pubkey",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "pubkey"
              ]
            },
            "description": "→ {alg:'Ed25519', spki}. Cacheable 5 min. Must be identical across deploys."
          },
          "response": []
        },
        {
          "name": "Health (deep)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200 or 503', () => pm.expect([200, 503]).to.include(pm.response.code));",
                  "console.log(pm.response.json());"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/v1/health/deep",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "health",
                "deep"
              ]
            },
            "description": "No auth. 200 when every check passes, 503 otherwise: {ok, checks:{volumeWritable, compilerPresent, signingKey, disk, lastIngest, ingestLastHour, telemetryWritable}}."
          },
          "response": []
        },
        {
          "name": "Ingest stats (24 h)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{token}}",
                "description": "Account token from 'Create account' (or sign in at /studio)"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/ops/ingest-stats",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "ops",
                "ingest-stats"
              ]
            },
            "description": "Any account. {last24h:{attempts, done, failed, byReason, p50s, p95s}, recent:[…], telemetry24h:{canaryFails, uploadFails, loadErrors, lastCanaryFail, lastUploadFail}}."
          },
          "response": []
        },
        {
          "name": "Leads (operator)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200, 401 or 403', () => pm.expect([200, 401, 403]).to.include(pm.response.code));"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{adminToken}}",
                "description": "VERSE_ADMIN_TOKEN, or an account listed in VERSE_OPS_ACCOUNTS"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/v1/ops/leads",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "ops",
                "leads"
              ]
            },
            "description": "The contacts uploaders attached (name, phone, email, job, emailed), newest first, max 500. 401 without auth, 403 for any other account."
          },
          "response": []
        },
        {
          "name": "Client config",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/v1/config",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "config"
              ]
            },
            "description": "→ {cdnBase} — the CDN origin the viewer ranges public scenes against ('' = this origin)."
          },
          "response": []
        },
        {
          "name": "Post telemetry batch",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('204', () => pm.response.to.have.status(204));"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"kind\": \"canary-fail\",\n  \"detail\": \"postman probe\",\n  \"t\": 0\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/telemetry",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "telemetry"
              ]
            },
            "description": "≤1 MB per POST → always 204. Failure-beacon kinds: upload-fail, load-error, canary-fail; look rows feed the heatmap (docs/api.md#post-v1-telemetry)."
          },
          "response": []
        }
      ]
    },
    {
      "name": "Assistant",
      "description": "Ask Verse, the site assistant: text + whitelisted UI actions, no side effects. docs/api.md#assistant",
      "item": [
        {
          "name": "Ask Verse (SSE)",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200 or 403', () => pm.expect([200, 403]).to.include(pm.response.code));"
                ]
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"messages\": [\n    {\n      \"role\": \"user\",\n      \"content\": \"how big can my file be?\"\n    }\n  ],\n  \"context\": {\n    \"surface\": \"landing\"\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/v1/assist",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "assist"
              ]
            },
            "description": "text/event-stream of {type:'text'|'action'|'done'|'error'} events. Same-origin only (403 cross-site) — Postman may be refused; the shape is here for completeness."
          },
          "response": []
        },
        {
          "name": "Assistant status",
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('200', () => pm.response.to.have.status(200));"
                ]
              }
            }
          ],
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/v1/assist/status",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "v1",
                "assist",
                "status"
              ]
            },
            "description": "{mode: 'ai'} when the server has ANTHROPIC_API_KEY, {mode: 'basic'} otherwise."
          },
          "response": []
        }
      ]
    }
  ],
  "variable": [
    {
      "key": "baseUrl",
      "value": "https://verse.ctruh.com"
    },
    {
      "key": "apiKey",
      "value": ""
    },
    {
      "key": "token",
      "value": ""
    },
    {
      "key": "adminToken",
      "value": ""
    },
    {
      "key": "jobId",
      "value": ""
    },
    {
      "key": "sceneId",
      "value": ""
    },
    {
      "key": "uploadId",
      "value": ""
    },
    {
      "key": "shareToken",
      "value": ""
    },
    {
      "key": "shareId",
      "value": ""
    },
    {
      "key": "commentId",
      "value": ""
    },
    {
      "key": "versionN",
      "value": "2"
    },
    {
      "key": "roomId",
      "value": "postman-demo"
    }
  ]
}
