{"openapi":"3.1.0","info":{"title":"Paige API","version":"0.1.0","description":"The Paige public REST API (`/v1`). Build WhatsApp automations against your Paige project: send messages, manage templates, read conversations, tag contacts, assemble broadcasts, edit bot code + flows, and register signed webhooks.\n\n## Authentication\nEvery `/v1` request authenticates with a project API key: `Authorization: Bearer pk_live_…`. Mint and scope keys in **Settings → API keys**. A key carries its own project context, so `/v1` paths never include a project id.\n\n## Scopes\nEach endpoint requires one or more scopes (see each operation, and the `x-required-scopes` extension). Grant a key only the scopes it needs. A key missing a required scope gets `403 insufficient_scope`.\n\n## Response envelope\nSuccess: `{ \"success\": true, \"data\": … }`. Error: `{ \"success\": false, \"error\": { \"code\", \"message\" }, \"request_id\" }`. The `request_id` is also returned as the `X-Request-Id` header on every response.\n\n## Rate limits & quota\nRequests are throttled per key (`RateLimit-*` headers; exceed → `429 rate_limited`). WhatsApp sends also draw down a daily quota (exceed → `429 quota_exceeded`). Both carry `Retry-After`.\n\n## Idempotency\nSend an `Idempotency-Key` header on `POST /v1/messages` to make retries safe — a completed key replays the stored response and never re-sends.\n\n## Webhook signatures\nOutbound webhook deliveries carry `X-Paige-Signature: t=<unix>,v1=<hmac_sha256>`. Verify with the reference `verifyPaigeSignature` helper (constant-time HMAC over `\"<t>.<rawBody>\"`, 300s tolerance).","contact":{"name":"Paige","url":"https://paigeme.dev"}},"servers":[{"url":"https://api.paigeme.dev","description":"Production"}],"security":[{"ApiKeyAuth":[]}],"tags":[{"name":"Meta","description":"Smoke / diagnostics."},{"name":"Messages","description":"Send messages and read delivery status + media."},{"name":"Conversations","description":"List conversations, read messages, set state."},{"name":"Contacts","description":"Read contact messages; update names, tags, attributes."},{"name":"Templates","description":"WhatsApp message template CRUD."},{"name":"Broadcasts","description":"Segments + broadcast assembly (always pending_approval)."},{"name":"Build","description":"Read/edit bot code, deploy, generate + update flows."},{"name":"Webhooks","description":"Register signed outbound webhooks."}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"Project API key issued in Settings → API keys. Send it as `Authorization: Bearer pk_live_…`."}},"schemas":{"ApiError":{"type":"object","properties":{"code":{"type":"string","description":"Stable, machine-readable error code.","example":"invalid_request"},"message":{"type":"string","description":"Human-readable message (safe to surface).","example":"Invalid request body"}},"required":["code","message"]},"ErrorResponse":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"$ref":"#/components/schemas/ApiError"},"request_id":{"type":"string","description":"Per-request id, also returned as the X-Request-Id header."}},"required":["success","error","request_id"]}},"parameters":{}},"paths":{"/v1/openapi.json":{"get":{"operationId":"getOpenApiSpec","tags":["Meta"],"summary":"The OpenAPI 3.1 spec for this API (no auth)","description":"Serves this document. Public — point Mintlify / an agent at `<api-origin>/v1/openapi.json`.","security":[],"x-required-scopes":[],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{}}},"required":["success","data"]}}}},"500":{"description":"Unexpected server error (`server_error`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/ping":{"get":{"operationId":"ping","tags":["Meta"],"summary":"Smoke check — echo the resolved project + scopes","description":"Smoke check — echo the resolved project + scopes\n\n**Required scope:** any valid key.","security":[{"ApiKeyAuth":[]}],"x-required-scopes":[],"parameters":[{"schema":{"type":"string","description":"Target project id, for an MCP OAuth bearer (`mcp_at_…`) attached to more than one project — get ids from `GET /v1/projects`. Matched case-insensitively.\n\nOmit it and a READ falls back to the connection's default project; a **mutation** (any non-GET) on a connection with 2+ projects is rejected with `400 project_required` — a write is never defaulted to a guessed project. A connection with exactly one project never needs the header.\n\nScopes are checked against the SELECTED project only, never a union across the connection.\n\nFor a `pk_` API key the header selects nothing — one key is one project's context — but it IS validated: omit it and the key's own project is used, send it and it must name that project, otherwise the call is rejected with `403 project_not_attached` (a blank value is `400 invalid_project_header`, as above)."},"required":false,"name":"X-Paige-Project","in":"header"}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"ok":{"type":"boolean"},"projectId":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}}},"required":["ok","projectId","scopes"]}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid API key (`api_key_required` / `invalid_api_key` / `invalid_token` for a revoked or expired OAuth bearer).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit (`rate_limited`) or daily send quota (`quota_exceeded`) exceeded. Carries `RateLimit-*` + `Retry-After` headers.","headers":{"RateLimit-Limit":{"description":"Requests permitted in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error (`server_error`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/projects":{"get":{"operationId":"listProjects","tags":["Meta"],"summary":"The projects this credential may act on","description":"Discovery for multi-project connections (GRE-499). An MCP OAuth bearer (`mcp_at_…`) may be attached to SEVERAL projects, each with its OWN scopes; this returns them all, with `is_default` marking the one used when a call names no project. Pass a returned `id` in the `X-Paige-Project` header to target a project. A `pk_` API key is a single project's context and returns that one project as a one-element list (same shape), so a client never needs to branch on credential type.\n\nDeliberately NOT project-selected: it stays usable when the default project is paused (which would otherwise `403 project_inactive`), so a client can always discover a healthy sibling. The `X-Paige-Project` header is ignored here.\n\n**Required scope:** any valid key.","security":[{"ApiKeyAuth":[]}],"x-required-scopes":[],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"projects":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Project id — pass this as `X-Paige-Project`."},"name":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"},"description":"Scopes granted on THIS project. Never a union across the connection."},"is_default":{"type":"boolean","description":"Used when a read names no project."}},"required":["id","name","scopes","is_default"]}}},"required":["projects"]}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid API key (`api_key_required` / `invalid_api_key` / `invalid_token` for a revoked or expired OAuth bearer).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit (`rate_limited`) or daily send quota (`quota_exceeded`) exceeded. Carries `RateLimit-*` + `Retry-After` headers.","headers":{"RateLimit-Limit":{"description":"Requests permitted in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error (`server_error`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/messages":{"post":{"operationId":"sendMessage","tags":["Messages"],"summary":"Send a message (session, template, or interactive)","description":"Dispatch by `type`. Session types (`text|image|video|audio|document|location`) and `interactive` (button/list/carousel) are gated by the 24h customer-service window (closed → `409 outside_24h_window`); `template` is exempt. Supports the optional `Idempotency-Key` header (safe retries — a completed key replays the stored response). Reserves 1 unit of the daily send quota.\n\n**Required scope:** `messages:send`","security":[{"ApiKeyAuth":[]}],"x-required-scopes":["messages:send"],"parameters":[{"schema":{"type":"string","maxLength":255,"description":"Opt-in idempotency key for safe retries of this send."},"required":false,"name":"Idempotency-Key","in":"header"},{"schema":{"type":"string","description":"Target project id, for an MCP OAuth bearer (`mcp_at_…`) attached to more than one project — get ids from `GET /v1/projects`. Matched case-insensitively.\n\nOmit it and a READ falls back to the connection's default project; a **mutation** (any non-GET) on a connection with 2+ projects is rejected with `400 project_required` — a write is never defaulted to a guessed project. A connection with exactly one project never needs the header.\n\nScopes are checked against the SELECTED project only, never a union across the connection.\n\nFor a `pk_` API key the header selects nothing — one key is one project's context — but it IS validated: omit it and the key's own project is used, send it and it must name that project, otherwise the call is rejected with `403 project_not_attached` (a blank value is `400 invalid_project_header`, as above)."},"required":false,"name":"X-Paige-Project","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"conversationId":{"anyOf":[{"type":"string","minLength":1},{"type":"number"}]},"phone":{"type":"string"},"bsuid":{"type":"string"},"type":{"type":"string","enum":["text","image","video","audio","document","location"]},"content":{}},"required":["type"]},{"type":"object","properties":{"to":{"type":"string"},"phone":{"type":"string"},"name":{"type":"string"},"templateName":{"type":"string"},"language":{"type":"string"},"languageCode":{"type":"string"},"components":{"type":"array","items":{"type":"object","additionalProperties":{}}},"header":{"type":"object","properties":{"type":{"type":"string"},"text":{"type":"string"},"variables":{"type":"array","items":{"anyOf":[{"type":"string"},{"type":"number"}]}},"link":{"type":"string"},"id":{"type":"string"}}},"body":{"type":"object","properties":{"variables":{"type":"array","items":{"anyOf":[{"type":"string"},{"type":"number"}]}}}},"footer":{"type":"object","additionalProperties":{}},"buttons":{"type":"array","items":{"type":"object","properties":{"sub_type":{"type":"string"},"type":{"type":"string"},"index":{"anyOf":[{"type":"number"},{"type":"string"}]},"payload":{"type":"string"},"text":{"type":"string"}}}},"type":{"type":"string","enum":["template"]}},"required":["type"]},{"type":"object","properties":{"conversationId":{"anyOf":[{"type":"string","minLength":1},{"type":"number"}]},"phone":{"type":"string","pattern":"^\\+?\\d{7,15}$"},"bsuid":{"type":"string"},"content":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["button"]},"header":{"type":"object","properties":{"type":{"type":"string","enum":["text","image","video","document"]}},"required":["type"]},"body":{"type":"object","properties":{"text":{"type":"string","minLength":1,"maxLength":1024}},"required":["text"]},"footer":{"type":"object","properties":{"text":{"type":"string","minLength":1,"maxLength":60}},"required":["text"]},"action":{"type":"object","properties":{"buttons":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["reply"]},"reply":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":256},"title":{"type":"string","minLength":1,"maxLength":20}},"required":["id","title"]}},"required":["type","reply"]}}},"required":["buttons"]}},"required":["type","body","action"]},{"type":"object","properties":{"type":{"type":"string","enum":["list"]},"header":{"type":"object","properties":{"type":{"type":"string","enum":["text","image","video","document"]}},"required":["type"]},"body":{"type":"object","properties":{"text":{"type":"string","minLength":1,"maxLength":1024}},"required":["text"]},"footer":{"type":"object","properties":{"text":{"type":"string","minLength":1,"maxLength":60}},"required":["text"]},"action":{"type":"object","properties":{"button":{"type":"string","minLength":1,"maxLength":20},"sections":{"type":"array","items":{"type":"object","properties":{"title":{"type":"string","maxLength":24},"rows":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":200},"title":{"type":"string","minLength":1,"maxLength":24},"description":{"type":"string","maxLength":72}},"required":["id","title"]}}},"required":["rows"]}}},"required":["button","sections"]}},"required":["type","body","action"]},{"type":"object","properties":{"type":{"type":"string","enum":["carousel"]},"body":{"type":"object","properties":{"text":{"type":"string","minLength":1,"maxLength":1024}},"required":["text"]},"action":{"type":"object","properties":{"cards":{"type":"array","items":{"type":"object","properties":{"card_index":{"type":"integer","minimum":0},"body":{"type":"object","properties":{"text":{"type":"string","minLength":1,"maxLength":1024}},"required":["text"]},"action":{"type":"object","properties":{"buttons":{"type":"array","items":{"type":"object","additionalProperties":{}}}},"required":["buttons"]}},"required":["action"]}}},"required":["cards"]}},"required":["type","action"]}]},"type":{"type":"string","enum":["interactive"]}},"required":["content","type"]}]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"id":{"type":["string","null"]},"status":{"type":"string"}},"required":["id","status"]}},"required":["success","data"]}}}},"400":{"description":"Validation / bad request (e.g. `invalid_request`, `invalid_cursor`, `outside_24h_window` is 409). Also `invalid_project_header` (any credential: `X-Paige-Project` was sent but blank) and, for multi-project MCP bearers, `project_required` (a mutation, or a read with no default, and no `X-Paige-Project`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid API key (`api_key_required` / `invalid_api_key` / `invalid_token` for a revoked or expired OAuth bearer).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Out of credits (`insufficient_credits`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the required scope, project inactive, or a write targets a platform-protected file (`insufficient_scope` / `project_inactive` / `subscription_inactive` / `protected_file`). Also `project_not_attached` (the `X-Paige-Project` project is not one this credential may act on — not attached to the MCP connection, or not the `pk_` key's own project; identical response whether or not it exists) and, for MCP bearers, `no_project_access` (the connection has no projects attached).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found (`not_found` / `*_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict — 24h window closed (`outside_24h_window`) or idempotency (`idempotency_in_progress` / `idempotency_key_mismatch`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit (`rate_limited`) or daily send quota (`quota_exceeded`) exceeded. Carries `RateLimit-*` + `Retry-After` headers.","headers":{"RateLimit-Limit":{"description":"Requests permitted in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error (`server_error`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/templates":{"post":{"operationId":"createTemplate","tags":["Templates"],"summary":"Create a WhatsApp message template","description":"Creates a template on the project WABA. Meta approves asynchronously — returns `status: pending`.\n\n**Required scope:** `templates:manage`","security":[{"ApiKeyAuth":[]}],"x-required-scopes":["templates:manage"],"parameters":[{"schema":{"type":"string","description":"Target project id, for an MCP OAuth bearer (`mcp_at_…`) attached to more than one project — get ids from `GET /v1/projects`. Matched case-insensitively.\n\nOmit it and a READ falls back to the connection's default project; a **mutation** (any non-GET) on a connection with 2+ projects is rejected with `400 project_required` — a write is never defaulted to a guessed project. A connection with exactly one project never needs the header.\n\nScopes are checked against the SELECTED project only, never a union across the connection.\n\nFor a `pk_` API key the header selects nothing — one key is one project's context — but it IS validated: omit it and the key's own project is used, send it and it must name that project, otherwise the call is rejected with `403 project_not_attached` (a blank value is `400 invalid_project_header`, as above)."},"required":false,"name":"X-Paige-Project","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":512,"pattern":"^[a-z][a-z0-9_]*$"},"category":{"type":"string","enum":["MARKETING","UTILITY","AUTHENTICATION"]},"language":{"type":"string","minLength":2,"maxLength":15},"components":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["HEADER"]},"format":{"type":"string","enum":["TEXT","IMAGE","VIDEO","DOCUMENT"]},"text":{"type":"string","minLength":1,"maxLength":60},"example":{"type":"object","additionalProperties":{}}},"required":["type"]},{"type":"object","properties":{"type":{"type":"string","enum":["BODY"]},"text":{"type":"string","minLength":1,"maxLength":1024},"example":{"type":"object","additionalProperties":{}}},"required":["type","text"]},{"type":"object","properties":{"type":{"type":"string","enum":["FOOTER"]},"text":{"type":"string","minLength":1,"maxLength":60}},"required":["type","text"]},{"type":"object","properties":{"type":{"type":"string","enum":["BUTTONS"]},"buttons":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","enum":["QUICK_REPLY"]},"text":{"type":"string","minLength":1,"maxLength":25}},"required":["type","text"]},{"type":"object","properties":{"type":{"type":"string","enum":["URL"]},"text":{"type":"string","minLength":1,"maxLength":25},"url":{"type":"string","minLength":1,"maxLength":2000},"example":{"type":"array","items":{"type":"string"}}},"required":["type","text","url"]},{"type":"object","properties":{"type":{"type":"string","enum":["PHONE_NUMBER"]},"text":{"type":"string","minLength":1,"maxLength":25},"phone_number":{"type":"string","minLength":1,"maxLength":20}},"required":["type","text","phone_number"]},{"type":"object","properties":{"type":{"type":"string","enum":["COPY_CODE"]},"text":{"type":"string","minLength":1,"maxLength":25},"example":{"type":"array","items":{"type":"string"}}},"required":["type"]}]}}},"required":["type","buttons"]}]}}},"required":["name","category","language","components"],"additionalProperties":false}}}},"responses":{"201":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"id":{"type":["string","null"]},"name":{"type":"string"},"language":{"type":"string"},"category":{"type":"string"},"status":{"type":"string"}},"required":["id","name","language","category","status"]}},"required":["success","data"]}}}},"400":{"description":"Validation / bad request (e.g. `invalid_request`, `invalid_cursor`, `outside_24h_window` is 409). Also `invalid_project_header` (any credential: `X-Paige-Project` was sent but blank) and, for multi-project MCP bearers, `project_required` (a mutation, or a read with no default, and no `X-Paige-Project`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid API key (`api_key_required` / `invalid_api_key` / `invalid_token` for a revoked or expired OAuth bearer).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the required scope, project inactive, or a write targets a platform-protected file (`insufficient_scope` / `project_inactive` / `subscription_inactive` / `protected_file`). Also `project_not_attached` (the `X-Paige-Project` project is not one this credential may act on — not attached to the MCP connection, or not the `pk_` key's own project; identical response whether or not it exists) and, for MCP bearers, `no_project_access` (the connection has no projects attached).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict — 24h window closed (`outside_24h_window`) or idempotency (`idempotency_in_progress` / `idempotency_key_mismatch`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit (`rate_limited`) or daily send quota (`quota_exceeded`) exceeded. Carries `RateLimit-*` + `Retry-After` headers.","headers":{"RateLimit-Limit":{"description":"Requests permitted in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error (`server_error`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"Upstream Meta rejection (`invalid_template` / `meta_auth_error`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"get":{"operationId":"listTemplates","tags":["Templates"],"summary":"List message templates + approval status","description":"List message templates + approval status\n\n**Required scope:** `templates:read`","security":[{"ApiKeyAuth":[]}],"x-required-scopes":["templates:read"],"parameters":[{"schema":{"type":"string","description":"Target project id, for an MCP OAuth bearer (`mcp_at_…`) attached to more than one project — get ids from `GET /v1/projects`. Matched case-insensitively.\n\nOmit it and a READ falls back to the connection's default project; a **mutation** (any non-GET) on a connection with 2+ projects is rejected with `400 project_required` — a write is never defaulted to a guessed project. A connection with exactly one project never needs the header.\n\nScopes are checked against the SELECTED project only, never a union across the connection.\n\nFor a `pk_` API key the header selects nothing — one key is one project's context — but it IS validated: omit it and the key's own project is used, send it and it must name that project, otherwise the call is rejected with `403 project_not_attached` (a blank value is `400 invalid_project_header`, as above)."},"required":false,"name":"X-Paige-Project","in":"header"}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"templates":{"type":"array","items":{"type":"object","properties":{}}}},"required":["templates"]}},"required":["success","data"]}}}},"400":{"description":"Validation / bad request (e.g. `invalid_request`, `invalid_cursor`, `outside_24h_window` is 409). Also `invalid_project_header` (any credential: `X-Paige-Project` was sent but blank) and, for multi-project MCP bearers, `project_required` (a mutation, or a read with no default, and no `X-Paige-Project`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid API key (`api_key_required` / `invalid_api_key` / `invalid_token` for a revoked or expired OAuth bearer).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the required scope, project inactive, or a write targets a platform-protected file (`insufficient_scope` / `project_inactive` / `subscription_inactive` / `protected_file`). Also `project_not_attached` (the `X-Paige-Project` project is not one this credential may act on — not attached to the MCP connection, or not the `pk_` key's own project; identical response whether or not it exists) and, for MCP bearers, `no_project_access` (the connection has no projects attached).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit (`rate_limited`) or daily send quota (`quota_exceeded`) exceeded. Carries `RateLimit-*` + `Retry-After` headers.","headers":{"RateLimit-Limit":{"description":"Requests permitted in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error (`server_error`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/templates/{name}":{"delete":{"operationId":"deleteTemplate","tags":["Templates"],"summary":"Delete a template (all localizations, or one via ?language=)","description":"Delete a template (all localizations, or one via ?language=)\n\n**Required scope:** `templates:manage`","security":[{"ApiKeyAuth":[]}],"x-required-scopes":["templates:manage"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"name","in":"path"},{"schema":{"type":"string","description":"Remove only this localization; omit to remove every localization of the name."},"required":false,"name":"language","in":"query"},{"schema":{"type":"string","description":"Target project id, for an MCP OAuth bearer (`mcp_at_…`) attached to more than one project — get ids from `GET /v1/projects`. Matched case-insensitively.\n\nOmit it and a READ falls back to the connection's default project; a **mutation** (any non-GET) on a connection with 2+ projects is rejected with `400 project_required` — a write is never defaulted to a guessed project. A connection with exactly one project never needs the header.\n\nScopes are checked against the SELECTED project only, never a union across the connection.\n\nFor a `pk_` API key the header selects nothing — one key is one project's context — but it IS validated: omit it and the key's own project is used, send it and it must name that project, otherwise the call is rejected with `403 project_not_attached` (a blank value is `400 invalid_project_header`, as above)."},"required":false,"name":"X-Paige-Project","in":"header"}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"deleted":{"type":"boolean"},"name":{"type":"string"},"language":{"type":["string","null"]},"count":{"type":"number"}},"required":["deleted","name","language","count"]}},"required":["success","data"]}}}},"400":{"description":"Validation / bad request (e.g. `invalid_request`, `invalid_cursor`, `outside_24h_window` is 409). Also `invalid_project_header` (any credential: `X-Paige-Project` was sent but blank) and, for multi-project MCP bearers, `project_required` (a mutation, or a read with no default, and no `X-Paige-Project`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid API key (`api_key_required` / `invalid_api_key` / `invalid_token` for a revoked or expired OAuth bearer).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the required scope, project inactive, or a write targets a platform-protected file (`insufficient_scope` / `project_inactive` / `subscription_inactive` / `protected_file`). Also `project_not_attached` (the `X-Paige-Project` project is not one this credential may act on — not attached to the MCP connection, or not the `pk_` key's own project; identical response whether or not it exists) and, for MCP bearers, `no_project_access` (the connection has no projects attached).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found (`not_found` / `*_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit (`rate_limited`) or daily send quota (`quota_exceeded`) exceeded. Carries `RateLimit-*` + `Retry-After` headers.","headers":{"RateLimit-Limit":{"description":"Requests permitted in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error (`server_error`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"Upstream Meta rejection (`invalid_template` / `meta_auth_error`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/conversations":{"get":{"operationId":"listConversations","tags":["Conversations"],"summary":"List conversations (state filter, keyset cursor)","description":"List conversations (state filter, keyset cursor)\n\n**Required scope:** `conversations:read`","security":[{"ApiKeyAuth":[]}],"x-required-scopes":["conversations:read"],"parameters":[{"schema":{"type":"string"},"required":false,"name":"state","in":"query"},{"schema":{"type":["integer","null"],"description":"≤100, default 50."},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","description":"Opaque nextCursor from a prior page."},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"string","description":"Target project id, for an MCP OAuth bearer (`mcp_at_…`) attached to more than one project — get ids from `GET /v1/projects`. Matched case-insensitively.\n\nOmit it and a READ falls back to the connection's default project; a **mutation** (any non-GET) on a connection with 2+ projects is rejected with `400 project_required` — a write is never defaulted to a guessed project. A connection with exactly one project never needs the header.\n\nScopes are checked against the SELECTED project only, never a union across the connection.\n\nFor a `pk_` API key the header selects nothing — one key is one project's context — but it IS validated: omit it and the key's own project is used, send it and it must name that project, otherwise the call is rejected with `403 project_not_attached` (a blank value is `400 invalid_project_header`, as above)."},"required":false,"name":"X-Paige-Project","in":"header"}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"conversations":{"type":"array","items":{"type":"object","properties":{"id":{"anyOf":[{"type":"string"},{"type":"number"}]},"state":{"type":["string","null"]},"status":{"type":["string","null"]},"last_message_at":{},"last_inbound_at":{},"wa_user_id":{"type":["string","null"]},"contact":{"type":"object","properties":{"id":{},"phone":{"type":["string","null"]},"name":{"type":["string","null"]},"username":{"type":["string","null"]}},"required":["phone","name","username"]},"last_message":{"type":["object","null"],"properties":{"type":{"type":["string","null"]},"content":{"type":"object","properties":{"body":{"type":["string","null"]}},"required":["body"]},"direction":{"type":["string","null"]}},"required":["type","content","direction"]}},"required":["id","state","status","wa_user_id","contact","last_message"]}},"pagination":{"type":"object","properties":{"hasMore":{"type":"boolean"},"nextCursor":{"type":["string","null"]}},"required":["hasMore","nextCursor"]}},"required":["conversations","pagination"]}},"required":["success","data"]}}}},"400":{"description":"Validation / bad request (e.g. `invalid_request`, `invalid_cursor`, `outside_24h_window` is 409). Also `invalid_project_header` (any credential: `X-Paige-Project` was sent but blank) and, for multi-project MCP bearers, `project_required` (a mutation, or a read with no default, and no `X-Paige-Project`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid API key (`api_key_required` / `invalid_api_key` / `invalid_token` for a revoked or expired OAuth bearer).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the required scope, project inactive, or a write targets a platform-protected file (`insufficient_scope` / `project_inactive` / `subscription_inactive` / `protected_file`). Also `project_not_attached` (the `X-Paige-Project` project is not one this credential may act on — not attached to the MCP connection, or not the `pk_` key's own project; identical response whether or not it exists) and, for MCP bearers, `no_project_access` (the connection has no projects attached).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit (`rate_limited`) or daily send quota (`quota_exceeded`) exceeded. Carries `RateLimit-*` + `Retry-After` headers.","headers":{"RateLimit-Limit":{"description":"Requests permitted in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error (`server_error`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/conversations/{id}/messages":{"get":{"operationId":"getConversationMessages","tags":["Conversations"],"summary":"Messages in a conversation (keyset cursor)","description":"Messages in a conversation (keyset cursor)\n\n**Required scope:** `conversations:read`","security":[{"ApiKeyAuth":[]}],"x-required-scopes":["conversations:read"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":["integer","null"]},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string"},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"string","description":"Target project id, for an MCP OAuth bearer (`mcp_at_…`) attached to more than one project — get ids from `GET /v1/projects`. Matched case-insensitively.\n\nOmit it and a READ falls back to the connection's default project; a **mutation** (any non-GET) on a connection with 2+ projects is rejected with `400 project_required` — a write is never defaulted to a guessed project. A connection with exactly one project never needs the header.\n\nScopes are checked against the SELECTED project only, never a union across the connection.\n\nFor a `pk_` API key the header selects nothing — one key is one project's context — but it IS validated: omit it and the key's own project is used, send it and it must name that project, otherwise the call is rejected with `403 project_not_attached` (a blank value is `400 invalid_project_header`, as above)."},"required":false,"name":"X-Paige-Project","in":"header"}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"messages":{"type":"array","items":{"type":"object","properties":{"id":{},"direction":{"type":["string","null"]},"type":{"type":["string","null"]},"content":{"type":"object","properties":{"body":{"type":["string","null"]}},"required":["body"]},"status":{"type":["string","null"]},"error_code":{},"created_at":{},"raw_payload":{},"media_id":{}},"required":["direction","type","content","status"]}},"pagination":{"type":"object","properties":{"hasMore":{"type":"boolean"},"nextCursor":{"type":["string","null"]}},"required":["hasMore","nextCursor"]}},"required":["messages","pagination"]}},"required":["success","data"]}}}},"400":{"description":"Validation / bad request (e.g. `invalid_request`, `invalid_cursor`, `outside_24h_window` is 409). Also `invalid_project_header` (any credential: `X-Paige-Project` was sent but blank) and, for multi-project MCP bearers, `project_required` (a mutation, or a read with no default, and no `X-Paige-Project`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid API key (`api_key_required` / `invalid_api_key` / `invalid_token` for a revoked or expired OAuth bearer).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the required scope, project inactive, or a write targets a platform-protected file (`insufficient_scope` / `project_inactive` / `subscription_inactive` / `protected_file`). Also `project_not_attached` (the `X-Paige-Project` project is not one this credential may act on — not attached to the MCP connection, or not the `pk_` key's own project; identical response whether or not it exists) and, for MCP bearers, `no_project_access` (the connection has no projects attached).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found (`not_found` / `*_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit (`rate_limited`) or daily send quota (`quota_exceeded`) exceeded. Carries `RateLimit-*` + `Retry-After` headers.","headers":{"RateLimit-Limit":{"description":"Requests permitted in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error (`server_error`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/contacts/{id}/messages":{"get":{"operationId":"getContactMessages","tags":["Contacts"],"summary":"All messages for a contact (phone or BSUID)","description":"`{id}` is the contact phone (E.164) by default, or its BSUID with `?identifierType=bsuid`.\n\n**Required scope:** `contacts:read`","security":[{"ApiKeyAuth":[]}],"x-required-scopes":["contacts:read"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":["integer","null"]},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string"},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"string","enum":["phone","bsuid"]},"required":false,"name":"identifierType","in":"query"},{"schema":{"type":"string","description":"Target project id, for an MCP OAuth bearer (`mcp_at_…`) attached to more than one project — get ids from `GET /v1/projects`. Matched case-insensitively.\n\nOmit it and a READ falls back to the connection's default project; a **mutation** (any non-GET) on a connection with 2+ projects is rejected with `400 project_required` — a write is never defaulted to a guessed project. A connection with exactly one project never needs the header.\n\nScopes are checked against the SELECTED project only, never a union across the connection.\n\nFor a `pk_` API key the header selects nothing — one key is one project's context — but it IS validated: omit it and the key's own project is used, send it and it must name that project, otherwise the call is rejected with `403 project_not_attached` (a blank value is `400 invalid_project_header`, as above)."},"required":false,"name":"X-Paige-Project","in":"header"}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"messages":{"type":"array","items":{"type":"object","properties":{"id":{},"direction":{"type":["string","null"]},"type":{"type":["string","null"]},"content":{"type":"object","properties":{"body":{"type":["string","null"]}},"required":["body"]},"status":{"type":["string","null"]},"error_code":{},"created_at":{},"raw_payload":{},"media_id":{}},"required":["direction","type","content","status"]}},"pagination":{"type":"object","properties":{"hasMore":{"type":"boolean"},"nextCursor":{"type":["string","null"]}},"required":["hasMore","nextCursor"]}},"required":["messages","pagination"]}},"required":["success","data"]}}}},"400":{"description":"Validation / bad request (e.g. `invalid_request`, `invalid_cursor`, `outside_24h_window` is 409). Also `invalid_project_header` (any credential: `X-Paige-Project` was sent but blank) and, for multi-project MCP bearers, `project_required` (a mutation, or a read with no default, and no `X-Paige-Project`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid API key (`api_key_required` / `invalid_api_key` / `invalid_token` for a revoked or expired OAuth bearer).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the required scope, project inactive, or a write targets a platform-protected file (`insufficient_scope` / `project_inactive` / `subscription_inactive` / `protected_file`). Also `project_not_attached` (the `X-Paige-Project` project is not one this credential may act on — not attached to the MCP connection, or not the `pk_` key's own project; identical response whether or not it exists) and, for MCP bearers, `no_project_access` (the connection has no projects attached).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit (`rate_limited`) or daily send quota (`quota_exceeded`) exceeded. Carries `RateLimit-*` + `Retry-After` headers.","headers":{"RateLimit-Limit":{"description":"Requests permitted in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error (`server_error`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/messages/{id}":{"get":{"operationId":"getMessageStatus","tags":["Messages"],"summary":"Delivery status for one message","description":"Delivery status for one message\n\n**Required scope:** `conversations:read`","security":[{"ApiKeyAuth":[]}],"x-required-scopes":["conversations:read"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","description":"Target project id, for an MCP OAuth bearer (`mcp_at_…`) attached to more than one project — get ids from `GET /v1/projects`. Matched case-insensitively.\n\nOmit it and a READ falls back to the connection's default project; a **mutation** (any non-GET) on a connection with 2+ projects is rejected with `400 project_required` — a write is never defaulted to a guessed project. A connection with exactly one project never needs the header.\n\nScopes are checked against the SELECTED project only, never a union across the connection.\n\nFor a `pk_` API key the header selects nothing — one key is one project's context — but it IS validated: omit it and the key's own project is used, send it and it must name that project, otherwise the call is rejected with `403 project_not_attached` (a blank value is `400 invalid_project_header`, as above)."},"required":false,"name":"X-Paige-Project","in":"header"}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"id":{},"status":{"type":["string","null"]},"error_code":{},"direction":{"type":["string","null"]},"type":{"type":["string","null"]},"created_at":{}},"required":["status","direction","type"]}},"required":["success","data"]}}}},"400":{"description":"Validation / bad request (e.g. `invalid_request`, `invalid_cursor`, `outside_24h_window` is 409). Also `invalid_project_header` (any credential: `X-Paige-Project` was sent but blank) and, for multi-project MCP bearers, `project_required` (a mutation, or a read with no default, and no `X-Paige-Project`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid API key (`api_key_required` / `invalid_api_key` / `invalid_token` for a revoked or expired OAuth bearer).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the required scope, project inactive, or a write targets a platform-protected file (`insufficient_scope` / `project_inactive` / `subscription_inactive` / `protected_file`). Also `project_not_attached` (the `X-Paige-Project` project is not one this credential may act on — not attached to the MCP connection, or not the `pk_` key's own project; identical response whether or not it exists) and, for MCP bearers, `no_project_access` (the connection has no projects attached).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found (`not_found` / `*_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit (`rate_limited`) or daily send quota (`quota_exceeded`) exceeded. Carries `RateLimit-*` + `Retry-After` headers.","headers":{"RateLimit-Limit":{"description":"Requests permitted in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error (`server_error`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/messages/{id}/media":{"get":{"operationId":"getMessageMedia","tags":["Messages"],"summary":"Retrieve the media attached to a message (bytes or 302 redirect)","description":"Retrieve the media attached to a message (bytes or 302 redirect)\n\n**Required scope:** `conversations:read`","security":[{"ApiKeyAuth":[]}],"x-required-scopes":["conversations:read"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","description":"Target project id, for an MCP OAuth bearer (`mcp_at_…`) attached to more than one project — get ids from `GET /v1/projects`. Matched case-insensitively.\n\nOmit it and a READ falls back to the connection's default project; a **mutation** (any non-GET) on a connection with 2+ projects is rejected with `400 project_required` — a write is never defaulted to a guessed project. A connection with exactly one project never needs the header.\n\nScopes are checked against the SELECTED project only, never a union across the connection.\n\nFor a `pk_` API key the header selects nothing — one key is one project's context — but it IS validated: omit it and the key's own project is used, send it and it must name that project, otherwise the call is rejected with `403 project_not_attached` (a blank value is `400 invalid_project_header`, as above)."},"required":false,"name":"X-Paige-Project","in":"header"}],"responses":{"200":{"description":"Success (raw bytes).","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary","description":"Raw media bytes (or a 302 redirect to a direct URL)."}}}},"400":{"description":"Validation / bad request (e.g. `invalid_request`, `invalid_cursor`, `outside_24h_window` is 409). Also `invalid_project_header` (any credential: `X-Paige-Project` was sent but blank) and, for multi-project MCP bearers, `project_required` (a mutation, or a read with no default, and no `X-Paige-Project`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid API key (`api_key_required` / `invalid_api_key` / `invalid_token` for a revoked or expired OAuth bearer).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the required scope, project inactive, or a write targets a platform-protected file (`insufficient_scope` / `project_inactive` / `subscription_inactive` / `protected_file`). Also `project_not_attached` (the `X-Paige-Project` project is not one this credential may act on — not attached to the MCP connection, or not the `pk_` key's own project; identical response whether or not it exists) and, for MCP bearers, `no_project_access` (the connection has no projects attached).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"410":{"description":"Media expired at Meta (`MEDIA_EXPIRED`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit (`rate_limited`) or daily send quota (`quota_exceeded`) exceeded. Carries `RateLimit-*` + `Retry-After` headers.","headers":{"RateLimit-Limit":{"description":"Requests permitted in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error (`server_error`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/conversations/{id}":{"patch":{"operationId":"updateConversationState","tags":["Conversations"],"summary":"Set a conversation's state (validated against known states)","description":"Set a conversation's state (validated against known states)\n\n**Required scope:** `conversations:write`","security":[{"ApiKeyAuth":[]}],"x-required-scopes":["conversations:write"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","description":"Target project id, for an MCP OAuth bearer (`mcp_at_…`) attached to more than one project — get ids from `GET /v1/projects`. Matched case-insensitively.\n\nOmit it and a READ falls back to the connection's default project; a **mutation** (any non-GET) on a connection with 2+ projects is rejected with `400 project_required` — a write is never defaulted to a guessed project. A connection with exactly one project never needs the header.\n\nScopes are checked against the SELECTED project only, never a union across the connection.\n\nFor a `pk_` API key the header selects nothing — one key is one project's context — but it IS validated: omit it and the key's own project is used, send it and it must name that project, otherwise the call is rejected with `403 project_not_attached` (a blank value is `400 invalid_project_header`, as above)."},"required":false,"name":"X-Paige-Project","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"state":{"type":"string"}}}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"conversation":{"type":"object","properties":{"id":{"anyOf":[{"type":"string"},{"type":"number"}]},"state":{"type":["string","null"]},"previous_state":{"type":["string","null"]}},"required":["id","state","previous_state"]}},"required":["conversation"]}},"required":["success","data"]}}}},"400":{"description":"Validation / bad request (e.g. `invalid_request`, `invalid_cursor`, `outside_24h_window` is 409). Also `invalid_project_header` (any credential: `X-Paige-Project` was sent but blank) and, for multi-project MCP bearers, `project_required` (a mutation, or a read with no default, and no `X-Paige-Project`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid API key (`api_key_required` / `invalid_api_key` / `invalid_token` for a revoked or expired OAuth bearer).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the required scope, project inactive, or a write targets a platform-protected file (`insufficient_scope` / `project_inactive` / `subscription_inactive` / `protected_file`). Also `project_not_attached` (the `X-Paige-Project` project is not one this credential may act on — not attached to the MCP connection, or not the `pk_` key's own project; identical response whether or not it exists) and, for MCP bearers, `no_project_access` (the connection has no projects attached).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found (`not_found` / `*_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit (`rate_limited`) or daily send quota (`quota_exceeded`) exceeded. Carries `RateLimit-*` + `Retry-After` headers.","headers":{"RateLimit-Limit":{"description":"Requests permitted in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error (`server_error`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/contacts/{id}":{"patch":{"operationId":"updateContact","tags":["Contacts"],"summary":"Update a contact name / tags / custom attributes","description":"`{id}` is the contact phone (E.164) by default, or its BSUID with `?identifierType=bsuid`. Tags accept a full-replacement array or an `{ add, remove }` delta; attributes merge (a `null` value deletes the key).\n\n**Required scope:** `contacts:write`","security":[{"ApiKeyAuth":[]}],"x-required-scopes":["contacts:write"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","enum":["phone","bsuid"]},"required":false,"name":"identifierType","in":"query"},{"schema":{"type":"string","description":"Target project id, for an MCP OAuth bearer (`mcp_at_…`) attached to more than one project — get ids from `GET /v1/projects`. Matched case-insensitively.\n\nOmit it and a READ falls back to the connection's default project; a **mutation** (any non-GET) on a connection with 2+ projects is rejected with `400 project_required` — a write is never defaulted to a guessed project. A connection with exactly one project never needs the header.\n\nScopes are checked against the SELECTED project only, never a union across the connection.\n\nFor a `pk_` API key the header selects nothing — one key is one project's context — but it IS validated: omit it and the key's own project is used, send it and it must name that project, otherwise the call is rejected with `403 project_not_attached` (a blank value is `400 invalid_project_header`, as above)."},"required":false,"name":"X-Paige-Project","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":["string","null"],"maxLength":255},"tags":{"anyOf":[{"type":"array","items":{"type":"string","minLength":1,"maxLength":64}},{"type":"object","properties":{"add":{"type":"array","items":{"type":"string","minLength":1,"maxLength":64}},"remove":{"type":"array","items":{"type":"string","minLength":1,"maxLength":64}}}}]},"attributes":{"type":"object","additionalProperties":{"anyOf":[{"type":"string","maxLength":1000},{"type":"number"},{"type":"boolean"},{"type":"null"},{"type":"null"}]}}}}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"contact":{"type":"object","properties":{"id":{},"phone":{"type":["string","null"]},"bsuid":{"type":["string","null"]},"name":{"type":["string","null"]},"tags":{},"attributes":{}},"required":["phone","bsuid","name"]}},"required":["contact"]}},"required":["success","data"]}}}},"400":{"description":"Validation / bad request (e.g. `invalid_request`, `invalid_cursor`, `outside_24h_window` is 409). Also `invalid_project_header` (any credential: `X-Paige-Project` was sent but blank) and, for multi-project MCP bearers, `project_required` (a mutation, or a read with no default, and no `X-Paige-Project`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid API key (`api_key_required` / `invalid_api_key` / `invalid_token` for a revoked or expired OAuth bearer).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the required scope, project inactive, or a write targets a platform-protected file (`insufficient_scope` / `project_inactive` / `subscription_inactive` / `protected_file`). Also `project_not_attached` (the `X-Paige-Project` project is not one this credential may act on — not attached to the MCP connection, or not the `pk_` key's own project; identical response whether or not it exists) and, for MCP bearers, `no_project_access` (the connection has no projects attached).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found (`not_found` / `*_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit (`rate_limited`) or daily send quota (`quota_exceeded`) exceeded. Carries `RateLimit-*` + `Retry-After` headers.","headers":{"RateLimit-Limit":{"description":"Requests permitted in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error (`server_error`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/files":{"get":{"operationId":"listFiles","tags":["Build"],"summary":"List the bot code files","description":"List the bot code files\n\n**Required scope:** `code:read`","security":[{"ApiKeyAuth":[]}],"x-required-scopes":["code:read"],"parameters":[{"schema":{"type":"string","enum":["dev","prod"]},"required":false,"name":"environment","in":"query"},{"schema":{"type":"string","description":"Target project id, for an MCP OAuth bearer (`mcp_at_…`) attached to more than one project — get ids from `GET /v1/projects`. Matched case-insensitively.\n\nOmit it and a READ falls back to the connection's default project; a **mutation** (any non-GET) on a connection with 2+ projects is rejected with `400 project_required` — a write is never defaulted to a guessed project. A connection with exactly one project never needs the header.\n\nScopes are checked against the SELECTED project only, never a union across the connection.\n\nFor a `pk_` API key the header selects nothing — one key is one project's context — but it IS validated: omit it and the key's own project is used, send it and it must name that project, otherwise the call is rejected with `403 project_not_attached` (a blank value is `400 invalid_project_header`, as above)."},"required":false,"name":"X-Paige-Project","in":"header"}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"files":{"type":"array","items":{"type":"string"}},"environment":{"type":"string"}},"required":["files","environment"]}},"required":["success","data"]}}}},"400":{"description":"Validation / bad request (e.g. `invalid_request`, `invalid_cursor`, `outside_24h_window` is 409). Also `invalid_project_header` (any credential: `X-Paige-Project` was sent but blank) and, for multi-project MCP bearers, `project_required` (a mutation, or a read with no default, and no `X-Paige-Project`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid API key (`api_key_required` / `invalid_api_key` / `invalid_token` for a revoked or expired OAuth bearer).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the required scope, project inactive, or a write targets a platform-protected file (`insufficient_scope` / `project_inactive` / `subscription_inactive` / `protected_file`). Also `project_not_attached` (the `X-Paige-Project` project is not one this credential may act on — not attached to the MCP connection, or not the `pk_` key's own project; identical response whether or not it exists) and, for MCP bearers, `no_project_access` (the connection has no projects attached).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit (`rate_limited`) or daily send quota (`quota_exceeded`) exceeded. Carries `RateLimit-*` + `Retry-After` headers.","headers":{"RateLimit-Limit":{"description":"Requests permitted in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error (`server_error`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/files/{path}":{"get":{"operationId":"readFile","tags":["Build"],"summary":"Read one bot code file","description":"Read one bot code file\n\n**Required scope:** `code:read`","security":[{"ApiKeyAuth":[]}],"x-required-scopes":["code:read"],"parameters":[{"schema":{"type":"string","description":"Project-relative path (may contain slashes)."},"required":true,"name":"path","in":"path"},{"schema":{"type":"string","enum":["dev","prod"]},"required":false,"name":"environment","in":"query"},{"schema":{"type":"string","description":"Target project id, for an MCP OAuth bearer (`mcp_at_…`) attached to more than one project — get ids from `GET /v1/projects`. Matched case-insensitively.\n\nOmit it and a READ falls back to the connection's default project; a **mutation** (any non-GET) on a connection with 2+ projects is rejected with `400 project_required` — a write is never defaulted to a guessed project. A connection with exactly one project never needs the header.\n\nScopes are checked against the SELECTED project only, never a union across the connection.\n\nFor a `pk_` API key the header selects nothing — one key is one project's context — but it IS validated: omit it and the key's own project is used, send it and it must name that project, otherwise the call is rejected with `403 project_not_attached` (a blank value is `400 invalid_project_header`, as above)."},"required":false,"name":"X-Paige-Project","in":"header"}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"path":{"type":"string"},"content":{"type":"string"},"environment":{"type":"string"}},"required":["path","content","environment"]}},"required":["success","data"]}}}},"400":{"description":"Validation / bad request (e.g. `invalid_request`, `invalid_cursor`, `outside_24h_window` is 409). Also `invalid_project_header` (any credential: `X-Paige-Project` was sent but blank) and, for multi-project MCP bearers, `project_required` (a mutation, or a read with no default, and no `X-Paige-Project`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid API key (`api_key_required` / `invalid_api_key` / `invalid_token` for a revoked or expired OAuth bearer).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the required scope, project inactive, or a write targets a platform-protected file (`insufficient_scope` / `project_inactive` / `subscription_inactive` / `protected_file`). Also `project_not_attached` (the `X-Paige-Project` project is not one this credential may act on — not attached to the MCP connection, or not the `pk_` key's own project; identical response whether or not it exists) and, for MCP bearers, `no_project_access` (the connection has no projects attached).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found (`not_found` / `*_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit (`rate_limited`) or daily send quota (`quota_exceeded`) exceeded. Carries `RateLimit-*` + `Retry-After` headers.","headers":{"RateLimit-Limit":{"description":"Requests permitted in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error (`server_error`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"put":{"operationId":"editFile","tags":["Build"],"summary":"Edit/create a file via the code agent","description":"Whole-file write (`content`) OR one exact replacement (`old_string`/`new_string`). Flow JSON is rejected — use the flow endpoints. Platform-critical / injected / auto-generated files (e.g. `routes/whatsapp_webhook.js`, `app.js`, `config/db.js`, `services/messages.js`, `config/mediaAssets.js`, matched across both flat and `src/` layouts) are rejected with `403 protected_file`; the file is left unchanged. Reads (`GET /v1/files`, `GET /v1/files/{path}`) are NOT restricted.\n\n**Required scope:** `code:write`","security":[{"ApiKeyAuth":[]}],"x-required-scopes":["code:write"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"path","in":"path"},{"schema":{"type":"string","description":"Target project id, for an MCP OAuth bearer (`mcp_at_…`) attached to more than one project — get ids from `GET /v1/projects`. Matched case-insensitively.\n\nOmit it and a READ falls back to the connection's default project; a **mutation** (any non-GET) on a connection with 2+ projects is rejected with `400 project_required` — a write is never defaulted to a guessed project. A connection with exactly one project never needs the header.\n\nScopes are checked against the SELECTED project only, never a union across the connection.\n\nFor a `pk_` API key the header selects nothing — one key is one project's context — but it IS validated: omit it and the key's own project is used, send it and it must name that project, otherwise the call is rejected with `403 project_not_attached` (a blank value is `400 invalid_project_header`, as above)."},"required":false,"name":"X-Paige-Project","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"content":{"type":"string","maxLength":1000000},"old_string":{"type":"string","maxLength":1000000},"new_string":{"type":"string","maxLength":1000000}}}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{}}},"required":["success","data"]}}}},"400":{"description":"Validation / bad request (e.g. `invalid_request`, `invalid_cursor`, `outside_24h_window` is 409). Also `invalid_project_header` (any credential: `X-Paige-Project` was sent but blank) and, for multi-project MCP bearers, `project_required` (a mutation, or a read with no default, and no `X-Paige-Project`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid API key (`api_key_required` / `invalid_api_key` / `invalid_token` for a revoked or expired OAuth bearer).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Out of credits (`insufficient_credits`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the required scope, project inactive, or a write targets a platform-protected file (`insufficient_scope` / `project_inactive` / `subscription_inactive` / `protected_file`). Also `project_not_attached` (the `X-Paige-Project` project is not one this credential may act on — not attached to the MCP connection, or not the `pk_` key's own project; identical response whether or not it exists) and, for MCP bearers, `no_project_access` (the connection has no projects attached).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit (`rate_limited`) or daily send quota (`quota_exceeded`) exceeded. Carries `RateLimit-*` + `Retry-After` headers.","headers":{"RateLimit-Limit":{"description":"Requests permitted in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error (`server_error`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/code":{"post":{"operationId":"buildCode","tags":["Build"],"summary":"Author/edit bot code from a natural-language instruction via the code agent","description":"Runs Paige's code agent (its system prompt, scaffold + connector awareness, and guardrails) in a bounded headless loop to write/edit the bot's code — the higher-quality counterpart to `PUT /v1/files/{path}`. Flow JSON is rejected (use the flow endpoints). Frozen/injected/auto-generated files (e.g. `services/messages.js`, `config/mediaAssets.js`, `package.json`, `.paige/**`) are refused, while the agent still authors the bot wiring it owns (`routes/*`, `app.js`, `config/db.js`). Consumes credits; rejected with `402 insufficient_credits` when out of balance. A truncated or failed agent run returns `502 build_failed`.\n\n**Required scope:** `code:write`","security":[{"ApiKeyAuth":[]}],"x-required-scopes":["code:write"],"parameters":[{"schema":{"type":"string","description":"Target project id, for an MCP OAuth bearer (`mcp_at_…`) attached to more than one project — get ids from `GET /v1/projects`. Matched case-insensitively.\n\nOmit it and a READ falls back to the connection's default project; a **mutation** (any non-GET) on a connection with 2+ projects is rejected with `400 project_required` — a write is never defaulted to a guessed project. A connection with exactly one project never needs the header.\n\nScopes are checked against the SELECTED project only, never a union across the connection.\n\nFor a `pk_` API key the header selects nothing — one key is one project's context — but it IS validated: omit it and the key's own project is used, send it and it must name that project, otherwise the call is rejected with `403 project_not_attached` (a blank value is `400 invalid_project_header`, as above)."},"required":false,"name":"X-Paige-Project","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"instruction":{"type":"string","minLength":1,"maxLength":10000,"description":"Natural-language description of the code to write or change. Paige's code agent authors/edits the bot files using its own system prompt, scaffold + connector awareness, and guardrails."}},"required":["instruction"]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"summary":{"type":"string"},"files_changed":{"type":"array","items":{"type":"string"}},"cancelled":{"type":"boolean"}},"required":["summary","files_changed","cancelled"]}},"required":["success","data"]}}}},"400":{"description":"Validation / bad request (e.g. `invalid_request`, `invalid_cursor`, `outside_24h_window` is 409). Also `invalid_project_header` (any credential: `X-Paige-Project` was sent but blank) and, for multi-project MCP bearers, `project_required` (a mutation, or a read with no default, and no `X-Paige-Project`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid API key (`api_key_required` / `invalid_api_key` / `invalid_token` for a revoked or expired OAuth bearer).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Out of credits (`insufficient_credits`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the required scope, project inactive, or a write targets a platform-protected file (`insufficient_scope` / `project_inactive` / `subscription_inactive` / `protected_file`). Also `project_not_attached` (the `X-Paige-Project` project is not one this credential may act on — not attached to the MCP connection, or not the `pk_` key's own project; identical response whether or not it exists) and, for MCP bearers, `no_project_access` (the connection has no projects attached).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit (`rate_limited`) or daily send quota (`quota_exceeded`) exceeded. Carries `RateLimit-*` + `Retry-After` headers.","headers":{"RateLimit-Limit":{"description":"Requests permitted in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error (`server_error`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"502":{"description":"Upstream Meta rejection (`invalid_template` / `meta_auth_error`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/deploy":{"post":{"operationId":"deploy","tags":["Build"],"summary":"Promote dev → production","description":"Promote dev → production\n\n**Required scope:** `code:write`","security":[{"ApiKeyAuth":[]}],"x-required-scopes":["code:write"],"parameters":[{"schema":{"type":"string","description":"Target project id, for an MCP OAuth bearer (`mcp_at_…`) attached to more than one project — get ids from `GET /v1/projects`. Matched case-insensitively.\n\nOmit it and a READ falls back to the connection's default project; a **mutation** (any non-GET) on a connection with 2+ projects is rejected with `400 project_required` — a write is never defaulted to a guessed project. A connection with exactly one project never needs the header.\n\nScopes are checked against the SELECTED project only, never a union across the connection.\n\nFor a `pk_` API key the header selects nothing — one key is one project's context — but it IS validated: omit it and the key's own project is used, send it and it must name that project, otherwise the call is rejected with `403 project_not_attached` (a blank value is `400 invalid_project_header`, as above)."},"required":false,"name":"X-Paige-Project","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"tables":{"type":"array","items":{"type":"string"}},"flows":{"type":"array","items":{"type":"string"}}}}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{}}},"required":["success","data"]}}}},"400":{"description":"Validation / bad request (e.g. `invalid_request`, `invalid_cursor`, `outside_24h_window` is 409). Also `invalid_project_header` (any credential: `X-Paige-Project` was sent but blank) and, for multi-project MCP bearers, `project_required` (a mutation, or a read with no default, and no `X-Paige-Project`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid API key (`api_key_required` / `invalid_api_key` / `invalid_token` for a revoked or expired OAuth bearer).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the required scope, project inactive, or a write targets a platform-protected file (`insufficient_scope` / `project_inactive` / `subscription_inactive` / `protected_file`). Also `project_not_attached` (the `X-Paige-Project` project is not one this credential may act on — not attached to the MCP connection, or not the `pk_` key's own project; identical response whether or not it exists) and, for MCP bearers, `no_project_access` (the connection has no projects attached).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit (`rate_limited`) or daily send quota (`quota_exceeded`) exceeded. Carries `RateLimit-*` + `Retry-After` headers.","headers":{"RateLimit-Limit":{"description":"Requests permitted in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error (`server_error`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/flows":{"post":{"operationId":"createFlow","tags":["Build"],"summary":"Generate a new WhatsApp Flow via the flows sub-agent","description":"Generate a new WhatsApp Flow via the flows sub-agent\n\n**Required scope:** `flows:write`","security":[{"ApiKeyAuth":[]}],"x-required-scopes":["flows:write"],"parameters":[{"schema":{"type":"string","description":"Target project id, for an MCP OAuth bearer (`mcp_at_…`) attached to more than one project — get ids from `GET /v1/projects`. Matched case-insensitively.\n\nOmit it and a READ falls back to the connection's default project; a **mutation** (any non-GET) on a connection with 2+ projects is rejected with `400 project_required` — a write is never defaulted to a guessed project. A connection with exactly one project never needs the header.\n\nScopes are checked against the SELECTED project only, never a union across the connection.\n\nFor a `pk_` API key the header selects nothing — one key is one project's context — but it IS validated: omit it and the key's own project is used, send it and it must name that project, otherwise the call is rejected with `403 project_not_attached` (a blank value is `400 invalid_project_header`, as above)."},"required":false,"name":"X-Paige-Project","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"instruction":{"type":"string","minLength":1,"maxLength":5000,"description":"Natural-language description of the WhatsApp Flow to build."}},"required":["instruction"]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{}}},"required":["success","data"]}}}},"400":{"description":"Validation / bad request (e.g. `invalid_request`, `invalid_cursor`, `outside_24h_window` is 409). Also `invalid_project_header` (any credential: `X-Paige-Project` was sent but blank) and, for multi-project MCP bearers, `project_required` (a mutation, or a read with no default, and no `X-Paige-Project`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid API key (`api_key_required` / `invalid_api_key` / `invalid_token` for a revoked or expired OAuth bearer).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Out of credits (`insufficient_credits`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the required scope, project inactive, or a write targets a platform-protected file (`insufficient_scope` / `project_inactive` / `subscription_inactive` / `protected_file`). Also `project_not_attached` (the `X-Paige-Project` project is not one this credential may act on — not attached to the MCP connection, or not the `pk_` key's own project; identical response whether or not it exists) and, for MCP bearers, `no_project_access` (the connection has no projects attached).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit (`rate_limited`) or daily send quota (`quota_exceeded`) exceeded. Carries `RateLimit-*` + `Retry-After` headers.","headers":{"RateLimit-Limit":{"description":"Requests permitted in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error (`server_error`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/flows/{id}":{"patch":{"operationId":"updateFlow","tags":["Build"],"summary":"Update a flow via the flows sub-agent (id = flow name)","description":"Update a flow via the flows sub-agent (id = flow name)\n\n**Required scope:** `flows:write`","security":[{"ApiKeyAuth":[]}],"x-required-scopes":["flows:write"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","description":"Target project id, for an MCP OAuth bearer (`mcp_at_…`) attached to more than one project — get ids from `GET /v1/projects`. Matched case-insensitively.\n\nOmit it and a READ falls back to the connection's default project; a **mutation** (any non-GET) on a connection with 2+ projects is rejected with `400 project_required` — a write is never defaulted to a guessed project. A connection with exactly one project never needs the header.\n\nScopes are checked against the SELECTED project only, never a union across the connection.\n\nFor a `pk_` API key the header selects nothing — one key is one project's context — but it IS validated: omit it and the key's own project is used, send it and it must name that project, otherwise the call is rejected with `403 project_not_attached` (a blank value is `400 invalid_project_header`, as above)."},"required":false,"name":"X-Paige-Project","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"change_request":{"type":"string","minLength":1,"maxLength":5000,"description":"Natural-language description of the change to make to the flow."}},"required":["change_request"]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{}}},"required":["success","data"]}}}},"400":{"description":"Validation / bad request (e.g. `invalid_request`, `invalid_cursor`, `outside_24h_window` is 409). Also `invalid_project_header` (any credential: `X-Paige-Project` was sent but blank) and, for multi-project MCP bearers, `project_required` (a mutation, or a read with no default, and no `X-Paige-Project`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid API key (`api_key_required` / `invalid_api_key` / `invalid_token` for a revoked or expired OAuth bearer).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"402":{"description":"Out of credits (`insufficient_credits`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the required scope, project inactive, or a write targets a platform-protected file (`insufficient_scope` / `project_inactive` / `subscription_inactive` / `protected_file`). Also `project_not_attached` (the `X-Paige-Project` project is not one this credential may act on — not attached to the MCP connection, or not the `pk_` key's own project; identical response whether or not it exists) and, for MCP bearers, `no_project_access` (the connection has no projects attached).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit (`rate_limited`) or daily send quota (`quota_exceeded`) exceeded. Carries `RateLimit-*` + `Retry-After` headers.","headers":{"RateLimit-Limit":{"description":"Requests permitted in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error (`server_error`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/segments":{"post":{"operationId":"createSegment","tags":["Broadcasts"],"summary":"Create + save a reusable audience segment","description":"Create + save a reusable audience segment\n\n**Required scope:** `broadcasts:manage`","security":[{"ApiKeyAuth":[]}],"x-required-scopes":["broadcasts:manage"],"parameters":[{"schema":{"type":"string","description":"Target project id, for an MCP OAuth bearer (`mcp_at_…`) attached to more than one project — get ids from `GET /v1/projects`. Matched case-insensitively.\n\nOmit it and a READ falls back to the connection's default project; a **mutation** (any non-GET) on a connection with 2+ projects is rejected with `400 project_required` — a write is never defaulted to a guessed project. A connection with exactly one project never needs the header.\n\nScopes are checked against the SELECTED project only, never a union across the connection.\n\nFor a `pk_` API key the header selects nothing — one key is one project's context — but it IS validated: omit it and the key's own project is used, send it and it must name that project, otherwise the call is rejected with `403 project_not_attached` (a blank value is `400 invalid_project_header`, as above)."},"required":false,"name":"X-Paige-Project","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":255},"filter":{"type":"object","properties":{"opted_in":{"type":"boolean"},"tags":{"type":"array","items":{"type":"string"}},"clicked":{"anyOf":[{"type":"string"},{"type":"object","additionalProperties":{}}]},"name_contains":{"type":"string"},"number_contains":{"type":"string"},"include_ids":{"type":"array","items":{"anyOf":[{"type":"number"},{"type":"string"}]}}}}},"required":["name"]}}}},"responses":{"201":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{}}},"required":["success","data"]}}}},"400":{"description":"Validation / bad request (e.g. `invalid_request`, `invalid_cursor`, `outside_24h_window` is 409). Also `invalid_project_header` (any credential: `X-Paige-Project` was sent but blank) and, for multi-project MCP bearers, `project_required` (a mutation, or a read with no default, and no `X-Paige-Project`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid API key (`api_key_required` / `invalid_api_key` / `invalid_token` for a revoked or expired OAuth bearer).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the required scope, project inactive, or a write targets a platform-protected file (`insufficient_scope` / `project_inactive` / `subscription_inactive` / `protected_file`). Also `project_not_attached` (the `X-Paige-Project` project is not one this credential may act on — not attached to the MCP connection, or not the `pk_` key's own project; identical response whether or not it exists) and, for MCP bearers, `no_project_access` (the connection has no projects attached).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"409":{"description":"Conflict — 24h window closed (`outside_24h_window`) or idempotency (`idempotency_in_progress` / `idempotency_key_mismatch`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit (`rate_limited`) or daily send quota (`quota_exceeded`) exceeded. Carries `RateLimit-*` + `Retry-After` headers.","headers":{"RateLimit-Limit":{"description":"Requests permitted in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error (`server_error`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/segments/preview":{"post":{"operationId":"previewSegment","tags":["Broadcasts"],"summary":"Live contact count for a filter (no save)","description":"Live contact count for a filter (no save)\n\n**Required scope:** `broadcasts:manage`","security":[{"ApiKeyAuth":[]}],"x-required-scopes":["broadcasts:manage"],"parameters":[{"schema":{"type":"string","description":"Target project id, for an MCP OAuth bearer (`mcp_at_…`) attached to more than one project — get ids from `GET /v1/projects`. Matched case-insensitively.\n\nOmit it and a READ falls back to the connection's default project; a **mutation** (any non-GET) on a connection with 2+ projects is rejected with `400 project_required` — a write is never defaulted to a guessed project. A connection with exactly one project never needs the header.\n\nScopes are checked against the SELECTED project only, never a union across the connection.\n\nFor a `pk_` API key the header selects nothing — one key is one project's context — but it IS validated: omit it and the key's own project is used, send it and it must name that project, otherwise the call is rejected with `403 project_not_attached` (a blank value is `400 invalid_project_header`, as above)."},"required":false,"name":"X-Paige-Project","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"filter":{"type":"object","properties":{"opted_in":{"type":"boolean"},"tags":{"type":"array","items":{"type":"string"}},"clicked":{"anyOf":[{"type":"string"},{"type":"object","additionalProperties":{}}]},"name_contains":{"type":"string"},"number_contains":{"type":"string"},"include_ids":{"type":"array","items":{"anyOf":[{"type":"number"},{"type":"string"}]}}}}},"required":["filter"]}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"filter":{"type":"object","properties":{}},"contact_count":{"type":"number"}},"required":["filter","contact_count"]}},"required":["success","data"]}}}},"400":{"description":"Validation / bad request (e.g. `invalid_request`, `invalid_cursor`, `outside_24h_window` is 409). Also `invalid_project_header` (any credential: `X-Paige-Project` was sent but blank) and, for multi-project MCP bearers, `project_required` (a mutation, or a read with no default, and no `X-Paige-Project`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid API key (`api_key_required` / `invalid_api_key` / `invalid_token` for a revoked or expired OAuth bearer).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the required scope, project inactive, or a write targets a platform-protected file (`insufficient_scope` / `project_inactive` / `subscription_inactive` / `protected_file`). Also `project_not_attached` (the `X-Paige-Project` project is not one this credential may act on — not attached to the MCP connection, or not the `pk_` key's own project; identical response whether or not it exists) and, for MCP bearers, `no_project_access` (the connection has no projects attached).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit (`rate_limited`) or daily send quota (`quota_exceeded`) exceeded. Carries `RateLimit-*` + `Retry-After` headers.","headers":{"RateLimit-Limit":{"description":"Requests permitted in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error (`server_error`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/broadcasts":{"post":{"operationId":"assembleBroadcast","tags":["Broadcasts"],"summary":"Assemble a broadcast — ALWAYS lands in pending_approval (never sends)","description":"Guardrail: an assembled broadcast is held in `pending_approval` and can only go out via the dashboard approve flow. A MARKETING template requires an opted-in audience.\n\n**Required scope:** `broadcasts:manage`","security":[{"ApiKeyAuth":[]}],"x-required-scopes":["broadcasts:manage"],"parameters":[{"schema":{"type":"string","description":"Target project id, for an MCP OAuth bearer (`mcp_at_…`) attached to more than one project — get ids from `GET /v1/projects`. Matched case-insensitively.\n\nOmit it and a READ falls back to the connection's default project; a **mutation** (any non-GET) on a connection with 2+ projects is rejected with `400 project_required` — a write is never defaulted to a guessed project. A connection with exactly one project never needs the header.\n\nScopes are checked against the SELECTED project only, never a union across the connection.\n\nFor a `pk_` API key the header selects nothing — one key is one project's context — but it IS validated: omit it and the key's own project is used, send it and it must name that project, otherwise the call is rejected with `403 project_not_attached` (a blank value is `400 invalid_project_header`, as above)."},"required":false,"name":"X-Paige-Project","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":255},"segment_id":{"type":"string"},"opted_in":{"type":"boolean"},"tags":{"type":"array","items":{"type":"string"}},"clicked":{"type":"object","properties":{"value":{"type":"string"},"since":{"type":"string"}}},"template_name":{"type":"string"},"template_id":{"type":"string"},"template_language":{"type":"string"},"variable_mapping":{"type":"object","additionalProperties":{}},"schedule":{"type":"string"}},"required":["name"]}}}},"responses":{"201":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{}}},"required":["success","data"]}}}},"400":{"description":"Validation / bad request (e.g. `invalid_request`, `invalid_cursor`, `outside_24h_window` is 409). Also `invalid_project_header` (any credential: `X-Paige-Project` was sent but blank) and, for multi-project MCP bearers, `project_required` (a mutation, or a read with no default, and no `X-Paige-Project`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid API key (`api_key_required` / `invalid_api_key` / `invalid_token` for a revoked or expired OAuth bearer).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the required scope, project inactive, or a write targets a platform-protected file (`insufficient_scope` / `project_inactive` / `subscription_inactive` / `protected_file`). Also `project_not_attached` (the `X-Paige-Project` project is not one this credential may act on — not attached to the MCP connection, or not the `pk_` key's own project; identical response whether or not it exists) and, for MCP bearers, `no_project_access` (the connection has no projects attached).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found (`not_found` / `*_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit (`rate_limited`) or daily send quota (`quota_exceeded`) exceeded. Carries `RateLimit-*` + `Retry-After` headers.","headers":{"RateLimit-Limit":{"description":"Requests permitted in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error (`server_error`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/broadcasts/{id}":{"get":{"operationId":"getBroadcast","tags":["Broadcasts"],"summary":"Broadcast status + rollup counts","description":"Broadcast status + rollup counts\n\n**Required scope:** `broadcasts:read`","security":[{"ApiKeyAuth":[]}],"x-required-scopes":["broadcasts:read"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","description":"Target project id, for an MCP OAuth bearer (`mcp_at_…`) attached to more than one project — get ids from `GET /v1/projects`. Matched case-insensitively.\n\nOmit it and a READ falls back to the connection's default project; a **mutation** (any non-GET) on a connection with 2+ projects is rejected with `400 project_required` — a write is never defaulted to a guessed project. A connection with exactly one project never needs the header.\n\nScopes are checked against the SELECTED project only, never a union across the connection.\n\nFor a `pk_` API key the header selects nothing — one key is one project's context — but it IS validated: omit it and the key's own project is used, send it and it must name that project, otherwise the call is rejected with `403 project_not_attached` (a blank value is `400 invalid_project_header`, as above)."},"required":false,"name":"X-Paige-Project","in":"header"}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{}}},"required":["success","data"]}}}},"400":{"description":"Validation / bad request (e.g. `invalid_request`, `invalid_cursor`, `outside_24h_window` is 409). Also `invalid_project_header` (any credential: `X-Paige-Project` was sent but blank) and, for multi-project MCP bearers, `project_required` (a mutation, or a read with no default, and no `X-Paige-Project`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid API key (`api_key_required` / `invalid_api_key` / `invalid_token` for a revoked or expired OAuth bearer).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the required scope, project inactive, or a write targets a platform-protected file (`insufficient_scope` / `project_inactive` / `subscription_inactive` / `protected_file`). Also `project_not_attached` (the `X-Paige-Project` project is not one this credential may act on — not attached to the MCP connection, or not the `pk_` key's own project; identical response whether or not it exists) and, for MCP bearers, `no_project_access` (the connection has no projects attached).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found (`not_found` / `*_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit (`rate_limited`) or daily send quota (`quota_exceeded`) exceeded. Carries `RateLimit-*` + `Retry-After` headers.","headers":{"RateLimit-Limit":{"description":"Requests permitted in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error (`server_error`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/broadcasts/{id}/recipients":{"get":{"operationId":"listBroadcastRecipients","tags":["Broadcasts"],"summary":"Per-recipient drill-down","description":"Per-recipient drill-down\n\n**Required scope:** `broadcasts:read`","security":[{"ApiKeyAuth":[]}],"x-required-scopes":["broadcasts:read"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":["integer","null"]},"required":false,"name":"limit","in":"query"},{"schema":{"type":["integer","null"]},"required":false,"name":"offset","in":"query"},{"schema":{"type":"string","enum":["queued","sent","delivered","read","failed","canceled"]},"required":false,"name":"status","in":"query"},{"schema":{"type":"string","description":"Target project id, for an MCP OAuth bearer (`mcp_at_…`) attached to more than one project — get ids from `GET /v1/projects`. Matched case-insensitively.\n\nOmit it and a READ falls back to the connection's default project; a **mutation** (any non-GET) on a connection with 2+ projects is rejected with `400 project_required` — a write is never defaulted to a guessed project. A connection with exactly one project never needs the header.\n\nScopes are checked against the SELECTED project only, never a union across the connection.\n\nFor a `pk_` API key the header selects nothing — one key is one project's context — but it IS validated: omit it and the key's own project is used, send it and it must name that project, otherwise the call is rejected with `403 project_not_attached` (a blank value is `400 invalid_project_header`, as above)."},"required":false,"name":"X-Paige-Project","in":"header"}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"recipients":{"type":"array","items":{"type":"object","properties":{}}},"total":{"type":"number"},"limit":{"type":"number"},"offset":{"type":"number"}},"required":["recipients","total","limit","offset"]}},"required":["success","data"]}}}},"400":{"description":"Validation / bad request (e.g. `invalid_request`, `invalid_cursor`, `outside_24h_window` is 409). Also `invalid_project_header` (any credential: `X-Paige-Project` was sent but blank) and, for multi-project MCP bearers, `project_required` (a mutation, or a read with no default, and no `X-Paige-Project`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid API key (`api_key_required` / `invalid_api_key` / `invalid_token` for a revoked or expired OAuth bearer).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the required scope, project inactive, or a write targets a platform-protected file (`insufficient_scope` / `project_inactive` / `subscription_inactive` / `protected_file`). Also `project_not_attached` (the `X-Paige-Project` project is not one this credential may act on — not attached to the MCP connection, or not the `pk_` key's own project; identical response whether or not it exists) and, for MCP bearers, `no_project_access` (the connection has no projects attached).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found (`not_found` / `*_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit (`rate_limited`) or daily send quota (`quota_exceeded`) exceeded. Carries `RateLimit-*` + `Retry-After` headers.","headers":{"RateLimit-Limit":{"description":"Requests permitted in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error (`server_error`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/webhooks":{"post":{"operationId":"createWebhook","tags":["Webhooks"],"summary":"Register an outbound webhook (returns the signing secret once)","description":"The `whsec_…` signing secret is returned EXACTLY once. Subscribing to a platform event (`message.received`/`message.status`/`template.approved`/`template.rejected`) also requires the matching `events:*` scope.\n\n**Required scope:** `webhooks:manage`","security":[{"ApiKeyAuth":[]}],"x-required-scopes":["webhooks:manage"],"parameters":[{"schema":{"type":"string","description":"Target project id, for an MCP OAuth bearer (`mcp_at_…`) attached to more than one project — get ids from `GET /v1/projects`. Matched case-insensitively.\n\nOmit it and a READ falls back to the connection's default project; a **mutation** (any non-GET) on a connection with 2+ projects is rejected with `400 project_required` — a write is never defaulted to a guessed project. A connection with exactly one project never needs the header.\n\nScopes are checked against the SELECTED project only, never a union across the connection.\n\nFor a `pk_` API key the header selects nothing — one key is one project's context — but it IS validated: omit it and the key's own project is used, send it and it must name that project, otherwise the call is rejected with `403 project_not_attached` (a blank value is `400 invalid_project_header`, as above)."},"required":false,"name":"X-Paige-Project","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","maxLength":512,"format":"uri"},"events":{"type":"array","items":{"type":"string","minLength":1,"maxLength":100,"pattern":"^[A-Za-z0-9._-]+$"}},"is_active":{"type":"boolean"}},"required":["url","events"],"additionalProperties":false}}}},"responses":{"201":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"webhook":{"type":"object","properties":{}},"secret":{"type":"string"}},"required":["webhook","secret"]}},"required":["success","data"]}}}},"400":{"description":"Validation / bad request (e.g. `invalid_request`, `invalid_cursor`, `outside_24h_window` is 409). Also `invalid_project_header` (any credential: `X-Paige-Project` was sent but blank) and, for multi-project MCP bearers, `project_required` (a mutation, or a read with no default, and no `X-Paige-Project`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid API key (`api_key_required` / `invalid_api_key` / `invalid_token` for a revoked or expired OAuth bearer).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the required scope, project inactive, or a write targets a platform-protected file (`insufficient_scope` / `project_inactive` / `subscription_inactive` / `protected_file`). Also `project_not_attached` (the `X-Paige-Project` project is not one this credential may act on — not attached to the MCP connection, or not the `pk_` key's own project; identical response whether or not it exists) and, for MCP bearers, `no_project_access` (the connection has no projects attached).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit (`rate_limited`) or daily send quota (`quota_exceeded`) exceeded. Carries `RateLimit-*` + `Retry-After` headers.","headers":{"RateLimit-Limit":{"description":"Requests permitted in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error (`server_error`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"get":{"operationId":"listWebhooks","tags":["Webhooks"],"summary":"List webhook registrations (never the secret)","description":"List webhook registrations (never the secret)\n\n**Required scope:** `webhooks:manage`","security":[{"ApiKeyAuth":[]}],"x-required-scopes":["webhooks:manage"],"parameters":[{"schema":{"type":"string","description":"Target project id, for an MCP OAuth bearer (`mcp_at_…`) attached to more than one project — get ids from `GET /v1/projects`. Matched case-insensitively.\n\nOmit it and a READ falls back to the connection's default project; a **mutation** (any non-GET) on a connection with 2+ projects is rejected with `400 project_required` — a write is never defaulted to a guessed project. A connection with exactly one project never needs the header.\n\nScopes are checked against the SELECTED project only, never a union across the connection.\n\nFor a `pk_` API key the header selects nothing — one key is one project's context — but it IS validated: omit it and the key's own project is used, send it and it must name that project, otherwise the call is rejected with `403 project_not_attached` (a blank value is `400 invalid_project_header`, as above)."},"required":false,"name":"X-Paige-Project","in":"header"}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"webhooks":{"type":"array","items":{"type":"object","properties":{}}}},"required":["webhooks"]}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid API key (`api_key_required` / `invalid_api_key` / `invalid_token` for a revoked or expired OAuth bearer).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the required scope, project inactive, or a write targets a platform-protected file (`insufficient_scope` / `project_inactive` / `subscription_inactive` / `protected_file`). Also `project_not_attached` (the `X-Paige-Project` project is not one this credential may act on — not attached to the MCP connection, or not the `pk_` key's own project; identical response whether or not it exists) and, for MCP bearers, `no_project_access` (the connection has no projects attached).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit (`rate_limited`) or daily send quota (`quota_exceeded`) exceeded. Carries `RateLimit-*` + `Retry-After` headers.","headers":{"RateLimit-Limit":{"description":"Requests permitted in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error (`server_error`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/webhooks/{id}":{"get":{"operationId":"getWebhook","tags":["Webhooks"],"summary":"Retrieve one webhook registration (never the secret)","description":"Retrieve one webhook registration (never the secret)\n\n**Required scope:** `webhooks:manage`","security":[{"ApiKeyAuth":[]}],"x-required-scopes":["webhooks:manage"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","description":"Target project id, for an MCP OAuth bearer (`mcp_at_…`) attached to more than one project — get ids from `GET /v1/projects`. Matched case-insensitively.\n\nOmit it and a READ falls back to the connection's default project; a **mutation** (any non-GET) on a connection with 2+ projects is rejected with `400 project_required` — a write is never defaulted to a guessed project. A connection with exactly one project never needs the header.\n\nScopes are checked against the SELECTED project only, never a union across the connection.\n\nFor a `pk_` API key the header selects nothing — one key is one project's context — but it IS validated: omit it and the key's own project is used, send it and it must name that project, otherwise the call is rejected with `403 project_not_attached` (a blank value is `400 invalid_project_header`, as above)."},"required":false,"name":"X-Paige-Project","in":"header"}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"webhook":{"type":"object","properties":{}}},"required":["webhook"]}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid API key (`api_key_required` / `invalid_api_key` / `invalid_token` for a revoked or expired OAuth bearer).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the required scope, project inactive, or a write targets a platform-protected file (`insufficient_scope` / `project_inactive` / `subscription_inactive` / `protected_file`). Also `project_not_attached` (the `X-Paige-Project` project is not one this credential may act on — not attached to the MCP connection, or not the `pk_` key's own project; identical response whether or not it exists) and, for MCP bearers, `no_project_access` (the connection has no projects attached).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found (`not_found` / `*_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit (`rate_limited`) or daily send quota (`quota_exceeded`) exceeded. Carries `RateLimit-*` + `Retry-After` headers.","headers":{"RateLimit-Limit":{"description":"Requests permitted in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error (`server_error`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"operationId":"updateWebhook","tags":["Webhooks"],"summary":"Partial update of a webhook (url / events / is_active)","description":"Partial update of a webhook (url / events / is_active)\n\n**Required scope:** `webhooks:manage`","security":[{"ApiKeyAuth":[]}],"x-required-scopes":["webhooks:manage"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","description":"Target project id, for an MCP OAuth bearer (`mcp_at_…`) attached to more than one project — get ids from `GET /v1/projects`. Matched case-insensitively.\n\nOmit it and a READ falls back to the connection's default project; a **mutation** (any non-GET) on a connection with 2+ projects is rejected with `400 project_required` — a write is never defaulted to a guessed project. A connection with exactly one project never needs the header.\n\nScopes are checked against the SELECTED project only, never a union across the connection.\n\nFor a `pk_` API key the header selects nothing — one key is one project's context — but it IS validated: omit it and the key's own project is used, send it and it must name that project, otherwise the call is rejected with `403 project_not_attached` (a blank value is `400 invalid_project_header`, as above)."},"required":false,"name":"X-Paige-Project","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","maxLength":512,"format":"uri"},"events":{"type":"array","items":{"type":"string","minLength":1,"maxLength":100,"pattern":"^[A-Za-z0-9._-]+$"}},"is_active":{"type":"boolean"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"webhook":{"type":"object","properties":{}}},"required":["webhook"]}},"required":["success","data"]}}}},"400":{"description":"Validation / bad request (e.g. `invalid_request`, `invalid_cursor`, `outside_24h_window` is 409). Also `invalid_project_header` (any credential: `X-Paige-Project` was sent but blank) and, for multi-project MCP bearers, `project_required` (a mutation, or a read with no default, and no `X-Paige-Project`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid API key (`api_key_required` / `invalid_api_key` / `invalid_token` for a revoked or expired OAuth bearer).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the required scope, project inactive, or a write targets a platform-protected file (`insufficient_scope` / `project_inactive` / `subscription_inactive` / `protected_file`). Also `project_not_attached` (the `X-Paige-Project` project is not one this credential may act on — not attached to the MCP connection, or not the `pk_` key's own project; identical response whether or not it exists) and, for MCP bearers, `no_project_access` (the connection has no projects attached).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found (`not_found` / `*_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit (`rate_limited`) or daily send quota (`quota_exceeded`) exceeded. Carries `RateLimit-*` + `Retry-After` headers.","headers":{"RateLimit-Limit":{"description":"Requests permitted in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error (`server_error`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"delete":{"operationId":"deleteWebhook","tags":["Webhooks"],"summary":"Delete a webhook registration","description":"Delete a webhook registration\n\n**Required scope:** `webhooks:manage`","security":[{"ApiKeyAuth":[]}],"x-required-scopes":["webhooks:manage"],"parameters":[{"schema":{"type":"string"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","description":"Target project id, for an MCP OAuth bearer (`mcp_at_…`) attached to more than one project — get ids from `GET /v1/projects`. Matched case-insensitively.\n\nOmit it and a READ falls back to the connection's default project; a **mutation** (any non-GET) on a connection with 2+ projects is rejected with `400 project_required` — a write is never defaulted to a guessed project. A connection with exactly one project never needs the header.\n\nScopes are checked against the SELECTED project only, never a union across the connection.\n\nFor a `pk_` API key the header selects nothing — one key is one project's context — but it IS validated: omit it and the key's own project is used, send it and it must name that project, otherwise the call is rejected with `403 project_not_attached` (a blank value is `400 invalid_project_header`, as above)."},"required":false,"name":"X-Paige-Project","in":"header"}],"responses":{"200":{"description":"Success.","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[true]},"data":{"type":"object","properties":{"deleted":{"type":"boolean"},"id":{"type":"string"}},"required":["deleted","id"]}},"required":["success","data"]}}}},"401":{"description":"Missing or invalid API key (`api_key_required` / `invalid_api_key` / `invalid_token` for a revoked or expired OAuth bearer).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the required scope, project inactive, or a write targets a platform-protected file (`insufficient_scope` / `project_inactive` / `subscription_inactive` / `protected_file`). Also `project_not_attached` (the `X-Paige-Project` project is not one this credential may act on — not attached to the MCP connection, or not the `pk_` key's own project; identical response whether or not it exists) and, for MCP bearers, `no_project_access` (the connection has no projects attached).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Resource not found (`not_found` / `*_not_found`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Rate limit (`rate_limited`) or daily send quota (`quota_exceeded`) exceeded. Carries `RateLimit-*` + `Retry-After` headers.","headers":{"RateLimit-Limit":{"description":"Requests permitted in the current window.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Requests remaining in the current window.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server error (`server_error`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}},"webhooks":{},"x-scopes":[{"scope":"messages:send","label":"Send messages","description":"Send WhatsApp session and template messages."},{"scope":"conversations:read","label":"Read conversations","description":"List conversations and read their messages and delivery status."},{"scope":"conversations:write","label":"Update conversations","description":"Change a conversation's state (e.g. handover / open / closed)."},{"scope":"contacts:read","label":"Read contacts","description":"Read contact records."},{"scope":"contacts:write","label":"Update contacts","description":"Create or update a contact name and tags."},{"scope":"broadcasts:read","label":"Read broadcasts","description":"View broadcast campaigns and their status."},{"scope":"broadcasts:manage","label":"Manage broadcasts","description":"Create, schedule, and send broadcast campaigns."},{"scope":"templates:read","label":"Read templates","description":"List message templates and their approval status."},{"scope":"templates:manage","label":"Manage templates","description":"Create and delete WhatsApp message templates."},{"scope":"flows:read","label":"Read flows","description":"View WhatsApp Flows."},{"scope":"code:read","label":"Read code","description":"List and read the project bot code files."},{"scope":"code:write","label":"Edit code","description":"Edit project code files and deploy dev → production."},{"scope":"flows:write","label":"Build flows","description":"Generate and update WhatsApp Flows (routed through the flows agent)."},{"scope":"webhooks:manage","label":"Manage webhooks","description":"Register and manage outbound webhook subscriptions."},{"scope":"events:message_received","label":"Event: message received","description":"Deliver a webhook when an inbound message arrives."},{"scope":"events:message_status","label":"Event: message status","description":"Deliver a webhook when a message delivery status changes."},{"scope":"events:template_status","label":"Event: template status","description":"Deliver a webhook when a template approval status changes."}]}