{"openapi":"3.1.0","info":{"title":"Brightshoal API","version":"1.0.0"},"servers":[{"url":"https://api.myleadsfactory.tech"}],"tags":[{"name":"Assistants","description":"An assistant is one agent: its instructions, model, voice, tools and channels. Every call and chat runs on an assistant, and every update creates a new version."},{"name":"Knowledge","description":"Knowledge bases hold the pages, documents and text an assistant answers from. Sources are revised, approved and published as immutable publications, then attached to assistants."},{"name":"Phone numbers","description":"Search, buy or import numbers, then attach an assistant so it answers inbound calls on that number."},{"name":"Calls","description":"Place outbound phone calls, start browser calls, and read every call back with its transcript, costs, recordings and analysis."},{"name":"Publishable keys","description":"Publishable keys (`pk_live_...`) let a browser start web chats with one assistant from the origins you allow. They are safe to ship in page source; secret API keys are not."},{"name":"Public chat API","description":"The versioned API for your own chat interface, in a page or an app: start a conversation with a publishable key, then send messages with the conversation token it returns. It runs the same conversations as the chat widget."},{"name":"WhatsApp","description":"Connect a WhatsApp Business Account from Meta and assign its numbers to assistants."},{"name":"Provider credentials","description":"Bring your own provider accounts (model, voice and telephony providers). Secrets are encrypted at rest and never returned."},{"name":"Voices and models","description":"Discover the voices and models a provider offers, and the registry of everything an assistant can be configured with."},{"name":"Conversations","description":"Web chat conversations with their messages, costs and lead. Your team can take one over, reply as a person and hand it back to the assistant."},{"name":"Leads","description":"The details an assistant captured from a visitor, and the outcome you report back once your team follows up."},{"name":"Webhooks","description":"Subscribe an HTTPS endpoint to events (calls ending, messages, leads) and inspect every delivery attempt."},{"name":"Call analytics","description":"Call volume and latency over a time range, per project or assistant."},{"name":"Chat analytics","description":"Web chat health, quality, the visitor funnel, trends and incidents, with notification subscriptions."},{"name":"Alerts","description":"Rules that notify you when a metric crosses a threshold."},{"name":"Audit log","description":"Who changed what in your organization, newest first."},{"name":"Projects","description":"Projects group assistants, numbers and keys inside an organization."},{"name":"Members","description":"The people in your organization and their roles."}],"paths":{"/api/assistants":{"post":{"tags":["Assistants"],"summary":"Create an assistant","description":"Creates an assistant at version 1. `config` needs `agent.instructions` and `session.llm`, `session.stt` and `session.tts`, each with a `provider` and `model` from the registry; everything else has a default. An org-wide key must name `project_id`. Validation checks the shape, not that the provider account works: place a test call or chat to be sure.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssistantCreate"}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Assistant"}},"required":["data"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"post-assistants","security":[{"apiKey":[]}]},"get":{"tags":["Assistants"],"summary":"List assistants","description":"Every assistant in scope with its full current configuration. Secrets in the config come back as `<key>_set` flags.","parameters":[{"name":"project_id","in":"query","required":false,"schema":{"type":"string"},"description":"Only this project's assistants."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Assistant"}}},"required":["data"]}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"get-assistants","security":[{"apiKey":[]}]}},"/api/assistants/{id}":{"get":{"tags":["Assistants"],"summary":"Get an assistant","description":"One assistant with its current configuration and version.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Assistant"}},"required":["data"]}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Assistant id, `asst_...`."}],"operationId":"get-assistants-id","security":[{"apiKey":[]}]},"patch":{"tags":["Assistants"],"summary":"Update an assistant","description":"Send `name`, `config` or both. A `config` replaces the whole stored configuration (it is not merged), except that omitted secrets and tool request specs are kept: read the assistant, change what you need and send the full config back. Each update bumps `version`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssistantPatch"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Assistant"}},"required":["data"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Assistant id, `asst_...`."}],"operationId":"patch-assistants-id","security":[{"apiKey":[]}]},"delete":{"tags":["Assistants"],"summary":"Delete an assistant","description":"Deletes the assistant. Its past calls, conversations and leads stay readable.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/DeleteResult"}},"required":["data"]}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Assistant id, `asst_...`."}],"operationId":"delete-assistants-id","security":[{"apiKey":[]}]}},"/api/assistant-avatars":{"post":{"tags":["Assistants"],"summary":"Upload an assistant avatar","description":"Stores a PNG, JPEG or WebP (at most 512 KB and 4096 pixels a side) as a public org asset and returns its URL, for `channels.web_chat.appearance.avatar_url`. The same picture always gets the same URL.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssistantAvatarUpload"}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AssistantAvatar"}},"required":["data"]}}}},"400":{"description":"`invalid_request` for a malformed body, or `avatar_empty`, `avatar_too_large`, `avatar_type_mismatch` (the bytes are not the declared image type) or `avatar_dimensions`, in `error`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`asset_storage_not_configured`: this deployment has no storage for uploaded images.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"post-assistant-avatars","security":[{"apiKey":[]}]}},"/api/assistants/{id}/chat-test-conversation":{"post":{"tags":["Assistants"],"summary":"Start a test chat conversation","description":"Starts a one-hour conversation with an assistant whose web chat is on, without a publishable key or an origin check, for trying a prompt from your own tooling. Use the returned token with the public chat routes. Test conversations carry `metadata.test` and are left out of lists and analytics unless asked for.","responses":{"201":{"description":"Test conversation created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"conversation_id":{"type":"string"},"token":{"type":"string"}},"required":["conversation_id","token"]}},"required":["data"]}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`web_chat_disabled`: turn on the assistant's `channels.web_chat.enabled` first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Assistant id, `asst_...`."}],"operationId":"post-assistants-id-chat-test-conversation","security":[{"apiKey":[]}]}},"/api/assistants/{id}/draft":{"get":{"tags":["Assistants"],"summary":"Get the assistant's draft","description":"The config being worked on in test mode, or null when there is none. Test chats (test keys and the console's test chat) run it; visitors never do until it is published.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AssistantDraft"}},"required":["data"]}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"operationId":"get-assistants-id-draft","security":[{"apiKey":[]}]},"put":{"tags":["Assistants"],"summary":"Save the assistant's draft","description":"Starts a draft from the live config, or replaces the current one. Credentials and tool request specs left out are kept from the stored draft (or from live for a new draft).","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssistantDraftSave"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AssistantDraft"}},"required":["data"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"operationId":"put-assistants-id-draft","security":[{"apiKey":[]}]},"delete":{"tags":["Assistants"],"summary":"Discard the assistant's draft","description":"Throws the draft away and returns the assistant; the live config is unchanged. Succeeds when there is no draft too.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"expected_draft_revision","in":"query","required":false,"schema":{"type":"integer","minimum":0},"description":"The draft_revision the caller looked at; a newer draft is not discarded (409 draft_conflict)."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Assistant"}},"required":["data"]}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"delete-assistants-id-draft","security":[{"apiKey":[]}]}},"/api/assistants/{id}/draft/rebase":{"post":{"tags":["Assistants"],"summary":"Update the draft from live","description":"After live changed under a draft (a stale draft), lays what the draft changed since its base version over today's live config, so it can be published without undoing live's change. Settings both changed differently are refused with 409 rebase_conflict and their `paths`, unless keep_draft keeps the draft's values; those paths are returned as `kept_draft`. Also 409 no_draft, draft_conflict, rebase_unavailable when the base version is not in history, or rebase_invalid (with `issues`) when the merged config would not be valid; the draft is then left as it was.","requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssistantDraftRebase"}}}},"responses":{"200":{"description":"The updated draft, and the settings where the draft's value was kept over live's.","content":{"application/json":{"schema":{"type":"object","required":["data","kept_draft"],"properties":{"data":{"$ref":"#/components/schemas/AssistantDraft"},"kept_draft":{"type":"array","items":{"type":"string"}}}}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"operationId":"post-assistants-id-draft-rebase","security":[{"apiKey":[]}]}},"/api/assistants/{id}/publish":{"post":{"tags":["Assistants"],"summary":"Publish the draft","description":"Makes the draft the live config and records the new version. Refused with 409 no_draft, version_conflict, draft_conflict, or live_changed when live was edited after the draft started.","requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssistantPublish"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Assistant"}},"required":["data"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"operationId":"post-assistants-id-publish","security":[{"apiKey":[]}]}},"/api/assistants/{id}/versions":{"get":{"tags":["Assistants"],"summary":"List the assistant's config history","description":"Every live config the assistant has had, newest first (the latest 100).","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AssistantVersion"}}},"required":["data"]}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"operationId":"get-assistants-id-versions","security":[{"apiKey":[]}]}},"/api/assistants/{id}/versions/{version}":{"get":{"tags":["Assistants"],"summary":"Get one version of the assistant's config","description":"The live config the assistant had at that version, as the history list returns it. 404 for a version not in its history.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AssistantVersion"}},"required":["data"]}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"version","in":"path","required":true,"schema":{"type":"integer","minimum":1}}],"operationId":"get-assistants-id-versions-version","security":[{"apiKey":[]}]}},"/api/assistants/{id}/versions/{version}/restore":{"post":{"tags":["Assistants"],"summary":"Restore a version","description":"Brings an old config back with today's credentials and tool auth: into the draft by default, or straight to live with target live.","requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssistantRestore"}}}},"responses":{"200":{"description":"The draft it was restored into, or the assistant with target live.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"anyOf":[{"$ref":"#/components/schemas/AssistantDraft"},{"$ref":"#/components/schemas/Assistant"}]}},"required":["data"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"version","in":"path","required":true,"schema":{"type":"integer","minimum":1}}],"operationId":"post-assistants-id-versions-version-restore","security":[{"apiKey":[]}]}},"/api/knowledge":{"post":{"tags":["Knowledge"],"summary":"Create a knowledge base","description":"An empty knowledge base. Add sources, approve them and publish before any assistant can answer from it. Needs an org-wide API key: project keys cannot use organization knowledge.","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"Trimmed; 1 to 200 characters."},"description":{"type":"string","maxLength":2000,"default":""}},"required":["name"]}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/KnowledgeBase"}},"required":["data"]}}}},"400":{"description":"`invalid_knowledge_payload`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`knowledge_owner_required`: a project-scoped key, or a member who is not the organization owner","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"`knowledge_operation_failed`: an unexpected failure; retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"post-knowledge","security":[{"apiKey":[]}]},"get":{"tags":["Knowledge"],"summary":"List knowledge bases","description":"Every knowledge base in the organization, newest first. Needs an org-wide API key: project keys cannot use organization knowledge. A project key gets an empty list.","parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/KnowledgeBase"}}},"required":["data"]}}}},"401":{"description":"Missing or invalid API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"`knowledge_operation_failed`: an unexpected failure; retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"get-knowledge","security":[{"apiKey":[]}]}},"/api/knowledge/{id}":{"get":{"tags":["Knowledge"],"summary":"Get a knowledge base","description":"The base with every source (and its latest revision), publications, the assistants answering from it (`attachments`), what open assistant drafts would change (`pending`) and member grants. Poll this after starting a publication until its status is `ready`.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Knowledge base id, `kb_...`."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/KnowledgeBaseDetail"}},"required":["data"]}}}},"401":{"description":"Missing or invalid API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`organization_knowledge_access_required` (a project-scoped key) or `knowledge_forbidden` (no grant, or too low a role)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`knowledge_not_found`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"`knowledge_operation_failed`: an unexpected failure; retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"get-knowledge-id","security":[{"apiKey":[]}]},"patch":{"tags":["Knowledge"],"summary":"Update a knowledge base","description":"Rename it, change its description, turn it off (`enabled: false` stops every attached assistant using it at once, without deleting anything), or choose how it is searched.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Knowledge base id, `kb_...`."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"Trimmed; 1 to 200 characters."},"description":{"type":"string","maxLength":2000},"enabled":{"type":"boolean"},"retrieval_mode":{"type":"string","enum":["vector_store","hybrid"],"description":"How assistants search it. New bases use `hybrid`, which takes effect once the live publication's hybrid index is ready; until then search uses the vector store."}},"required":[]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/KnowledgeBase"}},"required":["data"]}}}},"400":{"description":"`invalid_knowledge_payload`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`organization_knowledge_access_required` (a project-scoped key) or `knowledge_forbidden` (no grant, or too low a role)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`knowledge_not_found`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"`knowledge_operation_failed`: an unexpected failure; retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"patch-knowledge-id","security":[{"apiKey":[]}]}},"/api/knowledge/{id}/sources":{"post":{"tags":["Knowledge"],"summary":"Add or revise a source","description":"Adds text, a web page (`url`) or a file (`filename` and `base64`: txt, md, html, docx or pdf up to 5 MB) and extracts its text as a new revision awaiting approval. Pass an existing source `id` to revise it. Only `public` sources are ever published; `audience` defaults to `private` and cannot change later. URL sources must be public HTTPS pages that robots.txt allows, without query or fragment.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Knowledge base id, `kb_...`."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"id":{"type":"string","description":"An existing source to revise. Omit to add a new one."},"kind":{"type":"string","enum":["text","url","file"]},"title":{"type":"string","minLength":1,"maxLength":200,"description":"Trimmed; 1 to 200 characters."},"audience":{"type":"string","enum":["public","private"],"default":"private","description":"Fixed when the source is created."},"text":{"type":"string","maxLength":200000,"description":"`text` sources: the content."},"url":{"type":"string","maxLength":2048,"description":"`url` sources: the page to fetch."},"filename":{"type":"string","maxLength":200,"description":"`file` sources: the name, whose extension picks the parser."},"base64":{"type":"string","maxLength":7000000,"description":"`file` sources: the file bytes, base64-encoded."},"expires_at":{"type":["string","null"],"format":"date-time","description":"Stop using the source after this time."}},"required":["kind","title"]}}}},"responses":{"201":{"description":"Created; `data.id` is the source id, `ksrc_...`","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}},"required":["data"]}}}},"400":{"description":"`invalid_knowledge_payload` (including `text` over 200,000 characters), `source_text_required_or_too_large` (empty text), `invalid_source_id`, or a URL or file code: `invalid_source_url`, `public_https_url_required`, `source_path_not_allowed`, `source_disallowed_by_robots`, `source_address_not_public`, `source_redirect_not_allowed`, `source_too_large`, `source_needs_review_no_text`, `website_content_type_not_supported`, `unsupported_document_type`, `pdf_page_limit_reached`, `document_expansion_too_large`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`organization_knowledge_access_required` (a project-scoped key) or `knowledge_forbidden` (no grant, or too low a role)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`knowledge_not_found`, or `source_not_found` when revising","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`source_identity_is_immutable` (a revision changed the kind, URL or audience: add a new source instead) or `source_limit_reached` (100 active sources)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"`source_fetch_failed` (the page did not answer 200) or `knowledge_operation_failed`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`knowledge_storage_not_configured`: this deployment cannot store sources","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"post-knowledge-id-sources","security":[{"apiKey":[]}]}},"/api/knowledge/{id}/discover":{"post":{"tags":["Knowledge"],"summary":"Find a site's pages from its sitemap","description":"Reads the site's sitemap and returns up to 100 page URLs on the same origin that robots.txt allows, without importing anything. Add the ones you want as `url` sources.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Knowledge base id, `kb_...`."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"url":{"type":"string","maxLength":2048,"description":"Any page on the site; its origin's /sitemap.xml is read."}},"required":["url"]}}}},"responses":{"200":{"description":"Page URLs","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","maxItems":100,"items":{"type":"string","format":"uri"}}},"required":["data"]}}}},"400":{"description":"`invalid_knowledge_payload` or a URL code such as `public_https_url_required`, `source_disallowed_by_robots` or `source_address_not_public`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`organization_knowledge_access_required` (a project-scoped key) or `knowledge_forbidden` (no grant, or too low a role)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`knowledge_not_found`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"`knowledge_operation_failed`: an unexpected failure; retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"post-knowledge-id-discover","security":[{"apiKey":[]}]}},"/api/knowledge/{id}/grants/{userId}":{"post":{"tags":["Knowledge"],"summary":"Grant a member access","description":"Gives one organization member a role on this knowledge base: `viewer` reads, `editor` adds sources and attaches assistants, `publisher` also approves and publishes. Calling again changes the role. Requires an organization owner; an API key counts as one.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Knowledge base id, `kb_...`."},{"name":"userId","in":"path","required":true,"schema":{"type":"string"},"description":"The member's user id."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"role":{"type":"string","enum":["viewer","editor","publisher"]}},"required":["role"]}}}},"responses":{"200":{"description":"Granted; `data.id` is the user id","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}},"required":["data"]}}}},"400":{"description":"`invalid_knowledge_payload`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`knowledge_owner_required`, or `organization_knowledge_access_required` (a project-scoped key) or `knowledge_forbidden` (no grant, or too low a role)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`knowledge_not_found` or `member_not_found`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"`knowledge_operation_failed`: an unexpected failure; retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"post-knowledge-id-grants-userid","security":[{"apiKey":[]}]},"delete":{"tags":["Knowledge"],"summary":"Remove a member's access","description":"The member loses their role on this knowledge base. Requires an organization owner; an API key counts as one.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Knowledge base id, `kb_...`."},{"name":"userId","in":"path","required":true,"schema":{"type":"string"},"description":"The member's user id."}],"responses":{"200":{"description":"Removed","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}},"required":["data"]}}}},"401":{"description":"Missing or invalid API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`knowledge_owner_required`, or `organization_knowledge_access_required` (a project-scoped key) or `knowledge_forbidden` (no grant, or too low a role)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`knowledge_not_found` or `member_not_found`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"`knowledge_operation_failed`: an unexpected failure; retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"delete-knowledge-id-grants-userid","security":[{"apiKey":[]}]}},"/api/knowledge/{id}/publications":{"post":{"tags":["Knowledge"],"summary":"Build a publication","description":"Snapshots every approved public source into a new immutable publication and indexes it in the background (202). Poll the knowledge base until the publication is `ready`, then publish it. Returns the pending build instead when one is already queued. No request body.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Knowledge base id, `kb_...`."}],"responses":{"202":{"description":"Queued; `data.id` is the publication id, `kpub_...`","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}},"required":["data"]}}}},"400":{"description":"`no_public_sources`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`organization_knowledge_access_required` (a project-scoped key) or `knowledge_forbidden` (no grant, or too low a role)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`knowledge_not_found`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`review_all_public_sources_first` or `publication_limit_reached`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"`knowledge_operation_failed`: an unexpected failure; retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`knowledge_not_configured`, `knowledge_storage_not_configured` or `knowledge_provider_not_configured`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"post-knowledge-id-publications","security":[{"apiKey":[]}]}},"/api/knowledge/{id}/publications/{publicationId}/publish":{"post":{"tags":["Knowledge"],"summary":"Publish a ready publication","description":"Makes a `ready` publication the live one, atomically; publishing an older ready publication rolls back to it. 409 `publication_not_ready`, or `sources_changed_rebuild_publication` when sources changed since it was built.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Knowledge base id, `kb_...`."},{"name":"publicationId","in":"path","required":true,"schema":{"type":"string"},"description":"Publication id, `kpub_...`."}],"responses":{"200":{"description":"Published","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}},"required":["data"]}}}},"401":{"description":"Missing or invalid API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`organization_knowledge_access_required` (a project-scoped key) or `knowledge_forbidden` (no grant, or too low a role)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`knowledge_not_found`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`publication_not_ready` (also for an unknown publication id) or `sources_changed_rebuild_publication`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"`knowledge_operation_failed`: an unexpected failure; retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"post-knowledge-id-publications-publicationid-publish","security":[{"apiKey":[]}]}},"/api/knowledge/{id}/publications/{publicationId}/retry":{"post":{"tags":["Knowledge"],"summary":"Retry a failed publication","description":"Queues a `failed` publication for indexing again, from the files it already stored. A publication already queued, indexing or ready returns its id unchanged.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Knowledge base id, `kb_...`."},{"name":"publicationId","in":"path","required":true,"schema":{"type":"string"},"description":"Publication id, `kpub_...`."}],"responses":{"200":{"description":"Queued again","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}},"required":["data"]}}}},"401":{"description":"Missing or invalid API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`organization_knowledge_access_required` (a project-scoped key) or `knowledge_forbidden` (no grant, or too low a role)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`knowledge_not_found`, or `failed_publication_not_found` (no failed publication with that id)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"`knowledge_operation_failed`: an unexpected failure; retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"post-knowledge-id-publications-publicationid-retry","security":[{"apiKey":[]}]}},"/api/knowledge/{id}/publications/{publicationId}/test":{"post":{"tags":["Knowledge"],"summary":"Try a publication in test chats","description":"Test chats (on a test publishable key, or the console's test chat) search this `ready` publication instead of the live one, so new content can be tried before it goes live. Live chats are unchanged. Make it live with POST /knowledge/{id}/test-publication/promote. Requires publisher access.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Knowledge base id, `kb_...`."},{"name":"publicationId","in":"path","required":true,"schema":{"type":"string"},"description":"Publication id, `kpub_...`."}],"responses":{"200":{"description":"Test chats now search it","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}},"required":["data"]}}}},"401":{"description":"Missing or invalid API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`organization_knowledge_access_required` (a project-scoped key) or `knowledge_forbidden` (no grant, or too low a role)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`knowledge_not_found`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`publication_not_ready` (also for an unknown publication id) or `sources_changed_rebuild_publication`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"`knowledge_operation_failed`: an unexpected failure; retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"post-knowledge-id-publications-publicationid-test","security":[{"apiKey":[]}]}},"/api/knowledge/{id}/revisions/{revisionId}":{"get":{"tags":["Knowledge"],"summary":"Preview a revision's extracted text","description":"The text extracted from a source revision, to check before approving it.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Knowledge base id, `kb_...`."},{"name":"revisionId","in":"path","required":true,"schema":{"type":"string"},"description":"Revision id, `krev_...`, from the source's `revision.id`."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/KnowledgeRevisionPreview"}},"required":["data"]}}}},"401":{"description":"Missing or invalid API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`organization_knowledge_access_required` (a project-scoped key) or `knowledge_forbidden` (no grant, or too low a role)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`knowledge_not_found` or `revision_not_found`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"`knowledge_operation_failed`: an unexpected failure; retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`knowledge_storage_not_configured`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"get-knowledge-id-revisions-revisionid","security":[{"apiKey":[]}]}},"/api/knowledge/{id}/revisions/{revisionId}/approve":{"post":{"tags":["Knowledge"],"summary":"Approve a revision","description":"Marks the extracted text as fit to publish. Every public source's latest revision must be approved before a publication can be built. Requires publisher access. No request body.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Knowledge base id, `kb_...`."},{"name":"revisionId","in":"path","required":true,"schema":{"type":"string"},"description":"Revision id, `krev_...`."}],"responses":{"200":{"description":"Approved","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}},"required":["data"]}}}},"401":{"description":"Missing or invalid API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`organization_knowledge_access_required` (a project-scoped key) or `knowledge_forbidden` (no grant, or too low a role)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`knowledge_not_found` or `revision_not_found`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"`knowledge_operation_failed`: an unexpected failure; retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"post-knowledge-id-revisions-revisionid-approve","security":[{"apiKey":[]}]}},"/api/knowledge/{id}/search":{"post":{"tags":["Knowledge"],"summary":"Search a knowledge base","description":"Runs a question against the live publication (or, with `publication: test`, the one test chats search) and returns the passages an assistant would get, for checking coverage. It only reads, so a read-only key may call it although it is a POST.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Knowledge base id, `kb_...`."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"query":{"type":"string","minLength":1,"maxLength":2000},"publication":{"type":"string","enum":["live","test"],"default":"live","description":"`test` searches the publication test chats use."}},"required":["query"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/KnowledgeSearchResult"}},"required":["data"]}}}},"400":{"description":"`invalid_knowledge_payload`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`organization_knowledge_access_required` (a project-scoped key) or `knowledge_forbidden` (no grant, or too low a role)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`knowledge_not_found`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"`knowledge_operation_failed`: an unexpected failure; retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"post-knowledge-id-search","security":[{"apiKey":[]}]}},"/api/knowledge/{id}/sources/{sourceId}":{"delete":{"tags":["Knowledge"],"summary":"Revoke a source","description":"Answers stop using the source at once, even from the live publication; the next publication leaves it out.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Knowledge base id, `kb_...`."},{"name":"sourceId","in":"path","required":true,"schema":{"type":"string"},"description":"Source id, `ksrc_...`."}],"responses":{"200":{"description":"Revoked; `data` is the source row with `revoked_at` set","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}},"required":["data"]}}}},"401":{"description":"Missing or invalid API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`organization_knowledge_access_required` (a project-scoped key) or `knowledge_forbidden` (no grant, or too low a role)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`knowledge_not_found` or `source_not_found`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"`knowledge_operation_failed`: an unexpected failure; retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"delete-knowledge-id-sources-sourceid","security":[{"apiKey":[]}]}},"/api/knowledge/{id}/test-publication":{"delete":{"tags":["Knowledge"],"summary":"Stop trying a publication","description":"Test chats go back to searching the live publication. Requires publisher access.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Knowledge base id, `kb_...`."}],"responses":{"200":{"description":"Cleared; `data.id` is the knowledge base id","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}},"required":["data"]}}}},"401":{"description":"Missing or invalid API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`organization_knowledge_access_required` (a project-scoped key) or `knowledge_forbidden` (no grant, or too low a role)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`knowledge_not_found`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"`knowledge_operation_failed`: an unexpected failure; retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"delete-knowledge-id-test-publication","security":[{"apiKey":[]}]}},"/api/knowledge/{id}/test-publication/promote":{"post":{"tags":["Knowledge"],"summary":"Make the tested publication live","description":"Makes the publication test chats are searching the live one, then clears the test pick. Send `expected_publication_id` to be sure it is the one you tested. Refused when another publication went live after it was picked, since it may lack what that one added: try the publication again, or build a new one. Requires publisher access.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Knowledge base id, `kb_...`."}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"expected_publication_id":{"type":"string","minLength":1,"description":"The publication you tested, `kpub_...`; 409 `test_publication_changed` when the test pick is another one."}},"required":[]}}}},"responses":{"200":{"description":"Live; `data.id` is the publication id","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]}},"required":["data"]}}}},"400":{"description":"`invalid_knowledge_payload`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`organization_knowledge_access_required` (a project-scoped key) or `knowledge_forbidden` (no grant, or too low a role)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`knowledge_not_found`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`no_test_publication`, `test_publication_changed`, `test_publication_stale` (another publication went live after this one was picked), `publication_not_ready` or `sources_changed_rebuild_publication`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"`knowledge_operation_failed`: an unexpected failure; retry","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"post-knowledge-id-test-publication-promote","security":[{"apiKey":[]}]}},"/api/phone-numbers":{"get":{"tags":["Phone numbers"],"summary":"List phone numbers","description":"Numbers in scope, with the assistant each one is attached to and how it routes calls.","parameters":[{"name":"project_id","in":"query","required":false,"schema":{"type":"string"},"description":"Only this project's numbers."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PhoneNumber"}}},"required":["data"]}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"get-phone-numbers","security":[{"apiKey":[]}]},"post":{"tags":["Phone numbers"],"summary":"Import a phone number you own","description":"Registers a number you already own with the carrier, so an assistant can be attached to it. It does not buy anything. For a Twilio number, include its number SID in `provider_metadata.twilioNumberSid` so attaching can configure Twilio's routing.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PhoneNumberImport"}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PhoneNumber"}},"required":["data"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"post-phone-numbers","security":[{"apiKey":[]}]}},"/api/phone-numbers/{id}":{"get":{"tags":["Phone numbers"],"summary":"Get a phone number","description":"One number with its assistant, routing mode and status.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PhoneNumber"}},"required":["data"]}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Phone number id, `pn_...`."}],"operationId":"get-phone-numbers-id","security":[{"apiKey":[]}]},"patch":{"tags":["Phone numbers"],"summary":"Set a phone number's test mode","description":"Every call to or from a test number is a test call: it runs the assistant's saved draft, reaches test webhooks only and stays out of analytics. A live outbound call cannot use a test number as its caller ID. A database change only; carrier routing (routing_mode) is unchanged. Admins only.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PhoneNumberUpdate"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PhoneNumber"}},"required":["data"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Phone number id, `pn_...`."}],"operationId":"patch-phone-numbers-id","security":[{"apiKey":[]}]},"delete":{"tags":["Phone numbers"],"summary":"Release a phone number","description":"Deletes the number from the registry and, when it is carrier-managed and this deployment can reach the carrier, releases it back to the carrier (you lose the number and stop paying for it). Check `data.released`: false means the carrier was not asked, so the number may still be yours and billed there. Detach its assistant first: an attached number is 409 `{\"error\":\"number_attached\"}`.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PhoneNumberRelease"}},"required":["data"]}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`number_attached`: detach the assistant first","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Phone number id, `pn_...`."}],"operationId":"delete-phone-numbers-id","security":[{"apiKey":[]}]}},"/api/phone-numbers/{id}/attach":{"post":{"tags":["Phone numbers"],"summary":"Attach an assistant to a phone number","description":"Puts an assistant on the number and configures the carrier so inbound calls reach it. The assistant and number must be in the same project. `routing_mode` picks the path: `trunk` (SIP straight to the voice runtime, cold transfers) or `pv_webhook` (Twilio Programmable Voice bridge, warm transfers).","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PhoneNumberAttach"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PhoneNumber"}},"required":["data"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`telephony_not_configured`: this deployment has no carrier account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Phone number id, `pn_...`."}],"operationId":"post-phone-numbers-id-attach","security":[{"apiKey":[]}]},"delete":{"tags":["Phone numbers"],"summary":"Detach the assistant from a phone number","description":"The number stops reaching any assistant. It stays registered and owned; release it to give it up.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PhoneNumber"}},"required":["data"]}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Phone number id, `pn_...`."}],"operationId":"delete-phone-numbers-id-attach","security":[{"apiKey":[]}]}},"/api/phone-numbers/{id}/mode":{"post":{"tags":["Phone numbers"],"summary":"Change a phone number's routing mode","description":"Moves an attached number between `trunk` and `pv_webhook` routing and reconfigures the carrier, keeping its assistant.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PhoneNumberMode"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PhoneNumber"}},"required":["data"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`telephony_not_configured`: this deployment has no carrier account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Phone number id, `pn_...`."}],"operationId":"post-phone-numbers-id-mode","security":[{"apiKey":[]}]}},"/api/phone-numbers/available":{"get":{"tags":["Phone numbers"],"summary":"Search available US phone numbers","description":"Numbers the carrier can sell right now, in the United States only. Buy one with the purchase route.","parameters":[{"name":"area_code","in":"query","required":false,"schema":{"type":"string","minLength":1},"description":"Three-digit US area code, for example `415`."},{"name":"contains","in":"query","required":false,"schema":{"type":"string","minLength":1},"description":"Digits the number must contain."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100},"description":"How many numbers to return."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AvailablePhoneNumber"}}},"required":["data"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`telephony_not_configured`: this deployment has no carrier account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"get-phone-numbers-available","security":[{"apiKey":[]}]}},"/api/phone-numbers/purchase":{"post":{"tags":["Phone numbers"],"summary":"Buy a phone number","description":"Buys a number from the carrier (it is billed from then on) and registers it in the project, unattached. Attach an assistant next to answer calls on it.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PhoneNumberPurchase"}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PhoneNumber"}},"required":["data"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`telephony_not_configured`: this deployment has no carrier account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"post-phone-numbers-purchase","security":[{"apiKey":[]}]}},"/api/calls":{"post":{"tags":["Calls"],"summary":"Place an outbound phone call","description":"Dispatches the assistant into a fresh room and dials `to` over the outbound SIP trunk. With the default LiveKit runtime the request waits while the phone rings and returns once the callee answers, so allow for a long response; a call that is not answered or fails to connect answers 502 `provider_error` and is marked failed. With `voice_provider: vapi` it returns as soon as Vapi accepts the call. In both cases the 201 body says `status: \"ringing\"` (the body's literal value, not the live state): follow the call through the `call.connected` and `call.ended` webhooks, or read it back by id. The body is not wrapped in `data`. With `mode: test` the call runs the assistant's saved draft (its live config when there is none) and is a test call. 503 `livekit_not_configured` (or `vapi_not_configured`) when this deployment cannot place calls with that voice provider.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OutboundCallCreate"}}}},"responses":{"201":{"description":"Ringing. LiveKit calls return `room`; Vapi calls return `provider_call_id` instead.","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/OutboundCallCreated"},{"$ref":"#/components/schemas/OutboundCallCreatedVapi"}]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: no such assistant for this key, or `from` is not an active number in the assistant's project","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`test_number_requires_test_mode`: `from` is a test number and the call is live. `test_mode_unsupported_provider`: a test call asked for Vapi; nothing was dialled.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"`provider_error`: the call was not answered or could not be placed; it is marked failed and its room is closed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`livekit_not_configured` or `vapi_not_configured`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"post-calls","security":[{"apiKey":[]}]},"get":{"tags":["Calls"],"summary":"List calls","description":"Calls newest first, one cursor page at a time. Rows carry the transcript; usage lines, recordings and analysis come with the single call. A project key sees its own project; an org-wide key sees every project unless `project_id` narrows it.","parameters":[{"name":"project_id","in":"query","required":false,"schema":{"type":"string"},"description":"Only this project's calls. With a project key, another project's id returns an empty list."},{"name":"assistant_id","in":"query","required":false,"schema":{"type":"string"},"description":"Only this assistant's calls."},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["ringing","in_progress","completed","failed","no_answer","busy","cancelled"]},"description":"Only calls in this state."},{"name":"call_type","in":"query","required":false,"schema":{"type":"string","enum":["inbound","outbound","web"]},"description":"Only inbound, outbound or browser calls."},{"name":"q","in":"query","required":false,"schema":{"type":"string","description":"Case-insensitive literal substring across call id, phone numbers, and assistant id."}},{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Created at or after this time (ISO 8601 with an offset)."},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Created at or before this time (ISO 8601 with an offset)."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200,"default":50},"description":"Page size."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","description":"Opaque next_cursor from the previous page. Legacy ISO timestamp cursors are also accepted."}},{"name":"mode","in":"query","required":false,"schema":{"type":"string","enum":["live","test","all"],"default":"live","description":"Live calls unless asked: test calls (console test calls, test numbers, mode test) stay out by default."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Call"}},"next_cursor":{"type":["string","null"]},"has_more":{"type":"boolean"}},"required":["data","next_cursor","has_more"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"get-calls","security":[{"apiKey":[]}]}},"/api/calls/web":{"post":{"tags":["Calls"],"summary":"Start a browser call","description":"Mints a browser join token: connect to `url` with `token` using a LiveKit client and publish the microphone. The token lasts 15 minutes. For a live call the row exists only once the browser joins and the call starts, so `GET /calls/{id}` can 404 until then. With `mode: test` the call runs the assistant's saved draft (its live config when there is none), and its row (mode `test`) is created before the agent is dispatched. Call this from your server; hand only `url` and `token` to the browser. With `voice_provider: vapi` the response carries a `web_call_url` instead, when Vapi provides one. 503 when the selected voice provider is not configured.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebCallCreate"}}}},"responses":{"201":{"description":"Created. Not wrapped in `data`.","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/WebCallCreated"},{"$ref":"#/components/schemas/WebCallCreatedVapi"}]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: no such assistant for this key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`test_mode_unsupported_provider`: a test call asked for Vapi; nothing was started.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Vapi refused the call (Vapi calls only)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`livekit_not_configured` or `vapi_not_configured`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"post-calls-web","security":[{"apiKey":[]}]}},"/api/calls/{id}":{"get":{"tags":["Calls"],"summary":"Get a call","description":"One call with its transcript, usage lines, recordings, latency metrics, post-call analysis and the lifecycle events received for it. API keys see usage quantities but not prices.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CallDetail"}},"required":["data"]}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Call id, `call_...`."}],"operationId":"get-calls-id","security":[{"apiKey":[]}]}},"/api/calls/{id}/hangup":{"post":{"tags":["Calls"],"summary":"Hang up a live call","description":"Asks the voice runtime to end a ringing or in-progress call. The call ends shortly after, with a `call.ended` webhook. 409 `call_not_active` once it has ended, `provider_not_supported` for Vapi calls.","responses":{"200":{"description":"Hangup requested","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallHangupResponse"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`call_not_active` or `provider_not_supported`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`livekit_not_configured`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Call id, `call_...`."}],"operationId":"post-calls-id-hangup","security":[{"apiKey":[]}]}},"/api/calls/{id}/listen":{"post":{"tags":["Calls"],"summary":"Get a listen-only token for a live call","description":"A LiveKit token that can hear a live call but not speak in it, for supervising from your own interface. Connect to `url` with `token`. 409 when the call is not live or is a Vapi call.","responses":{"200":{"description":"Listen token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallListenResponse"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`call_not_active` or `provider_not_supported`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`livekit_not_configured`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Call id, `call_...`."}],"operationId":"post-calls-id-listen","security":[{"apiKey":[]}]}},"/api/calls/{id}/recordings":{"get":{"tags":["Calls"],"summary":"List a call's recordings","description":"Each recorded leg of the call with its status and length. Ask the access route for a playback URL once a recording is `ready`.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CallRecording"}}},"required":["data"]}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Call id, `call_...`."}],"operationId":"get-calls-id-recordings","security":[{"apiKey":[]}]}},"/api/calls/{id}/recordings/{recordingId}/access":{"get":{"tags":["Calls"],"summary":"Get a short-lived recording link","description":"Requires access to the parent call. The response is private/no-store. URLs are generated on demand, never stored, and expire after 15 minutes: fetch a new one each time you play.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Call id, `call_...`."},{"name":"recordingId","in":"path","required":true,"schema":{"type":"string"},"description":"Recording id, `rec_...`, from the call's recordings."},{"name":"download","in":"query","schema":{"type":"boolean","default":false},"description":"`true` for a URL that downloads the file; `false` for one that plays in the browser."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/RecordingAccess"}},"required":["data"]}}}},"400":{"description":"`invalid_download`: `download` must be `true` or `false`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`recording_not_ready`, or `recording_storage_mismatch` (stored by another deployment)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"description":"`recording_deleted`: the recording was deleted or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`recording_storage_not_configured` or `recording_access_unavailable`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"get-calls-id-recordings-recordingid-access","security":[{"apiKey":[]}]}},"/api/preview-links":{"get":{"tags":["Publishable keys"],"summary":"Get an assistant's preview link","description":"The assistant's live (unrevoked) preview link, expired or not, or null when it has none.","parameters":[{"name":"assistant_id","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"oneOf":[{"$ref":"#/components/schemas/PreviewLink"},{"type":"null"}]}}}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"get-preview-links","security":[{"apiKey":[]}]},"post":{"tags":["Publishable keys"],"summary":"Make a preview link (pk_preview_…)","description":"A page where anyone with the link, or only signed-in members of the org, can try the assistant's web chat; it loads from any site and while web chat is off, and its chats are tests that send nothing outside. One live link per assistant: making one turns the previous link off, with its open chats.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"assistant_id":{"type":"string"},"access":{"type":"string","enum":["public","team"]},"expires_at":{"type":["string","null"],"format":"date-time","description":"In the future; null works until it is turned off."}},"required":["assistant_id","access","expires_at"]}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PreviewLink"}},"required":["data"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"post-preview-links","security":[{"apiKey":[]}]}},"/api/preview-links/{id}":{"patch":{"tags":["Publishable keys"],"summary":"Change who may open a preview link, or when it expires","description":"The URL and key stay the same.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"access":{"type":"string","enum":["public","team"]},"expires_at":{"type":["string","null"],"format":"date-time"}}}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PreviewLink"}},"required":["data"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"operationId":"patch-preview-links-id","security":[{"apiKey":[]}]},"delete":{"tags":["Publishable keys"],"summary":"Turn a preview link off","description":"The page stops loading, and the chats open on it end.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PreviewLink"}},"required":["data"]}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"operationId":"delete-preview-links-id","security":[{"apiKey":[]}]}},"/api/preview-links/{id}/page":{"get":{"tags":["Publishable keys"],"summary":"Read a preview link for its page","description":"Needs no session. A team-only link returns only `access` and `org_id` unless the caller is a member of that org, who also gets the key.","security":[{"apiKey":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreviewPage"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"410":{"description":"The link has expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"operationId":"get-preview-links-id-page"}},"/api/publishable-keys":{"get":{"tags":["Publishable keys"],"summary":"List publishable keys","description":"Filter by project_id (covers project-scoped keys) or assistant_id (legacy pinned keys); no filter returns everything in scope. Site keys only unless kind says otherwise. Revoked keys are included, with `revoked_at` set.","parameters":[{"name":"project_id","in":"query","required":false,"schema":{"type":"string"},"description":"Only keys for this project."},{"name":"assistant_id","in":"query","required":false,"schema":{"type":"string"},"description":"Only keys pinned to this assistant."},{"name":"kind","in":"query","required":false,"schema":{"type":"string","enum":["site","test","all"]},"description":"site (the default) lists the keys for live websites, test the test keys, all both."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WidgetKey"}}},"required":["data"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"get-publishable-keys","security":[{"apiKey":[]}]},"post":{"tags":["Publishable keys"],"summary":"Create a publishable key","description":"Returns the key in `data.key`: `pk_live_...` for a site key, `pk_test_...` for a test key. `project_id` (or, with a project API key, nothing at all) mints a project-scoped key, usable with any assistant in the project: the embed names the assistant with `data-mlf-assistant` and public chat requests send `assistant`. `assistant_id` mints a key pinned to that assistant. An org-wide API key must send one of the two (400 otherwise). Publishable keys are safe in page source; which sites may use a site key is the assistant's `channels.web_chat.allowed_origins`. `kind: test` mints a test key for a staging site or localhost: it loads only on its own `allowed_origins`, and its chats run the assistant's draft, search the test knowledge publication and reach test webhooks only.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"project_id":{"type":"string","minLength":1,"description":"Mint a project-scoped key."},"assistant_id":{"type":"string","minLength":1,"description":"Mint a key pinned to this assistant."},"kind":{"type":"string","enum":["site","test"],"default":"site","description":"`site` for your live site, `test` for a staging site or localhost."},"allowed_origins":{"type":"array","items":{"type":"string"},"maxItems":20,"description":"Test keys only, and required for them: the HTTP(S) origins the key loads on."}}}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WidgetKey"}},"required":["data"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`assistant_not_found` or `project_not_found`: not in this key's scope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"post-publishable-keys","security":[{"apiKey":[]}]}},"/api/publishable-keys/{id}":{"delete":{"tags":["Publishable keys"],"summary":"Revoke a publishable key","description":"Revocation, not deletion: the row stays and gains a `revoked_at`, so an embed that still carries the key gets a clean refusal rather than a missing record.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WidgetKey"}},"required":["data"]}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Key record id, `wkey_...` (not the `pk_live_` key itself)."}],"operationId":"delete-publishable-keys-id","security":[{"apiKey":[]}]}},"/public/v1/config":{"get":{"tags":["Public chat API"],"summary":"Get the widget configuration","description":"What a chat interface needs before the first message: display name, greeting, quick replies, appearance, proactive rules, text overrides and (when on) attachment limits. Safe to call from a browser and cached for 60 seconds. When the assistant lists `allowed_origins`, a request whose `Origin` is not on the list is refused with 403.","security":[],"parameters":[{"name":"key","in":"query","required":true,"schema":{"type":"string"},"description":"Publishable key, `pk_live_...`."},{"name":"assistant","in":"query","required":false,"schema":{"type":"string"},"description":"Required with a project-scoped publishable key; ignored-if-matching for assistant-pinned keys."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PublicWidgetConfig"}},"required":["data"]}}}},"403":{"description":"`origin_not_allowed`: the request's `Origin` is not in the assistant's `allowed_origins`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: unknown or revoked key, no such assistant for it, or web chat is off for the assistant","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"get-public-v1-config"}},"/public/v1/conversations":{"post":{"tags":["Public chat API"],"summary":"Start a conversation","description":"Starts a conversation with the assistant behind a publishable key and returns its id and token. Send the token as `Authorization: Bearer ct_...` on every later call for this conversation; it is returned only here. The widget starts a conversation when the visitor sends a first message, not when the panel opens. A browser page calling from its own site is checked by its `Origin` header against the assistant's `allowed_origins` (an empty list allows every site).","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"widget_key":{"type":"string","minLength":1,"description":"Publishable key, `pk_live_...`."},"assistant":{"type":"string","minLength":1,"description":"Assistant id; required with a project-scoped publishable key."},"parent_origin":{"type":"string","description":"The site the chat runs on, for requests that carry no `Origin` header of their own (your server, for example) and for the widget's own frame. A browser calling from another site is checked by its real `Origin` header instead, and this field is ignored, so a page cannot borrow an allowed site's name."},"visitor_id":{"type":"string","description":"Your stable id for the visitor, so their conversations can be grouped. Stored only when analytics consent is granted or not required."},"visit_id":{"type":"string","maxLength":256,"description":"The visit this conversation started in. Kept in metadata.visit_id only when analytics consent is granted or not required, and only when it is 1 to 128 letters, digits, `_` or `-`; any other string up to 256 characters is ignored, and a longer one answers 400."},"timezone":{"type":"string","maxLength":64,"description":"The visitor's IANA time zone (for example `America/New_York`), so dates and callback times are theirs. A recognised zone is stored as metadata.visitor_timezone; an unknown one is ignored."},"attribution":{"$ref":"#/components/schemas/ChatAttribution","description":"Where the visitor came from. Sanitized, never a reason to refuse the chat. Only chat_page is kept without analytics consent."},"metadata":{"type":"object","additionalProperties":true,"description":"Free-form context stored on the conversation and sent in its webhooks (for example a region or campaign). The `preview` and `attribution` keys are reserved and dropped; send attribution in `attribution`."},"client_message_id":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[a-zA-Z0-9_-]+$","description":"The id of the first message you are about to send, so this request is linked to it in chat analytics."},"page_view_id":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[a-zA-Z0-9_-]+$","description":"The page view the chat started on, as reported to POST /public/v1/telemetry."},"consent":{"type":"string","enum":["granted","denied","not_required"],"description":"The visitor's analytics consent, which decides whether analytics ids are stored."},"preamble":{"type":"array","maxItems":5,"items":{"type":"object","properties":{"content":{"type":"string","minLength":1,"maxLength":2000}},"required":["content"]},"description":"Assistant lines your interface already showed (a greeting or proactive teaser), stored first so the transcript reads as the visitor saw it."}},"required":["widget_key"]}}}},"responses":{"201":{"description":"Conversation created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"conversation_id":{"type":"string"},"token":{"type":"string","description":"Conversation token, `ct_...`: send it as `Authorization: Bearer ct_...` on this conversation's routes. Returned once; valid for at most 30 days (less when a test key behind it is revoked, or a preview link turned off or expired)."},"status":{"type":"string","enum":["open"]},"messages":{"type":"array","items":{"$ref":"#/components/schemas/PublicMessage"},"description":"Any `preamble` lines you sent, stored as the assistant's first messages."},"config":{"$ref":"#/components/schemas/PublicWidgetConfig"}},"required":["conversation_id","token","status","messages","config"]}},"required":["data"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`origin_not_allowed`: the calling site is not in the assistant's `allowed_origins` (see `parent_origin` for which site that is)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: unknown or revoked key, no such assistant for it, or web chat is off for the assistant","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`key_quota_exceeded`: the publishable key has used a budget shared by every visitor of your site: 300 new conversations an hour, 30 an hour from one IP address, or 5,000 assistant replies a day. `rate_limited` is the ordinary per-address limit. Both carry `Retry-After`, in seconds.","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"post-public-v1-conversations"}},"/public/v1/conversations/{id}/messages":{"post":{"tags":["Public chat API"],"summary":"Send a message and stream the reply","description":"Sends the visitor's message and streams the reply as server-sent events (see ChatSseEvents): usually `message.accepted`, then `message.delta` chunks and any `tool.status`, always ending with exactly one `message.completed` or `error`. A 200 status does not mean the turn succeeded: read to the terminal event. While a person has taken over, the stream is a single `message.completed` with `message: null`, and their reply arrives through the updates route. One turn runs at a time per conversation. Send a `client_message_id` so a retry after a dropped connection replays the stored result as one terminal event instead of answering twice. A retry of a turn whose reply was cut off (none stored, or a truncated one) is answered again when no person has taken over and nothing but read-only work (such as knowledge searches) ran; otherwise it ends with `error` `turn_interrupted`.","x-sse-events":{"$ref":"#/components/schemas/ChatSseEvent"},"security":[{"conversationToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"client_message_id":{"type":"string","minLength":1,"maxLength":128,"pattern":"^[a-zA-Z0-9_-]+$","description":"Your id for this message, unique within the conversation. Resending the same id with the same content replays the stored turn; with different content or files it is 409 `message_id_conflict`."},"content":{"type":"string","maxLength":4000,"default":"","description":"May be empty when `attachment_ids` is given. A message needs non-empty content, attachment_ids, or both (otherwise 422 `invalid_content`)."},"consent":{"type":"string","enum":["granted","denied","not_required"],"description":"The visitor's analytics consent as your interface knows it now."},"attachment_ids":{"type":"array","maxItems":5,"items":{"type":"string","minLength":1,"maxLength":64},"description":"Ids returned by POST /public/v1/conversations/{id}/attachments for this conversation, at most 5. Each file can be sent once."},"page":{"type":"object","properties":{"url":{"type":"string","maxLength":2048},"title":{"type":"string","maxLength":200}},"required":["url","title"],"description":"The host page the visitor sent this from. A new message stores its path (no origin, query or hash) and title as `rich.metadata.page`, and a web chat model reads them beside that message. A retry keeps the page stored first. A malformed or non-http(s) page is ignored, never refused."},"page_view_id":{"type":"string","maxLength":128,"pattern":"^[a-zA-Z0-9_-]+$","description":"The page view the visitor sent this from, as reported to POST /public/v1/telemetry. Ignored when malformed."},"test":{"type":"boolean","enum":[true],"description":"Marks the conversation as a test from now on, for a chat that began before the page was marked as one. Cannot be undone."}}}}}},"responses":{"200":{"description":"SSE assistant turn","content":{"text/event-stream":{"schema":{"$ref":"#/components/schemas/ChatSseEvents"}}}},"401":{"description":"Missing, wrong or expired conversation token, or a conversation id the token does not belong to (`unauthorized`; an unknown id is a 401, never a 404)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`attachments_disabled`: the assistant's web chat `attachments.enabled` is false","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`turn_in_progress` (another turn is still running), `conversation_ended`, `message_id_conflict` (a replayed `client_message_id` with different content or attachments), `attachment_already_sent` or `llm_not_configured`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"`invalid_content` (no text and no files, or text over 4,000 characters), `attachment_not_found` or `too_many_attachments`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`key_quota_exceeded`: the publishable key has used a budget shared by every visitor of your site: 300 new conversations an hour, 30 an hour from one IP address, or 5,000 assistant replies a day. `rate_limited` is the ordinary per-address limit. Both carry `Retry-After`, in seconds.","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`chat_temporarily_unavailable`: retry after `Retry-After` seconds (1)","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Conversation id, `conv_...`. An id the token does not belong to answers 401."}],"operationId":"post-public-v1-conversations-id-messages"}},"/public/v1/conversations/{id}/updates":{"get":{"tags":["Public chat API"],"summary":"Poll for new messages","description":"Messages after a sequence number, plus the conversation's state. Poll this while a person has taken over (their replies arrive here, not on the send stream) or after a dropped stream. Keep the highest `seq` you have seen and pass it as `after_seq`. Polling has its own rate limit: 60 a minute per conversation and IP address.","security":[{"conversationToken":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Conversation id, `conv_...`. An id the token does not belong to answers 401."},{"name":"after_seq","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0},"description":"Return messages with a `seq` greater than this. A missing or invalid value reads as 0 (everything)."}],"responses":{"200":{"description":"Conversation state and visible messages after the cursor","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"conversation":{"type":"object","properties":{"status":{"type":"string","enum":["open","ended"]},"resumable":{"type":"boolean","description":"Ended only because the visitor went quiet: the next message reopens it."},"handoff_requested_at":{"type":["string","null"],"format":"date-time"},"taken_over":{"type":"boolean"},"lead_id":{"type":["string","null"],"description":"The lead this conversation created, once the visitor's details are in."},"lead":{"anyOf":[{"$ref":"#/components/schemas/PublicChatLead"},{"type":"null"}],"description":"Present only when the assistant's web chat `dynamic_ui.lead_panel` is on: what this visitor has given so far, or null before there is a lead."}},"required":["status","handoff_requested_at","taken_over"]},"messages":{"type":"array","items":{"$ref":"#/components/schemas/PublicMessage"}}},"required":["conversation","messages"]}},"required":["data"]}}}},"401":{"description":"Missing, wrong or expired conversation token, or a conversation id the token does not belong to (`unauthorized`; an unknown id is a 401, never a 404)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"get-public-v1-conversations-id-updates"}},"/public/v1/conversations/{id}":{"get":{"tags":["Public chat API"],"summary":"Get a conversation's visible history","description":"Everything the visitor can see, for rebuilding the chat after a reload: status, whether a quiet conversation can be reopened, handoff state and the visible messages. `lead_name` is the first name of the captured lead, when there is one.","security":[{"conversationToken":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PublicConversation"}},"required":["data"]}}}},"401":{"description":"Missing, wrong or expired conversation token, or a conversation id the token does not belong to (`unauthorized`; an unknown id is a 401, never a 404)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Conversation id, `conv_...`. An id the token does not belong to answers 401."}],"operationId":"get-public-v1-conversations-id"}},"/public/v1/conversations/{id}/attachments":{"post":{"tags":["Public chat API"],"summary":"Upload a file for a message","description":"The request body is the raw file bytes (any content type), not a multipart form. Send the file name URI-encoded in `X-MLF-Filename`. The type is detected from the bytes, not the header or extension: JPEG, PNG, WebP, GIF, PDF and DOCX are accepted. Limits: 10 MB per file, 5 per message, 20 per conversation. Refused unless the assistant's web chat `attachments.enabled` is true. The file joins a message when POST /public/v1/conversations/{id}/messages names its id in `attachment_ids`.","security":[{"conversationToken":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Conversation id, `conv_...`. An id the token does not belong to answers 401."},{"name":"X-MLF-Filename","in":"header","required":false,"schema":{"type":"string"},"description":"URI-encoded file name. Defaults to `file`."}],"requestBody":{"required":true,"content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"responses":{"201":{"description":"File stored","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ConversationAttachment"}},"required":["data"]}}}},"401":{"description":"Missing, wrong or expired conversation token, or a conversation id the token does not belong to (`unauthorized`; an unknown id is a 401, never a 404)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`attachments_disabled`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`conversation_ended` or `attachment_limit_reached` (20 files per conversation)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"`attachment_too_large` (over 10 MB)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"`attachment_type_not_supported`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"`attachment_empty`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`attachments_unavailable`: this deployment cannot store files","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"post-public-v1-conversations-id-attachments"}},"/public/chat/attachments/{id}":{"get":{"tags":["Public chat API"],"summary":"Download an attachment","description":"Public: the signed link is the only credential. Links come from API responses (`url` on an upload or on a message's `rich.attachments`) and from webhook payloads. Links from the widget and console API last 24 hours; links in webhooks last 7 days. Both stop working sooner when the chat came from a test key that is revoked or a preview link that is turned off or expires.","security":[],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Attachment id, `att_...`."},{"name":"expires","in":"query","required":true,"schema":{"type":"integer"},"description":"Link expiry, Unix seconds."},{"name":"signature","in":"query","required":true,"schema":{"type":"string"},"description":"The link's signature. Use the URL as given; do not build it yourself."}],"responses":{"200":{"description":"The file bytes, with the file's own Content-Type. Content-Disposition is inline for images and attachment for documents.","headers":{"Content-Disposition":{"schema":{"type":"string"}}},"content":{"image/jpeg":{"schema":{"type":"string","format":"binary"}},"image/png":{"schema":{"type":"string","format":"binary"}},"image/webp":{"schema":{"type":"string","format":"binary"}},"image/gif":{"schema":{"type":"string","format":"binary"}},"application/pdf":{"schema":{"type":"string","format":"binary"}},"application/vnd.openxmlformats-officedocument.wordprocessingml.document":{"schema":{"type":"string","format":"binary"}}}},"404":{"description":"Invalid or expired link, or the file no longer exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"get-public-chat-attachments-id"}},"/public/v1/conversations/{id}/end":{"post":{"tags":["Public chat API"],"summary":"End a conversation","description":"Ends the conversation for good when the visitor closes it, and triggers its `conversation.ended` webhook and analysis. A conversation that ended only because the visitor went quiet reopens on their next message; one ended here does not.","security":[{"conversationToken":[]}],"responses":{"200":{"description":"Conversation ended","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"status":{"type":"string","enum":["ended"]}},"required":["status"]}},"required":["data"]}}}},"401":{"description":"Missing, wrong or expired conversation token, or a conversation id the token does not belong to (`unauthorized`; an unknown id is a 401, never a 404)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Conversation id, `conv_...`. An id the token does not belong to answers 401."}],"operationId":"post-public-v1-conversations-id-end"}},"/public/v1/conversations/{id}/messages/{messageId}/feedback":{"post":{"tags":["Public chat API"],"summary":"Rate an assistant reply","description":"Thumbs up or down on one of the assistant's replies, with an optional reason for a thumbs down. Sending again replaces the verdict; `none` takes it back. Feeds the quality view in chat analytics.","security":[{"conversationToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"rating":{"type":"string","enum":["up","down","none"]},"reason":{"type":"string","enum":["wrong","unhelpful","incomplete","other"]}},"required":["rating"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"rating":{"type":"string","enum":["up","down","none"]}},"required":["rating"]}},"required":["data"]}}}},"401":{"description":"Missing, wrong or expired conversation token, or a conversation id the token does not belong to (`unauthorized`; an unknown id is a 401, never a 404)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: the message is not one of this conversation's assistant replies","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"`invalid_feedback`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Conversation id, `conv_...`. An id the token does not belong to answers 401."},{"name":"messageId","in":"path","required":true,"schema":{"type":"string"},"description":"An assistant reply in this conversation, `msg_...`."}],"operationId":"post-public-v1-conversations-id-messages-messageid-feedback"}},"/public/v1/conversations/{id}/rating":{"post":{"tags":["Public chat API"],"summary":"Rate the conversation","description":"The visitor's 1 to 5 rating of the whole chat, with an optional comment. Sending again replaces it.","security":[{"conversationToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"rating":{"type":"integer","minimum":1,"maximum":5},"comment":{"type":"string","maxLength":500}},"required":["rating"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"rating":{"type":"integer","minimum":1,"maximum":5}},"required":["rating"]}},"required":["data"]}}}},"401":{"description":"Missing, wrong or expired conversation token, or a conversation id the token does not belong to (`unauthorized`; an unknown id is a 401, never a 404)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"`invalid_rating`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Conversation id, `conv_...`. An id the token does not belong to answers 401."}],"operationId":"post-public-v1-conversations-id-rating"}},"/public/v1/conversations/{id}/presence":{"post":{"tags":["Public chat API"],"summary":"Report the visitor is still here","description":"A heartbeat while the chat is open, so a person who has taken over can see the visitor is still on the page. Presence lapses 150 seconds after the last call; send one every minute or so. No request body.","security":[{"conversationToken":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"ok":{"type":"boolean","enum":[true]}},"required":["ok"]}},"required":["data"]}}}},"401":{"description":"Missing, wrong or expired conversation token, or a conversation id the token does not belong to (`unauthorized`; an unknown id is a 401, never a 404)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Conversation id, `conv_...`. An id the token does not belong to answers 401."}],"operationId":"post-public-v1-conversations-id-presence"}},"/public/v1/conversations/{id}/consent":{"post":{"tags":["Public chat API"],"summary":"Update the visitor's analytics consent","description":"Call when the visitor answers your cookie banner while a chat is open, so later analytics follow their choice.","security":[{"conversationToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"consent":{"type":"string","enum":["granted","denied","not_required"]}},"required":["consent"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"consent":{"type":"string","enum":["granted","denied","not_required"]}},"required":["consent"]}},"required":["data"]}}}},"401":{"description":"Missing, wrong or expired conversation token, or a conversation id the token does not belong to (`unauthorized`; an unknown id is a 401, never a 404)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"`invalid_consent`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Conversation id, `conv_...`. An id the token does not belong to answers 401."}],"operationId":"post-public-v1-conversations-id-consent"}},"/public/v1/telemetry":{"post":{"tags":["Public chat API"],"summary":"Report chat analytics events","description":"The funnel events a chat interface reports (loaded, shown, opened, typed, sent, result), up to 50 per batch, which chat analytics is built from. Optional for a custom interface. Rate limited to 120 a minute per IP address (429). Past the limiter it always answers 204 with no body, even for a body it cannot read or a key it does not know, so analytics never breaks a page; such batches are dropped.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"widget_key":{"type":"string","minLength":1,"maxLength":256,"description":"Publishable key, `pk_live_...`."},"assistant":{"type":"string","minLength":1,"maxLength":128,"description":"Assistant id; needed with a project-scoped publishable key."},"parent_origin":{"type":"string","maxLength":2048,"description":"Read as on conversation start: ignored from a browser on another site."},"visitor_id":{"type":"string","maxLength":128},"page":{"type":"string","maxLength":2048,"description":"The page URL the events happened on."},"page_view_id":{"type":"string","pattern":"^[a-zA-Z0-9_-]{1,128}$"},"visit_id":{"type":"string","pattern":"^[a-zA-Z0-9_-]{1,128}$","description":"The visit this page view belongs to. Stored only with analytics consent."},"visit_number":{"type":"integer","minimum":1,"maximum":1000000,"description":"Which visit this is for the visitor: 1 for the first."},"consent":{"type":"string","enum":["granted","denied","not_required"]},"widget_version":{"type":"string","maxLength":40},"referrer":{"type":"string","maxLength":2048},"landing":{"type":"string","maxLength":2048,"description":"The landing URL's query, read for campaign tags and never stored."},"test":{"type":"boolean"},"ga_client_id":{"type":"string","maxLength":128},"ga_session_id":{"type":"string","maxLength":64},"events":{"type":"array","minItems":1,"maxItems":50,"items":{"type":"object","properties":{"event_id":{"type":"string","pattern":"^[a-zA-Z0-9_-]{1,128}$","description":"Unique per event, so a resent batch counts once."},"name":{"type":"string","enum":["widget_loaded","widget_shown","widget_opened","composer_typed","message_send","message_result"]},"occurred_at":{"type":"integer","exclusiveMinimum":0,"description":"Milliseconds since the epoch, browser clock."},"reason":{"type":"string","enum":["proactive","visitor","api"],"description":"`widget_opened`: why the panel opened."},"page":{"type":"string","maxLength":2048},"conversation_id":{"type":"string","pattern":"^[a-zA-Z0-9_-]{1,128}$"},"client_message_id":{"type":"string","pattern":"^[a-zA-Z0-9_-]{1,128}$"},"status":{"type":"string","enum":["ok","rescued","failed"],"description":"`message_result` only."},"error_code":{"type":"string","pattern":"^[a-z0-9_]{1,64}$"},"duration_ms":{"type":"integer","minimum":0,"maximum":3600000},"first_token_ms":{"type":"integer","minimum":0,"maximum":3600000},"attempts":{"type":"integer","minimum":0,"maximum":20},"quick_reply":{"type":"boolean"},"reply_shown":{"type":"boolean"}},"required":["event_id","name"]}}},"required":["widget_key","events"]}}}},"responses":{"204":{"description":"Accepted (or ignored). No body."},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"post-public-v1-telemetry"}},"/public/v1/conversations/{id}/voice":{"post":{"tags":["Public chat API"],"summary":"Start a voice session","description":"Lets the visitor talk to the assistant through their microphone, in the same conversation. Available when the assistant's web chat `voice.enabled` is on (the widget config then has `voice`), never in a preview link's chat. Join the LiveKit room at `url` with `token` before `expires_at`, publish the microphone and play the agent's audio. The agent knows the conversation so far and carries on from it. Live captions arrive as LiveKit text streams on topic `lk.transcription`: the visitor's words come from identity `web_{voice_session_id}`, the assistant's from the agent, with attributes `lk.segment_id` and `lk.transcription_final`. The session ends after `max_seconds`, when either side hangs up, or when the browser never joins. The spoken turns then join the conversation as messages with `rich.metadata.channel` `voice` (read them through the updates route), and the conversation is checked for a lead. One session per conversation at a time.","security":[{"conversationToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"consent":{"type":"object","properties":{"microphone":{"type":"boolean","enum":[true]}},"required":["microphone"],"description":"The visitor agreed to use their microphone. Ask before you call this."}},"required":["consent"]}}}},"responses":{"201":{"description":"Voice session started","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"voice_session_id":{"type":"string","description":"Keep it to read or end the session."},"url":{"type":"string","description":"The LiveKit server to join, `wss://...`."},"token":{"type":"string","description":"A room token for this browser only."},"expires_at":{"type":"string","format":"date-time","description":"Join before this. The session itself then runs up to `max_seconds`."},"max_seconds":{"type":"integer","description":"The session's length cap, 60 to 600 seconds."}},"required":["voice_session_id","url","token","expires_at","max_seconds"]}},"required":["data"]}}}},"400":{"description":"`consent_required`: the body did not carry `consent.microphone: true`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, wrong or expired conversation token, or a conversation id the token does not belong to (`unauthorized`; an unknown id is a 401, never a 404)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`voice_disabled` (voice is off for the assistant, or this is a preview chat) or `origin_not_allowed`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`voice_session_active` (one is already running: end it first; `voice_session_id` names it when known), `conversation_ended`, or `conversation_taken_over` (a person is answering)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"`key_quota_exceeded`: the publishable key has started 60 voice sessions this hour, 10 from this IP address, or used its 300 voice minutes for the UTC day. `voice_busy`: the key already has 5 sessions running. `rate_limited`: the ordinary per-address limit. All carry `Retry-After`, in seconds.","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`voice_unavailable`: voice calls cannot be placed right now. When the cause is passing it carries `Retry-After` (5 seconds); without the header, voice is not set up on this deployment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Conversation id, `conv_...`. An id the token does not belong to answers 401."}],"operationId":"post-public-v1-conversations-id-voice"}},"/public/v1/conversations/{id}/voice/{voiceSessionId}":{"get":{"tags":["Public chat API"],"summary":"Get a voice session","description":"Whether the session is still connecting, running or over, and why it ended. Only sessions of this conversation are found.","security":[{"conversationToken":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"voice_session_id":{"type":"string","description":"The voice session's id, which is also its call's id."},"status":{"type":"string","enum":["connecting","active","ended"]},"ended_reason":{"type":["string","null"],"description":"Null until the session ends. Then the call's ended reason (for example `customer_hangup`, `assistant_ended` or `max_duration`), `visitor_ended` when you ended it through this API, `not_joined` when the browser never joined, or `expired`."},"max_seconds":{"type":["integer","null"],"description":"The session's length cap, in seconds."}},"required":["voice_session_id","status","ended_reason","max_seconds"]}},"required":["data"]}}}},"401":{"description":"Missing, wrong or expired conversation token, or a conversation id the token does not belong to (`unauthorized`; an unknown id is a 401, never a 404)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: no such voice session in this conversation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Conversation id, `conv_...`. An id the token does not belong to answers 401."},{"name":"voiceSessionId","in":"path","required":true,"schema":{"type":"string"},"description":"The `voice_session_id` that starting the session returned."}],"operationId":"get-public-v1-conversations-id-voice-voicesessionid"}},"/public/v1/conversations/{id}/voice/{voiceSessionId}/end":{"post":{"tags":["Public chat API"],"summary":"End a voice session","description":"Hangs up for both sides. Safe to repeat: a session that already ended answers with its state. The conversation stays open for typing.","security":[{"conversationToken":[]}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"voice_session_id":{"type":"string","description":"The voice session's id, which is also its call's id."},"status":{"type":"string","enum":["connecting","active","ended"]},"ended_reason":{"type":["string","null"],"description":"Null until the session ends. Then the call's ended reason (for example `customer_hangup`, `assistant_ended` or `max_duration`), `visitor_ended` when you ended it through this API, `not_joined` when the browser never joined, or `expired`."},"max_seconds":{"type":["integer","null"],"description":"The session's length cap, in seconds."}},"required":["voice_session_id","status","ended_reason","max_seconds"]}},"required":["data"]}}}},"401":{"description":"Missing, wrong or expired conversation token, or a conversation id the token does not belong to (`unauthorized`; an unknown id is a 401, never a 404)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: no such voice session in this conversation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"`voice_unavailable`: the call could not be ended just now and may still be running. Retry after `Retry-After` seconds.","headers":{"Retry-After":{"schema":{"type":"integer"},"description":"Seconds to wait."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Conversation id, `conv_...`. An id the token does not belong to answers 401."},{"name":"voiceSessionId","in":"path","required":true,"schema":{"type":"string"},"description":"The `voice_session_id` that starting the session returned."}],"operationId":"post-public-v1-conversations-id-voice-voicesessionid-end"}},"/api/integrations/whatsapp/connections":{"get":{"tags":["WhatsApp"],"responses":{"200":{"description":"Connections","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WhatsAppConnection"}}},"required":["data"]}}}},"400":{"description":"`invalid_request`: the body failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`org_scope_required` (needs an org-wide key; project keys get 403) or the role is too low","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: no such connection or number in this organization","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`whatsapp_already_connected`, `whatsapp_credentials_required`, `whatsapp_disconnected`, or on sync `whatsapp_number_already_connected` (a number already belongs to another connection)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Meta refused the request (`meta_<Meta error code>`, or `meta_http_<status>`), or could not be reached (`meta_unreachable`, which says nothing about whether Meta acted on it)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"get-integrations-whatsapp-connections","summary":"List WhatsApp accounts and numbers","description":"Every connected WhatsApp Business Account with its numbers, the assistant each number is assigned to, and counts of replies and turns that failed. Credentials are never returned. Needs an org-wide key.","security":[{"apiKey":[]}]},"post":{"tags":["WhatsApp"],"responses":{"201":{"description":"Connected","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WhatsAppConnection"}},"required":["data"]}}}},"400":{"description":"`invalid_request`: the body failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`org_scope_required` (needs an org-wide key; project keys get 403) or the role is too low","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: no such connection or number in this organization","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`whatsapp_already_connected`, `whatsapp_credentials_required`, `whatsapp_disconnected`, or on sync `whatsapp_number_already_connected` (a number already belongs to another connection)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Meta refused the request (`meta_<Meta error code>`, or `meta_http_<status>`), or could not be reached (`meta_unreachable`, which says nothing about whether Meta acted on it)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"post-integrations-whatsapp-connections","summary":"Connect a WhatsApp Business account","description":"Checks the credentials with Meta, stores them encrypted and imports the account's phone numbers, unassigned. Then subscribe the account to its callback and assign numbers to assistants. Needs an org-wide key.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","waba_id","credentials"],"properties":{"name":{"type":"string","minLength":1,"maxLength":100,"description":"Your label for the account."},"waba_id":{"type":"string","pattern":"^[0-9]+$","description":"The WhatsApp Business Account id (digits)."},"api_version":{"type":"string","default":"v25.0","pattern":"^v[0-9]+\\.0$","description":"Graph API version."},"credentials":{"type":"object","required":["access_token","app_secret","verify_token"],"description":"From your Meta app. Write-only: stored encrypted and never returned.","properties":{"access_token":{"type":"string","minLength":1,"maxLength":8192,"writeOnly":true,"description":"A system user access token with WhatsApp permissions."},"app_secret":{"type":"string","minLength":1,"maxLength":256,"writeOnly":true,"description":"Verifies Meta's callback signatures."},"verify_token":{"type":"string","minLength":16,"maxLength":256,"writeOnly":true,"description":"Any string you choose; Meta echoes it when it verifies the callback."}}}}}}}},"security":[{"apiKey":[]}]}},"/api/integrations/whatsapp/connections/{id}":{"patch":{"tags":["WhatsApp"],"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WhatsAppConnection"}},"required":["data"]}}}},"400":{"description":"Invalid body, or resuming would route a number to an assistant whose chat model cannot answer","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`org_scope_required` (needs an org-wide key; project keys get 403) or the role is too low","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: no such connection or number in this organization","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`whatsapp_already_connected`, `whatsapp_credentials_required`, `whatsapp_disconnected`, or on sync `whatsapp_number_already_connected` (a number already belongs to another connection)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Meta refused the request (`meta_<Meta error code>`, or `meta_http_<status>`), or could not be reached (`meta_unreachable`, which says nothing about whether Meta acted on it)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"patch-integrations-whatsapp-connections-id","summary":"Update a WhatsApp connection","description":"`enabled: false` pauses every number on the account; `true` resumes. New `credentials` are checked with Meta before they replace the stored ones. A disconnected account needs `credentials` to resume (409 `whatsapp_credentials_required`).","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"WhatsApp connection id."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":100},"enabled":{"type":"boolean"},"credentials":{"type":"object","required":["access_token","app_secret","verify_token"],"description":"From your Meta app. Write-only: stored encrypted and never returned.","properties":{"access_token":{"type":"string","minLength":1,"maxLength":8192,"writeOnly":true,"description":"A system user access token with WhatsApp permissions."},"app_secret":{"type":"string","minLength":1,"maxLength":256,"writeOnly":true,"description":"Verifies Meta's callback signatures."},"verify_token":{"type":"string","minLength":16,"maxLength":256,"writeOnly":true,"description":"Any string you choose; Meta echoes it when it verifies the callback."}}}}}}}},"security":[{"apiKey":[]}]},"delete":{"tags":["WhatsApp"],"responses":{"200":{"description":"Disconnected","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"disconnected":{"type":"boolean","enum":[true]}},"required":["disconnected"]}},"required":["data"]}}}},"400":{"description":"`invalid_request`: the body failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`org_scope_required` (needs an org-wide key; project keys get 403) or the role is too low","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: no such connection or number in this organization","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`whatsapp_already_connected`, `whatsapp_credentials_required`, `whatsapp_disconnected`, or on sync `whatsapp_number_already_connected` (a number already belongs to another connection)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Meta refused the request (`meta_<Meta error code>`, or `meta_http_<status>`), or could not be reached (`meta_unreachable`, which says nothing about whether Meta acted on it)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"delete-integrations-whatsapp-connections-id","summary":"Disconnect a WhatsApp account","description":"Disconnects the account: messages stop being answered and the stored credentials are erased. Numbers, assignments and conversation history stay, so reconnecting with new credentials picks up where it left off.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"WhatsApp connection id."}],"security":[{"apiKey":[]}]}},"/api/integrations/whatsapp/connections/{id}/numbers/{numberId}":{"put":{"tags":["WhatsApp"],"responses":{"200":{"description":"Assigned","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WhatsAppNumber"}},"required":["data"]}}}},"400":{"description":"Invalid body, or the assistant's chat model has no chat adapter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`org_scope_required` (needs an org-wide key; project keys get 403) or the role is too low","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: no such connection or number in this organization","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`whatsapp_already_connected`, `whatsapp_credentials_required`, `whatsapp_disconnected`, or on sync `whatsapp_number_already_connected` (a number already belongs to another connection)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Meta refused the request (`meta_<Meta error code>`, or `meta_http_<status>`), or could not be reached (`meta_unreachable`, which says nothing about whether Meta acted on it)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"put-integrations-whatsapp-connections-id-numbers-numberid","summary":"Assign a WhatsApp number to an assistant","description":"Routes the number's WhatsApp messages to an assistant (`assistant_id: null` unassigns it), optionally pausing just this number. The assistant answers with its web chat model, so that model must be one chat supports.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"WhatsApp connection id."},{"name":"numberId","in":"path","required":true,"schema":{"type":"string"},"description":"WhatsApp number id, from the connection's `numbers`."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["assistant_id"],"properties":{"assistant_id":{"type":["string","null"],"minLength":1,"description":"Assistant id, or null to unassign."},"enabled":{"type":"boolean","description":"False pauses this number only."},"mode":{"type":"string","enum":["live","test"]}}}}}},"security":[{"apiKey":[]}]}},"/api/integrations/whatsapp/connections/{id}/subscribe":{"post":{"tags":["WhatsApp"],"responses":{"200":{"description":"Subscribed","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"subscribed":{"type":"boolean","enum":[true]}},"required":["subscribed"]}},"required":["data"]}}}},"400":{"description":"`invalid_request`: the body failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`org_scope_required` (needs an org-wide key; project keys get 403) or the role is too low","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: no such connection or number in this organization","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`whatsapp_already_connected`, `whatsapp_credentials_required`, `whatsapp_disconnected`, or on sync `whatsapp_number_already_connected` (a number already belongs to another connection)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Meta refused the request (`meta_<Meta error code>`, or `meta_http_<status>`), or could not be reached (`meta_unreachable`, which says nothing about whether Meta acted on it)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"post-integrations-whatsapp-connections-id-subscribe","summary":"Subscribe an account to its message callback","description":"Tells Meta to post this account's messages to its callback, then Meta verifies it with your `verify_token`. Use the public API origin with the connection's `webhook_path` (`/public/whatsapp/webhooks/{id}`); the older `/whatsapp/webhooks/{id}` path is also accepted. `callback_url` must be HTTPS with no query, fragment or credentials, at one of those paths (otherwise 400 `whatsapp_public_https_webhook_required`).","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"WhatsApp connection id."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["callback_url"],"properties":{"callback_url":{"type":"string","format":"uri","maxLength":2048,"pattern":"^https://"}}}}}},"security":[{"apiKey":[]}]}},"/api/integrations/whatsapp/connections/{id}/sync":{"post":{"tags":["WhatsApp"],"responses":{"200":{"description":"The account's numbers","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WhatsAppNumber"}}},"required":["data"]}}}},"400":{"description":"`invalid_request`: the body failed validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`org_scope_required` (needs an org-wide key; project keys get 403) or the role is too low","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"`not_found`: no such connection or number in this organization","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"`whatsapp_already_connected`, `whatsapp_credentials_required`, `whatsapp_disconnected`, or on sync `whatsapp_number_already_connected` (a number already belongs to another connection)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Meta refused the request (`meta_<Meta error code>`, or `meta_http_<status>`), or could not be reached (`meta_unreachable`, which says nothing about whether Meta acted on it)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"post-integrations-whatsapp-connections-id-sync","summary":"Sync an account's phone numbers","description":"Fetches the account's current phone numbers from Meta. New numbers arrive unassigned; existing ones keep their assistant.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"WhatsApp connection id."}],"security":[{"apiKey":[]}]}},"/api/integrations":{"get":{"tags":["Provider credentials"],"summary":"List provider credentials","description":"Which providers have a credential stored for the project (model, speech and telephony providers), with when it was saved; the secrets themselves are never returned. Credentials belong to one project; an org-wide key must name `project_id`.","parameters":[{"name":"project_id","in":"query","required":false,"schema":{"type":"string"},"description":"The project. Required with an org-wide key."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Integration"}}},"required":["data"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"get-integrations","security":[{"apiKey":[]}]}},"/api/integrations/{provider}":{"put":{"tags":["Provider credentials"],"summary":"Save a provider credential","description":"Saves your own account's key for a provider, so assistants in the project run on it. It is stored encrypted and never returned. Where the provider can be checked, the key is tried first and a refused key is not stored (422). `verified: false` means it could not be checked; it is stored anyway.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntegrationCreate"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/IntegrationStatus"}},"required":["data"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"credential_rejected: the provider refused the key. Nothing is stored.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,63}$"},"description":"Provider slug as assistants name it in `provider`, for example `openai`, `deepgram`, `elevenlabs`, `twilio`."}],"operationId":"put-integrations-provider","security":[{"apiKey":[]}]},"delete":{"tags":["Provider credentials"],"summary":"Remove a provider credential","description":"Deletes the project's stored key for this provider.","parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,63}$"},"description":"Provider slug as assistants name it in `provider`, for example `openai`, `deepgram`, `elevenlabs`, `twilio`."},{"name":"project_id","in":"query","required":false,"schema":{"type":"string"},"description":"The project. Required with an org-wide key."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/IntegrationDeleteResult"}},"required":["data"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"delete-integrations-provider","security":[{"apiKey":[]}]}},"/api/registry":{"get":{"tags":["Voices and models"],"summary":"Get the provider and model registry","description":"Static catalog aligned with the supported LiveKit Agents SDK version.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProviderRegistry"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"get-registry","security":[{"apiKey":[]}]}},"/api/tts/{provider}/library":{"get":{"tags":["Voices and models"],"summary":"Browse a provider's shared voice library","description":"Loads a page from a provider's public voice library using the project's stored credential.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProviderLibraryResponse"}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string"}},{"name":"project_id","in":"query","required":false,"schema":{"type":"string"}},{"name":"search","in":"query","required":false,"schema":{"type":"string"}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":0}},{"name":"gender","in":"query","required":false,"schema":{"type":"string"}},{"name":"language","in":"query","required":false,"schema":{"type":"string"}}],"operationId":"get-tts-provider-library","security":[{"apiKey":[]}]}},"/api/tts/{provider}/library/add":{"post":{"tags":["Voices and models"],"summary":"Add a shared voice to the provider account","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddLibraryVoiceRequest"}}}},"responses":{"200":{"description":"Voice added","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddLibraryVoiceResponse"}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"502":{"description":"Provider request failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string"}},{"name":"project_id","in":"query","required":false,"schema":{"type":"string"}}],"operationId":"post-tts-provider-library-add","security":[{"apiKey":[]}]}},"/api/tts/{provider}/models":{"get":{"tags":["Voices and models"],"summary":"Discover provider models","description":"Loads current TTS models, Anthropic models, or Bedrock chat models using tenant credentials or the platform connection.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProviderModelsResponse"}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string"}},{"name":"project_id","in":"query","required":false,"schema":{"type":"string"}}],"operationId":"get-tts-provider-models","security":[{"apiKey":[]}]}},"/api/tts/{provider}/voices":{"get":{"tags":["Voices and models"],"summary":"Discover a provider's TTS voices","description":"Loads the current voice inventory from the provider using the project's stored credential.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProviderVoicesResponse"}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string"}},{"name":"project_id","in":"query","required":false,"schema":{"type":"string"}}],"operationId":"get-tts-provider-voices","security":[{"apiKey":[]}]}},"/api/conversations":{"get":{"tags":["Conversations"],"summary":"List conversations","description":"Web chat and WhatsApp conversations, newest first by default, one cursor page at a time. Each row is the conversation without its messages; fetch one by id for the transcript. Test conversations are left out unless `include_test=true`.","parameters":[{"name":"project_id","in":"query","required":false,"schema":{"type":"string"},"description":"Only this project's conversations (an org-wide key sees every project by default)."},{"name":"assistant_id","in":"query","required":false,"style":"form","explode":true,"schema":{"type":"array","items":{"type":"string"}},"description":"Only these assistants' conversations. Repeat the parameter or comma-separate the values; a row matching any of them is kept."},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["open","ended"]},"description":"Only open or only ended conversations."},{"name":"q","in":"query","required":false,"schema":{"type":"string","description":"Case-insensitive literal substring across conversation id and lead name/email."}},{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Created at or after this time (ISO 8601 with an offset)."},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Created at or before this time (ISO 8601 with an offset)."},{"name":"engagement","description":"`engaged`: the visitor replied at least once. `no_reply`: they never did.","in":"query","required":false,"schema":{"type":"string","enum":["engaged","no_reply","all"],"default":"all"}},{"name":"handoff","in":"query","required":false,"description":"requested: a handoff was asked for. pending: asked for, nobody has taken over, chat open. answered: a human took over at or after the request. missed: the chat ended before anyone took over (never an abandoned chat). missed_in_hours: missed, asked while advisors were said to be available. out_of_hours: asked while advisors were said to be unavailable (outside working hours). active: someone holds the seat now. taken_over: a human took over at least once, asked for or not. abandoned: a human took over and then left the visitor waiting (human_outcome abandoned).","schema":{"type":"string","enum":["requested","pending","answered","missed","missed_in_hours","out_of_hours","active","taken_over","abandoned"]}},{"name":"include_test","in":"query","required":false,"description":"Test chats (metadata.test, e.g. the console's test chat) are left out unless this is true. Ignored when `mode` is given.","schema":{"type":"boolean","default":false}},{"name":"mode","in":"query","required":false,"description":"Which chats to include, by the mode the server gave each. Absent: test and preview chats are left out, and so are live chats a page marked as a test, as before. live: every live-mode chat, including those a page marked as a test. test: test-mode chats. preview: preview chats. all: every chat.","schema":{"type":"string","enum":["live","test","preview","all"]}},{"name":"needs_review","in":"query","required":false,"description":"When true, only conversations whose analysis flagged the human advisor's replies with severity 2 or more (see ConversationAnalysis.human_review).","schema":{"type":"boolean"}},{"name":"taken_over_by","in":"query","required":false,"schema":{"type":"string"},"description":"Only conversations this operator id (the takeover `agent.id`) holds or held."},{"name":"sort","in":"query","required":false,"description":"Sort by creation time or by number of messages.","schema":{"type":"string","enum":["created_at","message_count"],"default":"created_at"}},{"name":"dir","in":"query","required":false,"description":"Sort direction.","schema":{"type":"string","enum":["asc","desc"],"default":"desc"}},{"name":"cursor","in":"query","required":false,"description":"`next_cursor` from the previous page. Keep the same filters and sort while paging.","schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"description":"Page size.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"include_total","in":"query","required":false,"description":"When true, the response carries `total`: the number of conversations matching every filter, ignoring the cursor.","schema":{"type":"boolean","default":false}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Conversation"}},"next_cursor":{"type":["string","null"],"description":"Opaque pagination cursor."},"has_more":{"type":"boolean"},"total":{"type":"integer","minimum":0,"description":"Conversations matching the filters across all pages. Present only when include_total=true."},"live_handoff":{"type":"boolean","description":"Whether the org takes web chats over live. When false, takeover is refused and handoff requests are callback requests."}},"required":["data","next_cursor","has_more","live_handoff"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"get-conversations","security":[{"apiKey":[]}]}},"/api/conversations/{id}":{"get":{"tags":["Conversations"],"summary":"Get a conversation","description":"The conversation with its messages, usage lines, lead and latest analysis. Tool and system rows, and assistant tool-call steps, are returned only to instance staff, org admins and owners, and API keys. Members and viewers get the transcript the visitor saw.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Conversation"}},"required":["data"]}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Conversation id, `conv_...`."}],"operationId":"get-conversations-id","security":[{"apiKey":[]}]}},"/api/conversations/{id}/journey":{"get":{"tags":["Conversations"],"summary":"The visits this conversation's visitor made to the site","description":"Up to 20 recent visits, newest first, across the organization's projects, by the conversation's visitor id and by the visitor ids of other conversations in the same organization whose lead shares its email, phone number (digits only, at least 7) or customer id (at most 10 visitors, listed in `visitors`); test traffic excluded. A conversation without a visitor id (no analytics consent, or an older widget) and without linked chats returns visitors and visits empty.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/VisitorJourney"}},"required":["data"]}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"operationId":"get-conversations-id-journey","security":[{"apiKey":[]}]}},"/api/conversations/{id}/messages":{"post":{"tags":["Conversations"],"summary":"Send a message as a person","description":"Only the operator holding the seat may send (403 otherwise). With `client_message_id`, a retry by the same operator returns the original message instead of sending it again; reusing the id for different content is 409 `client_message_id_reused`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"content":{"type":"string","minLength":1,"maxLength":4000},"agent":{"$ref":"#/components/schemas/AgentIdentityInput"},"client_message_id":{"type":"string","minLength":1,"maxLength":200,"description":"Unique per message from this operator; makes a retried send safe."}},"required":["content"]}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ConversationMessage"}},"required":["data"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Conversation id, `conv_...`."}],"operationId":"post-conversations-id-messages","security":[{"apiKey":[]}]}},"/api/conversations/{id}/release":{"post":{"tags":["Conversations"],"summary":"Hand a conversation back to the assistant","description":"The holder releases their own seat; org admins and API keys may release anyone's, which is recorded as a forced release naming the previous holder. Send the same `agent` you took over with, so the release is recorded as yours rather than forced. Release still works when the org has turned live handoff off.","requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationTakeoverRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ConversationTakeover"}},"required":["data"]}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Conversation id, `conv_...`."}],"operationId":"post-conversations-id-release","security":[{"apiKey":[]}]}},"/api/conversations/{id}/takeover":{"post":{"tags":["Conversations"],"summary":"Take over a conversation","description":"Only one operator holds a seat: when another already does, 409 `already_taken_over` names them in `taken_over_by`. Taking over a seat you hold refreshes it. 409 `live_handoff_disabled` when the org does not take web chats over live. Heartbeat and operator messages refuse the same way. With an API key, name the person in `agent` (otherwise the holder is `api_key`) and send the same `agent` on every message, heartbeat and release. A seat with no heartbeat for three minutes is released.","requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationTakeoverRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ConversationTakeover"}},"required":["data"]}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Conversation id, `conv_...`."}],"operationId":"post-conversations-id-takeover","security":[{"apiKey":[]}]}},"/api/conversations/{id}/takeover/heartbeat":{"post":{"tags":["Conversations"],"summary":"Keep a takeover alive","description":"Call at least once a minute while a person is working the chat. A seat with no heartbeat for three minutes is released and the assistant answers again. Send the same `agent` you took over with.","requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationTakeoverRequest"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ConversationTakeoverHeartbeat"}},"required":["data"]}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Conversation id, `conv_...`."}],"operationId":"post-conversations-id-takeover-heartbeat","security":[{"apiKey":[]}]}},"/api/conversations/handoff-summary":{"get":{"tags":["Conversations"],"summary":"Summarize human handoffs","description":"Conversations created in the range (inclusive), test conversations excluded unless `mode` says otherwise. answer_seconds runs from the handoff request to the first takeover. first_response_seconds and longest_wait_seconds are taken over the conversations with human_metrics.","parameters":[{"name":"project_id","in":"query","required":false,"schema":{"type":"string"},"description":"Only this project's conversations."},{"name":"assistant_id","in":"query","required":false,"style":"form","explode":true,"schema":{"type":"array","items":{"type":"string"}},"description":"Only these assistants' conversations. Repeat the parameter or comma-separate the values; a row matching any of them is kept."},{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Created at or after this time."},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Created at or before this time."},{"name":"mode","in":"query","required":false,"description":"Which chats to include, by the mode the server gave each. Absent: test and preview chats are left out, and so are live chats a page marked as a test, as before. live: every live-mode chat, including those a page marked as a test. test: test-mode chats. preview: preview chats. all: every chat.","schema":{"type":"string","enum":["live","test","preview","all"]}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ConversationHandoffSummary"}},"required":["data"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"get-conversations-handoff-summary","security":[{"apiKey":[]}]}},"/api/test-data":{"get":{"tags":["Conversations"],"summary":"Count a project's test data","description":"What POST /v1/test-data/clear would delete now: test-mode and preview chats, their files, the leads they captured, and test telemetry. An estimate, as tests may start meanwhile.","parameters":[{"name":"project_id","in":"query","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"project_id":{"type":"string"},"conversations":{"type":"integer","description":"Test and preview chats there are now."},"leads":{"type":"integer","description":"Test and preview leads."},"events":{"type":"integer","description":"Chat events and request outcomes."},"attachments":{"type":"integer","description":"Uploaded files."},"calls":{"type":"integer","description":"Finished test calls (with their costs, analysis and recording rows)."}},"required":["project_id","conversations","leads","events","attachments"]}},"required":["data"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"get-test-data","security":[{"apiKey":[]}]}},"/api/test-data/clear":{"post":{"tags":["Conversations"],"summary":"Clear a project's test and preview chats and leads","description":"Deletes the project's test-mode and preview chats started before the request (with their messages, files, analysis, telemetry and webhook events) and the leads they captured. Live chats are never touched, nor are live chats whose page only claimed to be a test. Sends no webhooks. Returns what it deleted.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"project_id":{"type":"string"}},"required":["project_id"]}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"project_id":{"type":"string"},"conversations":{"type":"integer","description":"Test and preview chats deleted."},"leads":{"type":"integer","description":"Test and preview leads."},"events":{"type":"integer","description":"Chat events and request outcomes."},"attachments":{"type":"integer","description":"Uploaded files."},"calls":{"type":"integer","description":"Finished test calls (with their costs, analysis and recording rows)."},"attachment_failures":{"type":"integer","description":"Files whose delete failed (logged)."}},"required":["project_id","conversations","leads","events","attachments"]}},"required":["data"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"post-test-data-clear","security":[{"apiKey":[]}]}},"/api/leads":{"get":{"tags":["Leads"],"summary":"List leads","description":"Leads assistants captured from web chat and WhatsApp, newest first, one cursor page at a time. A lead is created when the assistant takes the visitor's details and updated as the conversation goes on; subscribe to the `lead.captured` webhook to hear about each one as it happens.","parameters":[{"name":"project_id","in":"query","required":false,"schema":{"type":"string"},"description":"Only this project's leads."},{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Created at or after this time."},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Created at or before this time."},{"name":"mode","in":"query","required":false,"schema":{"type":"string","enum":["live","test","preview","all"],"default":"live"},"description":"Only leads captured in chats of this mode: live by default, so test and preview chats' leads are left out unless asked for. `all` lists every lead."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string","description":"Opaque next_cursor from the previous page. Legacy ISO timestamp cursors are also accepted."}},{"name":"limit","in":"query","required":false,"description":"Page size.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Lead"}},"next_cursor":{"type":["string","null"],"description":"Opaque cursor for the next page."},"has_more":{"type":"boolean"}},"required":["data","next_cursor","has_more"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"get-leads","security":[{"apiKey":[]}]}},"/api/leads/{id}":{"get":{"tags":["Leads"],"summary":"Get a lead","description":"One lead with the details the assistant captured, the conversation it came from and any outcome you reported.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Lead"}},"required":["data"]}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Lead id, `lead_...`."}],"operationId":"get-leads-id","security":[{"apiKey":[]}]}},"/api/leads/{id}/outcome":{"put":{"tags":["Leads"],"summary":"Report a lead's outcome","description":"Call from your CRM when a lead turns into a booking, is lost, or goes back to open, so chat analytics can count bookings and revenue. Replaces the whole outcome: a field you leave out is cleared, and `at` defaults to now. `customer_id` is separate: leaving it out keeps the stored one.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeadOutcomeInput"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Lead"}},"required":["data"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Lead id, `lead_...`."}],"operationId":"put-leads-id-outcome","security":[{"apiKey":[]}]}},"/api/webhooks":{"post":{"tags":["Webhooks"],"summary":"Create a webhook subscription","description":"Subscribes an HTTPS endpoint to events in one project. Each delivery is a JSON POST signed with `X-Webhook-Signature: sha256=<hex HMAC-SHA256 of the raw body, keyed with the secret>`; answer with any 2xx within 10 seconds. The response carries the signing `secret` this once: store it. Send your own `secret`, or one is minted for you. Event types: `call.started`, `call.connected`, `call.ended`, `conversation.started`, `conversation.message.created`, `conversation.handoff_requested`, `conversation.callback_requested`, `conversation.takeover.started`, `conversation.takeover.released`, `conversation.analyzed`, `conversation.ended`, `lead.captured`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookSubscriptionCreate"}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WebhookSubscriptionCreated"}},"required":["data"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"post-webhooks","security":[{"apiKey":[]}]},"get":{"tags":["Webhooks"],"summary":"List webhook subscriptions","description":"Subscriptions in scope, without their signing secrets (a secret is shown only when the subscription is created).","parameters":[{"name":"project_id","in":"query","required":false,"schema":{"type":"string"},"description":"Only this project's subscriptions."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WebhookSubscription"}}},"required":["data"]}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"get-webhooks","security":[{"apiKey":[]}]}},"/api/webhooks/{id}":{"get":{"tags":["Webhooks"],"summary":"Get a webhook subscription","description":"One subscription, without its signing secret.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WebhookSubscription"}},"required":["data"]}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Subscription id, `whsub_...`."}],"operationId":"get-webhooks-id","security":[{"apiKey":[]}]},"patch":{"tags":["Webhooks"],"summary":"Update a webhook subscription","description":"Change the URL, the events, the secret, or turn the subscription off and on. Send at least one field. A new `secret` applies to the next attempt, including retries already queued; it is not echoed back.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookSubscriptionPatch"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WebhookSubscription"}},"required":["data"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Subscription id, `whsub_...`."}],"operationId":"patch-webhooks-id","security":[{"apiKey":[]}]},"delete":{"tags":["Webhooks"],"summary":"Delete a webhook subscription","description":"Stops deliveries for good, including retries still pending, and deletes the subscription's delivery log.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/DeleteResult"}},"required":["data"]}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Subscription id, `whsub_...`."}],"operationId":"delete-webhooks-id","security":[{"apiKey":[]}]}},"/api/webhooks/{id}/deliveries":{"get":{"tags":["Webhooks"],"summary":"List a subscription's recent deliveries","description":"The latest 100 deliveries, newest first: one row per event, with the cumulative `attempts` and the latest attempt's status and error, without the event body. Each event is tried up to 10 times with backoff from 10 seconds (about 85 minutes in all). `failed` means it never got a 2xx: every attempt failed, the subscription was turned off meanwhile, or it was still pending after 24 hours.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Subscription id, `whsub_...`."},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["pending","delivered","failed"]},"description":"Only deliveries in this state."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WebhookDelivery"}}},"required":["data"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"get-webhooks-id-deliveries","security":[{"apiKey":[]}]}},"/api/analytics/latency":{"get":{"tags":["Call analytics"],"summary":"Get call latency","description":"End-to-end response latency (p50 and p95, in milliseconds) and per-stage percentiles over the window, with the slowest calls. Percentiles are null when there are no measured calls. The body is not wrapped in `data`.","parameters":[{"name":"project_id","in":"query","required":false,"schema":{"type":"string"},"description":"Only this project's calls (an org-wide key covers every project by default)."},{"name":"assistant_id","in":"query","required":false,"style":"form","explode":true,"schema":{"type":"array","items":{"type":"string"}},"description":"Only these assistants' calls. Repeat the parameter or comma-separate the values; a row matching any of them is kept."},{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Start of the window, ISO 8601 with an offset. Default: 30 days before `to`."},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"End of the window. Default: now."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalyticsLatency"}}}},"400":{"description":"`invalid_range`: `from` or `to` is not a valid timestamp, or `from` is not before `to`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"get-analytics-latency","security":[{"apiKey":[]}]}},"/api/analytics/volume":{"get":{"tags":["Call analytics"],"summary":"Get call volume","description":"Call counts and minutes over the window: totals, by status, by call type, by why calls ended, and per day. The window defaults to the last 30 days. The body is not wrapped in `data`.","parameters":[{"name":"project_id","in":"query","required":false,"schema":{"type":"string"},"description":"Only this project's calls (an org-wide key covers every project by default)."},{"name":"assistant_id","in":"query","required":false,"style":"form","explode":true,"schema":{"type":"array","items":{"type":"string"}},"description":"Only these assistants' calls. Repeat the parameter or comma-separate the values; a row matching any of them is kept."},{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Start of the window, ISO 8601 with an offset. Default: 30 days before `to`."},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"End of the window. Default: now."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalyticsVolume"}}}},"400":{"description":"`invalid_range`: `from` or `to` is not a valid timestamp, or `from` is not before `to`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"get-analytics-volume","security":[{"apiKey":[]}]}},"/api/chat-analytics/failures":{"get":{"tags":["Chat analytics"],"summary":"List failed chat requests","description":"Each public chat request (config, events, start, send) that failed or was refused, with its status, outcome code, site, browser and request id, for chasing a broken embed. A malformed cursor is a 400.","parameters":[{"name":"project_id","in":"query","required":false,"schema":{"type":"string"},"description":"Only this project. An org-wide key covers every project by default."},{"name":"assistant_id","in":"query","required":false,"style":"form","explode":true,"schema":{"type":"array","items":{"type":"string"}},"description":"Only these assistants. Repeat the parameter or comma-separate the values; a row matching any of them is kept."},{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Default: 24 hours before `to`."},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Default: now. Ranges over 90 days are rejected."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"`next_cursor` from the previous page."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Page size."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ChatFailure"}},"next_cursor":{"type":["string","null"]},"has_more":{"type":"boolean"}},"required":["data","next_cursor","has_more"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"get-chat-analytics-failures","security":[{"apiKey":[]}]}},"/api/chat-analytics/funnel":{"get":{"tags":["Chat analytics"],"summary":"Get the visitor funnel","description":"Page views at each stage (loaded, shown, opened, typed, sent), then sends, conversations, leads, handoffs and bookings, optionally split by a dimension and compared with the window before.","parameters":[{"name":"project_id","in":"query","required":false,"schema":{"type":"string"},"description":"Only this project. An org-wide key covers every project by default."},{"name":"assistant_id","in":"query","required":false,"style":"form","explode":true,"schema":{"type":"array","items":{"type":"string"}},"description":"Only these assistants. Repeat the parameter or comma-separate the values; a row matching any of them is kept."},{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Default: 24 hours before `to`."},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Default: now. Ranges over 90 days are rejected."},{"name":"group_by","in":"query","required":false,"schema":{"type":"string","enum":["none","site","page","device","browser","source","hour","open_reason","visitor_type"],"default":"none"},"description":"Split the funnel into one group per value of this dimension."},{"name":"compare","in":"query","required":false,"schema":{"type":"string","enum":["previous"]},"description":"`previous` adds each group's counts for the window of the same length just before."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ChatFunnel"}},"required":["data"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"get-chat-analytics-funnel","security":[{"apiKey":[]}]}},"/api/chat-analytics/health":{"get":{"tags":["Chat analytics"],"summary":"Get chat health by site","description":"For each site the chat runs on: page views, shown and opened panels, sends, failures and refusals, reply time percentiles, the last successful reply and open incidents, with a status (`ok`, `warning`, `critical`, `idle`). Defaults to the last 24 hours.","parameters":[{"name":"project_id","in":"query","required":false,"schema":{"type":"string"},"description":"Only this project. An org-wide key covers every project by default."},{"name":"assistant_id","in":"query","required":false,"style":"form","explode":true,"schema":{"type":"array","items":{"type":"string"}},"description":"Only these assistants. Repeat the parameter or comma-separate the values; a row matching any of them is kept."},{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Default: 24 hours before `to`."},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Default: now. Ranges over 90 days are rejected."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ChatHealth"}},"required":["data"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"get-chat-analytics-health","security":[{"apiKey":[]}]}},"/api/chat-analytics/incidents":{"get":{"tags":["Chat analytics"],"summary":"List chat health incidents","description":"Problems the health checker opened on its own (for example a site whose sends started failing), newest activity first. An incident resolves itself when the site recovers or goes quiet, or by hand.","parameters":[{"name":"project_id","in":"query","required":false,"schema":{"type":"string"},"description":"Only this project. An org-wide key covers every project by default."},{"name":"assistant_id","in":"query","required":false,"style":"form","explode":true,"schema":{"type":"array","items":{"type":"string"}},"description":"Only these assistants' incidents. Repeat the parameter or comma-separate the values; a row matching any of them is kept."},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["open","resolved","all"],"default":"open"},"description":"Which incidents to list."},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"`next_cursor` from the previous page."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Page size."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ChatIncident"}},"next_cursor":{"type":["string","null"]},"has_more":{"type":"boolean"}},"required":["data","next_cursor","has_more"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"get-chat-analytics-incidents","security":[{"apiKey":[]}]}},"/api/chat-analytics/incidents/{id}/mute":{"post":{"tags":["Chat analytics"],"summary":"Mute an incident","description":"Stops notifications for this incident for 15 minutes to 7 days. The incident stays open and keeps counting.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatIncidentMute"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ChatIncident"}},"required":["data"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Incident id."}],"operationId":"post-chat-analytics-incidents-id-mute","security":[{"apiKey":[]}]}},"/api/chat-analytics/incidents/{id}/resolve":{"post":{"tags":["Chat analytics"],"summary":"Resolve an incident","description":"Closes the incident as `manual`. No request body. 409 `already_resolved` if it is closed already; a new failure opens a new incident.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ChatIncident"}},"required":["data"]}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Incident id."}],"operationId":"post-chat-analytics-incidents-id-resolve","security":[{"apiKey":[]}]}},"/api/chat-analytics/incidents/{id}/unmute":{"post":{"tags":["Chat analytics"],"summary":"Unmute an incident","description":"Resumes notifications for a muted incident. No request body.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ChatIncident"}},"required":["data"]}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Incident id."}],"operationId":"post-chat-analytics-incidents-id-unmute","security":[{"apiKey":[]}]}},"/api/chat-analytics/outcomes/backfill":{"post":{"tags":["Chat analytics"],"summary":"Label how already analysed chats ended (staff only)","description":"Queues ended, counted web chats in the window whose analysis has no outcome label for an outcome-only run. It sends nothing to any CRM or webhook. At most 500 per call; call again while `remaining` is true.","parameters":[{"name":"project_id","in":"query","required":false,"schema":{"type":"string"},"description":"Only this project. An org-wide key covers every project by default."},{"name":"assistant_id","in":"query","required":false,"style":"form","explode":true,"schema":{"type":"array","items":{"type":"string"}},"description":"Only these assistants. Repeat the parameter or comma-separate the values; a row matching any of them is kept."},{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Default: 24 hours before `to`."},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Default: now. Ranges over 90 days are rejected."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ChatOutcomesBackfill"}},"required":["data"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"post-chat-analytics-outcomes-backfill","security":[{"apiKey":[]}]}},"/api/chat-analytics/quality":{"get":{"tags":["Chat analytics"],"summary":"Get chat quality","description":"How well the chat is doing for visitors: reply and first-token time, missing replies, handoffs answered, how complete leads are, cost per lead, the assistant lines visitors most often stop after, and `improvement` (reply feedback, conversation ratings, containment, unanswered knowledge questions, unhelpful answers and comments, with emails, phone numbers and common name patterns masked).","parameters":[{"name":"project_id","in":"query","required":false,"schema":{"type":"string"},"description":"Only this project. An org-wide key covers every project by default."},{"name":"assistant_id","in":"query","required":false,"style":"form","explode":true,"schema":{"type":"array","items":{"type":"string"}},"description":"Only these assistants. Repeat the parameter or comma-separate the values; a row matching any of them is kept."},{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Default: 24 hours before `to`."},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Default: now. Ranges over 90 days are rejected."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ChatQuality"}},"required":["data"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"get-chat-analytics-quality","security":[{"apiKey":[]}]}},"/api/chat-analytics/subscriptions":{"get":{"tags":["Chat analytics"],"summary":"List notification subscriptions","description":"Where health alerts and the digest are sent: Slack incoming webhooks or your own HTTPS endpoint. Needs the admin role, which API keys have.","parameters":[{"name":"project_id","in":"query","required":false,"schema":{"type":"string"},"description":"Only this project. An org-wide key covers every project by default."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ChatSubscription"}}},"required":["data"]}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"get-chat-analytics-subscriptions","security":[{"apiKey":[]}]},"post":{"tags":["Chat analytics"],"summary":"Create a notification subscription","description":"Sends `health` alerts (incidents opening and resolving) or a `digest` summary to a Slack incoming webhook or an HTTPS URL. An org-wide key must name `project_id`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatSubscriptionCreate"}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ChatSubscription"}},"required":["data"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"post-chat-analytics-subscriptions","security":[{"apiKey":[]}]}},"/api/chat-analytics/subscriptions/{id}":{"patch":{"tags":["Chat analytics"],"summary":"Update a notification subscription","description":"Turn a subscription on or off, or change where it sends or its time zone. Send at least one field.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatSubscriptionPatch"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ChatSubscription"}},"required":["data"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Subscription id."}],"operationId":"patch-chat-analytics-subscriptions-id","security":[{"apiKey":[]}]},"delete":{"tags":["Chat analytics"],"summary":"Delete a notification subscription","description":"Stops the subscription for good. Answers 204 with no body.","responses":{"204":{"description":"Deleted"},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Subscription id."}],"operationId":"delete-chat-analytics-subscriptions-id","security":[{"apiKey":[]}]}},"/api/chat-analytics/trend":{"get":{"tags":["Chat analytics"],"summary":"Get daily or weekly chat totals","description":"Funnel totals per day or per ISO week (weeks start on Monday), for charts over a longer range than the live views.","parameters":[{"name":"project_id","in":"query","required":false,"schema":{"type":"string"},"description":"Only this project. An org-wide key covers every project by default."},{"name":"assistant_id","in":"query","required":false,"style":"form","explode":true,"schema":{"type":"array","items":{"type":"string"}},"description":"Only these assistants. Repeat the parameter or comma-separate the values; a row matching any of them is kept."},{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Default: 90 days before `to`."},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date-time"},"description":"Default: now. Up to 400 days."},{"name":"interval","in":"query","required":false,"schema":{"type":"string","enum":["day","week"],"default":"day"},"description":"One point per day or per week."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ChatTrend"}},"required":["data"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"get-chat-analytics-trend","security":[{"apiKey":[]}]}},"/api/alerts":{"get":{"tags":["Alerts"],"summary":"List alerts","description":"Alert rules in scope, with their thresholds and where they notify.","parameters":[{"name":"project_id","in":"query","required":false,"schema":{"type":"string"},"description":"Only this project's alerts."}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Alert"}}},"required":["data"]}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"get-alerts","security":[{"apiKey":[]}]},"post":{"tags":["Alerts"],"summary":"Create an alert","description":"A rule that watches one call metric (count, total cost, average latency or error rate) over a rolling window and posts to a Slack incoming webhook or your HTTPS URL when it crosses the threshold, and again when it recovers. It does not repeat while it stays breached. An org-wide key must name `project_id`.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlertCreate"}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Alert"}},"required":["data"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"post-alerts","security":[{"apiKey":[]}]}},"/api/alerts/{id}":{"get":{"tags":["Alerts"],"summary":"Get an alert","description":"One alert rule.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Alert"}},"required":["data"]}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Alert id."}],"operationId":"get-alerts-id","security":[{"apiKey":[]}]},"patch":{"tags":["Alerts"],"summary":"Update an alert","description":"Change any field of the rule, or turn it off with `enabled: false`. Send at least one field.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlertPatch"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Alert"}},"required":["data"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Alert id."}],"operationId":"patch-alerts-id","security":[{"apiKey":[]}]},"delete":{"tags":["Alerts"],"summary":"Delete an alert","description":"Removes the rule; it stops notifying at once.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/DeleteResult"}},"required":["data"]}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Alert id."}],"operationId":"delete-alerts-id","security":[{"apiKey":[]}]}},"/api/audit-log":{"get":{"tags":["Audit log"],"summary":"List audit log entries","description":"Who changed what (assistants, keys, numbers, knowledge, takeovers and more), newest first, one cursor page at a time. A project-scoped key sees only that project's entries.","parameters":[{"name":"limit","in":"query","required":false,"description":"Page size.","schema":{"type":"integer","minimum":1,"maximum":200,"default":50}},{"name":"cursor","in":"query","required":false,"description":"`next_cursor` from the previous page.","schema":{"type":"string","description":"Opaque next_cursor from the previous page. Legacy ISO timestamp cursors are also accepted."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AuditEntry"}},"next_cursor":{"type":["string","null"],"description":"Opaque cursor for the next page."},"has_more":{"type":"boolean"}},"required":["data","next_cursor","has_more"]}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"get-audit-log","security":[{"apiKey":[]}]}},"/api/projects":{"get":{"tags":["Projects"],"summary":"List projects","description":"Projects in the organization. A project-scoped key sees only its own project.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Project"}}},"required":["data"]}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"get-projects","security":[{"apiKey":[]}]},"post":{"tags":["Projects"],"summary":"Create a project","description":"A new project to hold assistants, numbers, keys and webhooks. Needs an org-wide key: a project-scoped key gets 403 `invalid_request` (\"A project-scoped key cannot create projects.\").","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectCreate"}}}},"responses":{"201":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Project"}},"required":["data"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"post-projects","security":[{"apiKey":[]}]}},"/api/projects/{id}":{"get":{"tags":["Projects"],"summary":"Get a project","description":"One project, with whether its agent webhook secret is set.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Project"}},"required":["data"]}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Project id, `proj_...`."}],"operationId":"get-projects-id","security":[{"apiKey":[]}]},"patch":{"tags":["Projects"],"summary":"Update a project","description":"webhook_secret is write-only: it is never returned by any endpoint, only reflected as webhook_secret_set. Send null to clear it; the project's next call mints a new one.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectPatch"}}}},"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Project"}},"required":["data"]}}}},"400":{"description":"Validation error: usually `invalid_request`, with the failing fields in `issues` when there are any","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Refused, for example a read-only key on a write (`read_only_key`), a role too low for the route (`insufficient_role`), or a route that needs a console session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Not found, or outside what the key can see","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Project id, `proj_...`."}],"operationId":"patch-projects-id","security":[{"apiKey":[]}]}},"/api/members":{"get":{"tags":["Members"],"summary":"List organization members","description":"The whole organization roster with roles, without pagination. Use a member's `user_id` to grant them access to a knowledge base. Needs an org-wide key: a project-scoped key gets 403 `project_key_not_allowed`. Read-only keys are allowed.","responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/OrgMember"}}},"required":["data"]}}}},"401":{"description":"Missing, malformed, unknown or revoked API key (`unauthorized`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"`project_key_not_allowed`: this route needs an org-wide API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded (`rate_limited`). Wait `Retry-After` seconds. Budgets are per 60-second window: developer API 120 per key and 600 per organization; public chat 30 per IP address, except polling for updates (60 per conversation and IP address) and analytics events (120 per IP address).","headers":{"Retry-After":{"description":"Seconds until the window resets.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed in the current 60-second window.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"X-RateLimit-Reset":{"description":"When the window resets, in Unix seconds.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"operationId":"get-members","security":[{"apiKey":[]}]}}},"components":{"schemas":{"AddLibraryVoiceRequest":{"type":"object","properties":{"public_owner_id":{"type":"string"},"voice_id":{"type":"string"},"name":{"type":"string"}},"required":["public_owner_id","voice_id","name"]},"AddLibraryVoiceResponse":{"type":"object","properties":{"id":{"type":"string"}},"required":["id"]},"AgentIdentityInput":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string","format":"email"},"name":{"type":"string"}}},"Alert":{"type":"object","properties":{"id":{"type":"string"},"org_id":{"type":"string"},"project_id":{"type":"string"},"name":{"type":"string"},"metric":{"type":"string","enum":["call_count","cost_total","avg_latency_ms","error_rate"]},"comparator":{"type":"string","enum":["gt","gte","lt","lte"]},"threshold":{"type":"string","description":"Decimal serialized as a string (numeric column)."},"window_seconds":{"type":"string","description":"Integer serialized as a string (numeric column)."},"channel":{"$ref":"#/components/schemas/AlertChannel"},"enabled":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","org_id","project_id","name","metric","comparator","threshold","window_seconds","channel","enabled"]},"AlertChannel":{"type":"object","properties":{"type":{"type":"string","enum":["slack","webhook"]},"url":{"type":"string","format":"uri","pattern":"^https://","description":"Public HTTPS destination. Private addresses and redirects are refused."}},"required":["type","url"]},"AlertCreate":{"type":"object","properties":{"project_id":{"type":"string","description":"Required for an org-wide key."},"name":{"type":"string","minLength":1},"metric":{"type":"string","enum":["call_count","cost_total","avg_latency_ms","error_rate"]},"comparator":{"type":"string","enum":["gt","gte","lt","lte"],"description":"How the metric compares with the threshold to fire."},"threshold":{"type":"number"},"window_seconds":{"type":"integer","minimum":1,"description":"The rolling window the metric is measured over."},"channel":{"$ref":"#/components/schemas/AlertChannel"},"enabled":{"type":"boolean","description":"Default true."}},"required":["name","metric","comparator","threshold","window_seconds","channel"]},"AlertPatch":{"type":"object","minProperties":1,"properties":{"name":{"type":"string","minLength":1},"metric":{"type":"string","enum":["call_count","cost_total","avg_latency_ms","error_rate"]},"comparator":{"type":"string","enum":["gt","gte","lt","lte"]},"threshold":{"type":"number"},"window_seconds":{"type":"integer","minimum":1},"channel":{"$ref":"#/components/schemas/AlertChannel"},"enabled":{"type":"boolean"}}},"AnalyticsCount":{"type":"object","properties":{"count":{"type":"integer"}},"required":["count"]},"AnalyticsLatency":{"type":"object","properties":{"e2e":{"$ref":"#/components/schemas/AnalyticsPercentiles"},"stages":{"type":"array","items":{"allOf":[{"$ref":"#/components/schemas/AnalyticsPercentiles"},{"type":"object","properties":{"stage":{"type":"string"}},"required":["stage"]}]}},"slowest":{"type":"array","maxItems":10,"items":{"type":"object","properties":{"call_id":{"type":"string"},"e2e_p95":{"type":"number"},"assistant_id":{"type":"string"},"started_at":{"type":["string","null"],"format":"date-time"}},"required":["call_id","e2e_p95","assistant_id","started_at"]}}},"required":["e2e","stages","slowest"]},"AnalyticsPercentiles":{"type":"object","properties":{"p50":{"type":["number","null"]},"p95":{"type":["number","null"]}},"required":["p50","p95"]},"AnalyticsVolume":{"type":"object","properties":{"total_calls":{"type":"integer"},"total_minutes":{"type":"number"},"completed":{"type":"integer"},"failed":{"type":"integer"},"by_status":{"type":"array","items":{"allOf":[{"$ref":"#/components/schemas/AnalyticsCount"},{"type":"object","properties":{"status":{"type":"string"}},"required":["status"]}]}},"by_call_type":{"type":"array","items":{"allOf":[{"$ref":"#/components/schemas/AnalyticsCount"},{"type":"object","properties":{"call_type":{"type":"string"}},"required":["call_type"]}]}},"by_canonical_ended_reason":{"type":"array","items":{"allOf":[{"$ref":"#/components/schemas/AnalyticsCount"},{"type":"object","properties":{"ended_reason":{"type":"string"}},"required":["ended_reason"]}]}},"daily":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string","format":"date"},"calls":{"type":"integer"},"minutes":{"type":"number"}},"required":["date","calls","minutes"]}}},"required":["total_calls","total_minutes","completed","failed","by_status","by_call_type","by_canonical_ended_reason","daily"]},"Assistant":{"type":"object","properties":{"id":{"type":"string"},"org_id":{"type":"string"},"project_id":{"type":"string"},"name":{"type":"string"},"config":{"$ref":"#/components/schemas/AssistantConfig"},"version":{"type":"integer"},"draft_revision":{"type":"integer","description":"The revision a draft save names as expected_draft_revision; 0 before the first draft."},"draft":{"type":["object","null"],"description":"Whether a draft exists, without its config (GET /assistants/{id}/draft has it).","properties":{"revision":{"type":"integer"},"base_version":{"type":"integer"},"updated_at":{"type":"string","format":"date-time"}}},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","org_id","project_id","name","config","version"]},"AssistantAvatar":{"type":"object","properties":{"url":{"type":"string","format":"uri"}},"required":["url"]},"AssistantAvatarUpload":{"type":"object","properties":{"content_type":{"type":"string","enum":["image/png","image/jpeg","image/webp"]},"base64":{"type":"string","description":"The file's bytes, base64-encoded."}},"required":["content_type","base64"]},"AssistantConfig":{"type":"object","description":"The runtime profile minus per-call fields. agent.instructions and either session.realtime or the complete session.{llm,stt,tts} pipeline are required. Realtime requires provider; its model may use the runtime default. A pipeline LLM may omit provider for a LiveKit-hosted model id. Credentials are write-only: integrations.google_analytics.api_secret, channels.web_chat.analytics.ga4.api_secret and recording.s3.{access_key,secret,session_token} come back as `<key>_set: boolean`, and a write that omits one keeps the stored value. Chat (web or WhatsApp) answers with channels.web_chat.llm, defaulting independently to openai/gpt-6.1-sol with medium reasoning; creating or enabling web chat, or changing that model while web chat or WhatsApp routing is live, is refused with 400 when chat has no adapter for its provider.","properties":{"agent":{"type":"object","properties":{"instructions":{"type":"string"}},"required":["instructions"]},"tools":{"$ref":"#/components/schemas/AssistantTools"},"session":{"type":"object","properties":{"llm":{"type":"object","properties":{"provider":{"type":"string","minLength":1},"model":{"type":"string","minLength":1}},"required":["model"],"additionalProperties":true},"realtime":{"type":"object","properties":{"provider":{"type":"string","minLength":1},"model":{"type":"string","minLength":1}},"required":["provider"],"additionalProperties":true},"stt":{"$ref":"#/components/schemas/ProviderModel"},"tts":{"$ref":"#/components/schemas/ProviderModel"},"tools":{"$ref":"#/components/schemas/AssistantTools"}},"anyOf":[{"required":["realtime"]},{"required":["llm","stt","tts"]}],"additionalProperties":true},"variables":{"type":"array","maxItems":50,"items":{"$ref":"#/components/schemas/AssistantVariableDef"},"description":"Authoring-time schema for {{key}} placeholders in agent.instructions, agent.context, agent.first_message, and agent.first_message_instructions; never delivered to the runtime."},"knowledge":{"type":"object","maxProperties":50,"additionalProperties":{"type":"object","properties":{"chat":{"type":"boolean","description":"Search it in web chat and WhatsApp."},"voice":{"type":"boolean","description":"Search it on calls. Needs a pipeline session, not realtime."},"priority":{"type":"integer","minimum":0,"maximum":100,"description":"Each search reaches up to four of the attached bases that have a live publication, highest priority first (then by id), and searches them in parallel."}},"required":["chat","voice","priority"],"additionalProperties":false},"description":"The knowledge bases the assistant answers from, by knowledge base id (`kb_...`). Unlike the rest of the config, leaving it out of a write keeps the stored selection; send `{}` to detach every base. An entry with both `chat` and `voice` false counts as detached. Changing it needs an org-wide key and editor access to each base: 403 `organization_knowledge_access_required` for a project key or `knowledge_forbidden` without editor access, 400 `knowledge_base_not_found` for an unknown id, 409 `knowledge_voice_requires_pipeline_assistant` for voice on a realtime session. Each refusal names the base in `knowledge_base_id`."}},"required":["agent","session"],"additionalProperties":true},"AssistantCreate":{"type":"object","properties":{"project_id":{"type":"string","description":"Required for an org-wide key; ignored (must match) for a project-scoped key."},"name":{"type":"string","minLength":1},"config":{"$ref":"#/components/schemas/AssistantConfig"}},"required":["name","config"]},"AssistantDraft":{"type":["object","null"],"properties":{"assistant_id":{"type":"string"},"config":{"$ref":"#/components/schemas/AssistantConfig"},"hidden_changes":{"type":"array","items":{"type":"string"},"description":"Paths that differ from live but read back the same: credentials and tool request specs. Values are never returned."},"revision":{"type":"integer"},"base_version":{"type":"integer","description":"The live version the draft started from."},"live_version":{"type":"integer"},"stale":{"type":"boolean","description":"Live was edited after the draft started; publishing is refused."},"updated_at":{"type":"string","format":"date-time"}}},"AssistantDraftRebase":{"type":"object","properties":{"expected_draft_revision":{"type":"integer","minimum":0},"keep_draft":{"type":"boolean","description":"Keep the draft's value where live changed the same setting differently."}}},"AssistantDraftSave":{"type":"object","properties":{"config":{"$ref":"#/components/schemas/AssistantConfig"},"expected_draft_revision":{"type":"integer","minimum":0,"description":"The draft_revision the caller read. A save over a newer draft is refused with 409 draft_conflict."},"expected_version":{"type":"integer","minimum":1,"description":"The live version the caller edited from. A save that starts a new draft is refused with 409 live_changed when live has moved on since."}},"required":["config"]},"AssistantPatch":{"type":"object","description":"At least one of `name` and `config`.","minProperties":1,"properties":{"name":{"type":"string","minLength":1},"config":{"$ref":"#/components/schemas/AssistantConfig"},"expected_version":{"type":"integer","minimum":1,"description":"The version the caller read. A save over a newer version is refused with 409 version_conflict and the current_version."}}},"AssistantPublish":{"type":"object","properties":{"expected_version":{"type":"integer","minimum":1},"expected_draft_revision":{"type":"integer","minimum":0},"note":{"type":"string","maxLength":500}}},"AssistantRestore":{"type":"object","properties":{"target":{"type":"string","enum":["draft","live"],"default":"draft"},"expected_version":{"type":"integer","minimum":1},"expected_draft_revision":{"type":"integer","minimum":0}}},"AssistantToolDefinition":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"parameters":{"type":"object","additionalProperties":true,"description":"JSON Schema for the arguments, as exposed to the model."},"enabled":{"type":"boolean","default":true,"description":"False keeps the tool saved, with its request, but offers it on no channel."},"channels":{"type":"array","items":{"type":"string","enum":["text","voice"]},"minItems":1,"uniqueItems":true,"description":"Where the tool is offered: text is web chat and WhatsApp, voice is every call including browser test calls. Absent means both."},"read_only":{"type":"boolean","default":false,"description":"True when the tool only looks things up. Test and preview chats call only read-only tools; any other tool answers that requests are off in a test chat."},"request":{"type":"object","writeOnly":true,"description":"Server-side HTTP execution spec, never returned on reads. A write that omits it keeps the stored spec for the same tool name; removing or renaming the tool drops it.","properties":{"url":{"type":"string","format":"uri"},"method":{"type":"string","enum":["GET","POST","PUT","PATCH","DELETE"]},"headers":{"type":"object","additionalProperties":{"type":"string"}},"timeout_ms":{"type":"integer","minimum":1,"maximum":120000}},"required":["url"]}},"required":["name"],"additionalProperties":true},"AssistantTools":{"type":"object","description":"Custom tools. config.tools is the canonical home; the legacy session.tools is read only when config.tools is absent.","properties":{"definitions":{"type":"array","items":{"$ref":"#/components/schemas/AssistantToolDefinition"}}},"additionalProperties":true},"AssistantVariableDef":{"type":"object","properties":{"key":{"type":"string","pattern":"^[A-Za-z_][A-Za-z0-9_.-]*$","maxLength":64},"label":{"type":"string","maxLength":120},"description":{"type":"string","maxLength":500},"default":{"type":"string","maxLength":2000}},"required":["key"],"additionalProperties":true},"AssistantVersion":{"type":"object","properties":{"assistant_id":{"type":"string"},"version":{"type":"integer"},"name":{"type":"string"},"source":{"type":"string","enum":["create","api_edit","publish","restore","bulk_update","seed"]},"created_by":{"type":["string","null"]},"note":{"type":["string","null"]},"created_at":{"type":"string","format":"date-time"},"config":{"$ref":"#/components/schemas/AssistantConfig"}},"required":["assistant_id","version","source"]},"AuditEntry":{"type":"object","properties":{"id":{"type":"string"},"org_id":{"type":"string"},"project_id":{"type":["string","null"]},"actor_type":{"type":"string","enum":["user","api_key"]},"actor_id":{"type":"string"},"actor_label":{"type":["string","null"]},"action":{"type":"string"},"resource_type":{"type":"string"},"resource_id":{"type":["string","null"]},"metadata":{"type":["object","null"],"additionalProperties":true},"created_at":{"type":"string","format":"date-time"}},"required":["id","org_id","project_id","actor_type","actor_id","actor_label","action","resource_type","resource_id","metadata","created_at"]},"AvailablePhoneNumber":{"type":"object","properties":{"phoneNumber":{"type":"string"},"friendlyName":{"type":"string"},"countryCode":{"type":"string"},"capabilities":{"type":"object","additionalProperties":{"type":"boolean"}}},"required":["phoneNumber"]},"Call":{"type":"object","properties":{"id":{"type":"string"},"org_id":{"type":"string"},"project_id":{"type":"string"},"assistant_id":{"type":"string"},"call_type":{"type":"string","enum":["inbound","outbound","web"]},"status":{"type":"string","enum":["ringing","in_progress","completed","failed","no_answer","busy","cancelled"]},"mode":{"type":"string","enum":["live","test"],"description":"Set when the call is created and never changed. A test call ran the assistant's draft with test restrictions (read-only declared tools run server-side, no transfers), its webhooks carry livemode: false and reach test subscriptions only, and it stays out of analytics and alerts."},"customer_number":{"type":["string","null"]},"agent_number":{"type":["string","null"]},"room_name":{"type":["string","null"],"description":"Admins, owners, API keys and staff only."},"ended_reason":{"type":["string","null"]},"ended_reason_canonical":{"type":["string","null"],"description":"Closed-enum normalization of ended_reason."},"started_at":{"type":["string","null"],"format":"date-time"},"connected_at":{"type":["string","null"],"format":"date-time"},"ended_at":{"type":["string","null"],"format":"date-time"},"duration_ms":{"type":["integer","null"]},"recording_url":{"type":["string","null"]},"transfer_outcome":{"type":["string","null"],"description":"Normalized warm-transfer outcome when the call involved a transfer."},"transcript":{"type":["array","null"],"items":{"$ref":"#/components/schemas/CallTranscriptMessage"},"description":"Null until the call has ended. Members and viewers get user and assistant turns only, with tool calls reduced to id and name."},"voice_provider":{"type":"string","enum":["livekit","vapi"],"description":"Every caller. Live controls (hangup, listen) apply to livekit calls only."},"agent_configuration":{"type":["object","null"],"additionalProperties":true,"description":"Prompt snapshot. Admins, owners, API keys and staff only."},"provider_metadata":{"type":["object","null"],"additionalProperties":true,"description":"Admins, owners, API keys and staff only."},"metadata":{"type":["object","null"],"additionalProperties":true,"description":"Admins, owners, API keys and staff only."},"variables":{"type":["object","null"],"additionalProperties":{"type":"string"},"description":"Admins, owners, API keys and staff only."},"unresolved_variables":{"type":["array","null"],"items":{"type":"string"},"description":"Admins, owners, API keys and staff only."},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","org_id","project_id","assistant_id","call_type","status","mode","voice_provider"]},"CallCost":{"type":"object","properties":{"id":{"type":"string"},"category":{"type":"string"},"provider":{"type":["string","null"]},"model":{"type":["string","null"]},"unit":{"type":"string"},"quantity":{"type":"string"},"unit_price":{"type":["string","null"],"description":"Staff only."},"amount":{"type":["string","null"],"description":"Staff only."},"currency":{"type":"string","description":"Staff only."},"plan_tier":{"type":["string","null"],"enum":["build","ship","scale",null],"description":"Staff only."}},"required":["id","category","unit","quantity"]},"CallDetail":{"allOf":[{"$ref":"#/components/schemas/Call"},{"type":"object","properties":{"costs":{"type":"array","items":{"$ref":"#/components/schemas/CallCost"},"description":"Admins, owners, API keys and staff only; amounts and prices for staff only."},"recordings":{"type":"array","items":{"$ref":"#/components/schemas/CallRecording"}},"performance":{"oneOf":[{"$ref":"#/components/schemas/CallPerformance"},{"type":"null"}],"description":"Admins, owners, API keys and staff only."},"transcript":{"type":["array","null"],"items":{"$ref":"#/components/schemas/CallTranscriptMessage"}},"analysis":{"type":["object","null"],"description":"Post-call analysis when the assistant has it enabled.","properties":{"summary":{"type":["string","null"]},"structured":{"type":["object","null"],"additionalProperties":true}}},"events":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEvent"},"maxItems":200,"description":"Admins, owners, API keys and staff only."},"events_truncated":{"type":"boolean","description":"True when more than 200 webhook events exist for this call. Admins, owners, API keys and staff only."}},"required":["recordings","transcript"]}]},"CallHangupResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"hangup_requested":{"type":"boolean"}},"required":["id","hangup_requested"]}},"required":["data"]},"CallListenResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"token":{"type":"string"},"url":{"type":"string"},"room":{"type":"string"}},"required":["token","url","room"]}},"required":["data"]},"CallPerformance":{"type":"object","properties":{"id":{"type":"string"},"metrics":{"type":"object"},"e2e_p50_ms":{"type":["string","null"]},"e2e_p95_ms":{"type":["string","null"]},"e2e_max_ms":{"type":["string","null"]},"num_interruptions":{"type":["integer","null"]},"total_turns":{"type":["integer","null"]},"latency_summary":{"type":["object","null"],"additionalProperties":{"type":"object","properties":{"count":{"type":"integer"},"min":{"type":"number"},"mean":{"type":"number"},"p50":{"type":"number"},"p95":{"type":"number"},"p99":{"type":"number"},"max":{"type":"number"}},"required":["count","min","mean","p50","p95","p99","max"]}}},"required":["id","metrics"]},"CallRecording":{"type":"object","description":"Private S3 recording metadata. Storage coordinates and access URLs are not included.","properties":{"id":{"type":"string"},"leg_type":{"type":"string","enum":["main","consult","post_transfer"]},"attempt_id":{"type":["string","null"]},"status":{"type":"string","enum":["pending","recording","processing","ready","failed","deleted"]},"content_type":{"type":["string","null"]},"size_bytes":{"type":["integer","null"],"minimum":0},"duration_ms":{"type":["integer","null"],"minimum":0},"channels":{"type":["integer","null"],"minimum":1},"started_at":{"type":["string","null"],"format":"date-time","description":"Audio timeline origin for transcript alignment."},"ended_at":{"type":["string","null"],"format":"date-time"},"ready_at":{"type":["string","null"],"format":"date-time"},"deleted_at":{"type":["string","null"],"format":"date-time"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","leg_type","attempt_id","status","content_type","size_bytes","duration_ms","channels","started_at","ended_at","ready_at","deleted_at","created_at","updated_at"]},"CallTranscriptMessage":{"type":"object","description":"One turn of a call transcript, the same shape as `messages` in the `call.ended` webhook.","properties":{"role":{"type":"string","enum":["user","assistant","tool"]},"content":{"type":"string"},"tool_call_id":{"type":"string","description":"Tool-result turns: the tool call this answers."},"tool_calls":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"arguments":{"type":"object","additionalProperties":true}},"required":["id","name"]}},"occurred_at":{"type":"string","format":"date-time"},"seconds_from_start":{"type":["number","null"],"description":"Offset from the recording start (or from when the call connected), for lining the transcript up with audio."},"duration_ms":{"type":["number","null"],"description":"How long the turn was spoken, in milliseconds (can be fractional)."},"metadata":{"type":"object","additionalProperties":true}},"required":["role","content"],"additionalProperties":true},"ChatAttribution":{"type":"object","description":"Where a chat came from, stored as conversation metadata.attribution. Only chat_page is kept without analytics consent.","properties":{"chat_page":{"type":"string","description":"The path the visitor started chatting on."},"visit":{"$ref":"#/components/schemas/ChatAttributionTouch","description":"This visit's landing."},"first":{"allOf":[{"$ref":"#/components/schemas/ChatAttributionTouch"},{"type":"object","properties":{"at":{"type":"string","format":"date-time","description":"When the visitor first landed."}}}],"description":"The visitor's first-ever landing."}}},"ChatAttributionTouch":{"type":"object","description":"One landing. Every field is optional; strings are trimmed and capped at 200 characters.","properties":{"landing_page":{"type":"string","description":"A path starting with /, without query or fragment."},"referrer_host":{"type":"string"},"utm_source":{"type":"string"},"utm_medium":{"type":"string"},"utm_campaign":{"type":"string"},"utm_term":{"type":"string"},"utm_content":{"type":"string"},"gclid":{"type":"string"},"gbraid":{"type":"string"},"wbraid":{"type":"string"},"fbclid":{"type":"string"},"msclkid":{"type":"string"}}},"ChatFailure":{"type":"object","properties":{"id":{"type":"string"},"occurred_at":{"type":"string","format":"date-time"},"route":{"type":"string","enum":["config","events","create","message"]},"outcome":{"type":"string"},"status_code":{"type":"integer"},"site":{"type":["string","null"]},"sampled":{"type":"boolean"},"request_id":{"type":["string","null"]},"conversation_id":{"type":["string","null"]},"client_message_id":{"type":["string","null"]},"device":{"type":["string","null"]},"browser":{"type":["string","null"]},"widget_version":{"type":["string","null"]},"assistant_id":{"type":["string","null"]}},"required":["id","occurred_at","route","outcome","status_code"]},"ChatFunnel":{"type":"object","properties":{"from":{"type":"string","format":"date-time"},"to":{"type":"string","format":"date-time"},"group_by":{"type":"string"},"visits_since":{"type":["string","null"],"format":"date-time","description":"When this scope's first page view tied to a visit arrived; visit counts start here. Null before any did."},"page_views_since":{"type":["string","null"],"format":"date-time","description":"When this scope's first page view the widget reported arrived; page-view stages start here. Null before any did."},"groups":{"type":"array","items":{"allOf":[{"type":"object","properties":{"page_views":{"type":"object","description":"Distinct page views per stage. Events without a page_view_id are excluded.","properties":{"loaded":{"type":"integer"},"shown":{"type":"integer"},"opened":{"type":"integer"},"typed":{"type":"integer"},"sent":{"type":"integer"}},"required":["loaded","shown","opened","typed","sent"]},"visits":{"type":"object","description":"Distinct visits (a run of page views by one browser) per stage, with the same filters as page_views. Only events that carry a visit_id count: visits exist only with analytics consent and a current widget.","properties":{"loaded":{"type":"integer"},"shown":{"type":"integer"},"opened":{"type":"integer"},"typed":{"type":"integer"},"sent":{"type":"integer"}},"required":["loaded","shown","opened","typed","sent"]},"visitors":{"type":"integer","description":"Distinct visitor ids among widget_loaded events."},"returning_visits":{"type":"integer","description":"Distinct visits among widget_loaded events whose visit_number is above 1."},"identified_page_views":{"type":"integer","description":"Distinct page views among widget_loaded events that carry a visit_id. Divide by tracked_page_views for coverage."},"tracked_page_views":{"type":"integer","description":"Distinct page views since visit tracking started for this scope (the response's visits_since); 0 before it started. The coverage denominator, so earlier, untracked weeks do not count as untracked traffic."},"sends":{"type":["object","null"],"properties":{"pressed":{"type":"integer"},"accepted":{"type":"integer"},"answered":{"type":"integer"},"failed":{"type":"integer"},"refused":{"type":"integer"}}},"conversations":{"type":["integer","null"],"description":"Chats with a visitor message started in the window, in every grouping: a chat takes the page, source and device of the page view its first message was sent from, or the group `untracked` when there is none. Test, preview-host and internal chats are left out (see excluded)."},"leads":{"type":["integer","null"],"description":"Leads saved in the window, grouped as their chat is."},"valid_leads":{"type":"integer","description":"Leads with at least one email or phone that is not a placeholder or the business's own number."},"handoffs":{"type":["integer","null"],"description":"Handoffs requested in the window, grouped as their chat is."},"booked":{"type":["integer","null"]},"converted":{"type":"object","description":"The conversion cohort: page views loaded in the window from which a chat started, and those whose chat saved a lead or a valid lead in the window. Divide by page_views.loaded for conversion; unlike the activity counts above, it leaves out chats with no page view or from before page tracking.","properties":{"conversations":{"type":"integer"},"leads":{"type":"integer"},"valid_leads":{"type":"integer"}},"required":["conversations","leads","valid_leads"]}},"required":["page_views","visits","visitors","returning_visits","identified_page_views","sends","conversations","leads","handoffs","booked"]},{"type":"object","properties":{"key":{"type":["string","null"]},"previous":{"type":"object","properties":{"page_views":{"type":"object","description":"Distinct page views per stage. Events without a page_view_id are excluded.","properties":{"loaded":{"type":"integer"},"shown":{"type":"integer"},"opened":{"type":"integer"},"typed":{"type":"integer"},"sent":{"type":"integer"}},"required":["loaded","shown","opened","typed","sent"]},"visits":{"type":"object","description":"Distinct visits (a run of page views by one browser) per stage, with the same filters as page_views. Only events that carry a visit_id count: visits exist only with analytics consent and a current widget.","properties":{"loaded":{"type":"integer"},"shown":{"type":"integer"},"opened":{"type":"integer"},"typed":{"type":"integer"},"sent":{"type":"integer"}},"required":["loaded","shown","opened","typed","sent"]},"visitors":{"type":"integer","description":"Distinct visitor ids among widget_loaded events."},"returning_visits":{"type":"integer","description":"Distinct visits among widget_loaded events whose visit_number is above 1."},"identified_page_views":{"type":"integer","description":"Distinct page views among widget_loaded events that carry a visit_id. Divide by tracked_page_views for coverage."},"tracked_page_views":{"type":"integer","description":"Distinct page views since visit tracking started for this scope (the response's visits_since); 0 before it started. The coverage denominator, so earlier, untracked weeks do not count as untracked traffic."},"sends":{"type":["object","null"],"properties":{"pressed":{"type":"integer"},"accepted":{"type":"integer"},"answered":{"type":"integer"},"failed":{"type":"integer"},"refused":{"type":"integer"}}},"conversations":{"type":["integer","null"],"description":"Chats with a visitor message started in the window, in every grouping: a chat takes the page, source and device of the page view its first message was sent from, or the group `untracked` when there is none. Test, preview-host and internal chats are left out (see excluded)."},"leads":{"type":["integer","null"],"description":"Leads saved in the window, grouped as their chat is."},"valid_leads":{"type":"integer","description":"Leads with at least one email or phone that is not a placeholder or the business's own number."},"handoffs":{"type":["integer","null"],"description":"Handoffs requested in the window, grouped as their chat is."},"booked":{"type":["integer","null"]},"converted":{"type":"object","description":"The conversion cohort: page views loaded in the window from which a chat started, and those whose chat saved a lead or a valid lead in the window. Divide by page_views.loaded for conversion; unlike the activity counts above, it leaves out chats with no page view or from before page tracking.","properties":{"conversations":{"type":"integer"},"leads":{"type":"integer"},"valid_leads":{"type":"integer"}},"required":["conversations","leads","valid_leads"]}},"required":["page_views","visits","visitors","returning_visits","identified_page_views","sends","conversations","leads","handoffs","booked"]}},"required":["key"]}]}},"groups_total":{"type":"integer","description":"Groups found; only the first 50 are returned (hours are never cut)."},"truncated":{"type":"boolean"},"coverage":{"type":"object","description":"Counted chats in the window, and those tied to a page view the widget reported.","properties":{"conversations":{"type":"integer"},"tracked":{"type":"integer"}},"required":["conversations","tracked"]},"excluded":{"type":"object","description":"Chats in the window left out of every number, each under the first reason that applies: a test marker or test key, a preview or staging host, or an internal lead email.","properties":{"test":{"type":"integer"},"preview":{"type":"integer"},"internal":{"type":"integer"}},"required":["test","preview","internal"]},"partial":{"type":"boolean","description":"True when not every send in the window could be read, so the counts are a lower bound. Retry, or narrow the range."}},"required":["from","to","group_by","groups","groups_total","truncated","coverage","excluded","partial"]},"ChatHealth":{"type":"object","properties":{"checker":{"type":"object","properties":{"swept_at":{"type":["string","null"],"format":"date-time"},"stale":{"type":"boolean"}},"required":["swept_at","stale"]},"partial":{"type":"boolean","description":"True when not every send in the window could be read, so the counts are a lower bound. Retry, or narrow the range."},"sites":{"type":"array","items":{"type":"object","properties":{"site":{"type":["string","null"]},"status":{"type":"string","enum":["ok","warning","critical","idle"]},"page_views":{"type":"integer"},"shown":{"type":"integer"},"opened":{"type":"integer"},"sends":{"type":"integer"},"accepted":{"type":"integer"},"failed":{"type":"integer"},"refused":{"type":"integer"},"failure_rate":{"type":["number","null"]},"reply_ms_p50":{"type":["integer","null"]},"reply_ms_p90":{"type":["integer","null"]},"last_success_at":{"type":["string","null"],"format":"date-time"},"open_incidents":{"type":"integer"}},"required":["site","status","page_views","sends","open_incidents"]}}},"required":["checker","sites","partial"]},"ChatImprovement":{"type":"object","description":"What visitors said about the chat, and where it could do better. Quoted text is one line, at most 200 characters, with emails and phone numbers masked, and names masked where they follow common patterns (an introduction such as \"my name is\" or a greeting such as \"Thanks Jane\"). Other personal details can remain.","properties":{"replies":{"type":"object","description":"Thumbs up and down on assistant replies.","properties":{"rated":{"type":"integer"},"up":{"type":"integer"},"down":{"type":"integer"},"reasons":{"type":"object","additionalProperties":{"type":"integer"},"description":"Thumbs-down count per reason."}},"required":["rated","up","down","reasons"]},"chats_rated":{"type":"object","properties":{"count":{"type":"integer"},"average":{"type":["number","null"]},"distribution":{"type":"array","items":{"type":"integer"},"description":"Count of 1 to 5 star ratings, in order."}},"required":["count","average","distribution"]},"containment":{"type":"object","properties":{"chats":{"type":"integer"},"without_person":{"type":"integer","description":"Never asked for a person."},"with_lead":{"type":"integer"},"asked_for_person":{"type":"integer"},"one_message":{"type":"integer","description":"Conversations, open or ended, with exactly one visitor message and no lead."}},"required":["chats","without_person","with_lead","asked_for_person","one_message"]},"knowledge_gaps":{"type":"object","description":"Replies whose knowledge searches all came back empty.","properties":{"total":{"type":"integer"},"recent":{"type":"array","items":{"type":"object","properties":{"question":{"type":"string"},"conversation_id":{"type":"string"},"at":{"type":"string","format":"date-time"}},"required":["question","conversation_id","at"]}}},"required":["total","recent"]},"unhelpful":{"type":"array","items":{"type":"object","properties":{"answer":{"type":"string"},"reason":{"type":["string","null"]},"conversation_id":{"type":"string"},"at":{"type":"string","format":"date-time"}},"required":["answer","reason","conversation_id","at"]},"description":"Recent replies rated down."},"comments":{"type":"array","items":{"type":"object","properties":{"rating":{"type":"integer"},"comment":{"type":"string"},"conversation_id":{"type":"string"},"at":{"type":"string","format":"date-time"}},"required":["rating","comment","conversation_id","at"]},"description":"Recent conversation ratings that came with a comment."}},"required":["replies","chats_rated","containment","knowledge_gaps","unhelpful","comments"]},"ChatIncident":{"type":"object","properties":{"id":{"type":"string"},"org_id":{"type":"string"},"project_id":{"type":"string"},"assistant_id":{"type":["string","null"]},"widget_key_id":{"type":["string","null"]},"site":{"type":["string","null"]},"rule":{"type":"string"},"severity":{"type":"string","enum":["info","warning","critical"]},"status":{"type":"string","enum":["open","resolved"]},"dedupe_key":{"type":"string"},"first_seen_at":{"type":"string","format":"date-time"},"last_seen_at":{"type":"string","format":"date-time"},"occurrences":{"type":"integer"},"example_request_id":{"type":["string","null"]},"example_conversation_id":{"type":["string","null"]},"detail":{"type":["object","null"],"additionalProperties":true},"muted_until":{"type":["string","null"],"format":"date-time"},"muted_by":{"type":["string","null"]},"resolved_at":{"type":["string","null"],"format":"date-time"},"resolution":{"type":["string","null"],"enum":["recovered","quiet","manual",null]},"resolved_by":{"type":["string","null"]},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","project_id","rule","severity","status"]},"ChatIncidentMute":{"type":"object","properties":{"minutes":{"type":"integer","minimum":15,"maximum":10080}},"required":["minutes"]},"ChatNotificationChannel":{"type":"object","properties":{"type":{"type":"string","enum":["slack","webhook"]},"url":{"type":"string","format":"uri","pattern":"^https://"}},"required":["type","url"]},"ChatOutcomesBackfill":{"type":"object","properties":{"queued":{"type":"integer"},"candidates":{"type":"integer"},"remaining":{"type":"boolean","description":"More chats in the window still need a label: call again."}},"required":["queued","candidates","remaining"]},"ChatQuality":{"type":"object","properties":{"reply_ms":{"type":"object","properties":{"buckets":{"type":"array","items":{"type":"object","properties":{"le_ms":{"type":["integer","null"]},"count":{"type":"integer"}},"required":["le_ms","count"]}},"p50":{"type":["integer","null"]},"p90":{"type":["integer","null"]},"p95":{"type":["integer","null"]},"p99":{"type":["integer","null"]},"samples":{"type":"integer","description":"Replies measured."},"slow":{"type":"integer","description":"Replies slower than 15 seconds."}}},"first_token_ms":{"type":"object","properties":{"p50":{"type":["integer","null"]},"p90":{"type":["integer","null"]}}},"replies":{"type":"object","properties":{"answered":{"type":"integer"},"missing":{"type":"integer"},"rescued":{"type":"integer"}}},"sends":{"type":"object","properties":{"total":{"type":"integer"},"failed":{"type":"integer"},"quick_reply_share":{"type":["number","null"]}}},"handoff":{"type":"object","properties":{"requested":{"type":"integer"},"answered":{"type":"integer","description":"Taken over by an advisor."},"replied":{"type":"integer","description":"An advisor wrote after the request."},"median_answer_seconds":{"type":["integer","null"]}}},"leads":{"type":"object","properties":{"total":{"type":"integer"},"with_email":{"type":"integer"},"with_phone":{"type":"integer"},"with_name":{"type":"integer"},"valid":{"type":"integer","description":"With one email or phone that is not a placeholder or the business's own number."},"flags":{"type":"object","properties":{"placeholder_email":{"type":"integer"},"placeholder_phone":{"type":"integer"},"business_phone":{"type":"integer"}}},"required_complete":{"type":["integer","null"],"description":"Leads holding every required field; null when no assistant marks any."},"booked":{"type":"integer"},"lost":{"type":"integer"},"revenue":{"type":"array","items":{"type":"object","properties":{"currency":{"type":"string"},"amount":{"type":"number"}},"required":["currency","amount"]}}}},"cost":{"type":"object","properties":{"total":{"type":"array","items":{"type":"object","properties":{"currency":{"type":"string"},"amount":{"type":"number"}},"required":["currency","amount"]}},"per_lead":{"type":"array","items":{"type":"object","properties":{"currency":{"type":"string"},"amount":{"type":"number"}},"required":["currency","amount"]}}}},"stop_points":{"type":"array","description":"Where ended chats stopped while the assistant waited on the visitor, by what it had asked (from the analysis labels).","items":{"type":"object","properties":{"key":{"type":"string"},"text":{"type":"string"},"conversations":{"type":"integer"}},"required":["key","text","conversations"]}},"outcomes":{"type":"object","description":"How ended chats ended, from the analysis labels. Counts by label; `labelled` of `ended` were labelled.","properties":{"ended":{"type":"integer"},"labelled":{"type":"integer"},"intents":{"type":"object","additionalProperties":{"type":"integer"}},"goal":{"type":"object","additionalProperties":{"type":"integer"}},"waiting_on":{"type":"object","additionalProperties":{"type":"integer"}},"contact_declined":{"type":"integer"},"friction":{"type":"object","additionalProperties":{"type":"integer"}}}},"tools":{"type":"array","description":"Tool calls in the window's chats. `expected` failures are the tool asking for what is missing; the rest are the platform failing.","items":{"type":"object","properties":{"name":{"type":"string"},"calls":{"type":"integer"},"failed":{"type":"integer"},"expected":{"type":"integer"},"errors":{"type":"object","additionalProperties":{"type":"integer"}}}}},"knowledge":{"type":"object","description":"Knowledge searches by result: unavailable is the search failing, empty is missing content.","properties":{"searches":{"type":"integer"},"ok":{"type":"integer"},"empty":{"type":"integer"},"unavailable":{"type":"integer"},"other":{"type":"integer"}}},"improvement":{"$ref":"#/components/schemas/ChatImprovement"},"partial":{"type":"boolean","description":"True when not every send in the window could be read, so the counts are a lower bound. Retry, or narrow the range."}},"required":["improvement","partial"]},"ChatSseEvent":{"description":"One server-sent event frame on the send-message stream: `event` is the SSE event name, `data` the parsed JSON of its data line.","oneOf":[{"type":"object","title":"message.accepted","properties":{"event":{"const":"message.accepted"},"data":{"type":"object"}},"required":["event","data"]},{"type":"object","title":"message.delta","properties":{"event":{"const":"message.delta"},"data":{"type":"object","properties":{"text":{"type":"string","description":"The next piece of the reply."}},"required":["text"]}},"required":["event","data"]},{"type":"object","title":"tool.status","properties":{"event":{"const":"tool.status"},"data":{"type":"object","properties":{"name":{"type":"string"},"state":{"type":"string","enum":["started","completed"]}},"required":["name","state"]}},"required":["event","data"]},{"type":"object","title":"message.completed","properties":{"event":{"const":"message.completed"},"data":{"type":"object","properties":{"message":{"anyOf":[{"$ref":"#/components/schemas/PublicMessage"},{"type":"null"}],"description":"The stored reply. Null while a person has taken over: their reply arrives through the updates route."},"conversation":{"type":"object","properties":{"status":{"type":"string","enum":["open","ended"]},"resumable":{"type":"boolean"},"handoff_requested_at":{"type":["string","null"],"format":"date-time"},"taken_over":{"type":"boolean"},"lead_id":{"type":["string","null"],"description":"Set once the assistant has captured a lead."},"lead":{"anyOf":[{"$ref":"#/components/schemas/PublicChatLead"},{"type":"null"}],"description":"As on the updates route: present only when the lead panel is on."}}}},"required":["message","conversation"]}},"required":["event","data"]},{"type":"object","title":"error","properties":{"event":{"const":"error"},"data":{"type":"object","properties":{"code":{"type":"string"}},"required":["code"]}},"required":["event","data"]},{"type":"object","title":"heartbeat","properties":{"event":{"const":"heartbeat"},"data":{"type":"object"}},"required":["event","data"]}]},"ChatSseEvents":{"type":"string","description":"text/event-stream frames, each `event: <name>`, `data: <JSON>` and a blank line; there is no `[DONE]` sentinel. Usually: `message.accepted` (data `{}`) once the visitor message is stored, then any number of `message.delta` (`{text}`, append in order) and `tool.status` (`{name,state}`, state `started` or `completed`). Always exactly one terminal frame, which may be the only frame (a replayed `client_message_id`, or a send while a person has taken over): `message.completed` (`{message,conversation}`, message is null while a person has taken over) or `error` (`{code}`: `llm_error`, `llm_not_configured`, `turn_timeout`, `turn_interrupted`, `message_already_accepted`, `attachment_already_sent`). `heartbeat` (`{}`) arrives every 15 seconds while the stream is open; ignore it. See ChatSseEvent for each frame's data."},"ChatSubscription":{"type":"object","properties":{"id":{"type":"string"},"org_id":{"type":"string"},"project_id":{"type":"string"},"kind":{"type":"string","enum":["health","digest"]},"channel":{"$ref":"#/components/schemas/ChatNotificationChannel"},"enabled":{"type":"boolean"},"timezone":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","project_id","kind","channel","enabled","timezone"]},"ChatSubscriptionCreate":{"type":"object","properties":{"project_id":{"type":"string"},"kind":{"type":"string","enum":["health","digest"]},"channel":{"$ref":"#/components/schemas/ChatNotificationChannel"},"timezone":{"type":"string","description":"IANA zone; default UTC."}},"required":["kind","channel"]},"ChatSubscriptionPatch":{"type":"object","minProperties":1,"properties":{"enabled":{"type":"boolean"},"channel":{"$ref":"#/components/schemas/ChatNotificationChannel"},"timezone":{"type":"string"}}},"ChatTrend":{"type":"object","properties":{"interval":{"type":"string","enum":["day","week"]},"points":{"type":"array","items":{"type":"object","properties":{"period":{"type":"string","description":"YYYY-MM-DD; a Monday for weekly points."},"page_views":{"type":"integer"},"shown":{"type":"integer"},"opened":{"type":"integer"},"sends":{"type":"integer"},"accepted":{"type":"integer"},"failed":{"type":"integer"},"conversations":{"type":"integer"},"leads":{"type":"integer"},"handoffs":{"type":"integer"}},"required":["period"]}}},"required":["interval","points"]},"Conversation":{"type":"object","properties":{"id":{"type":"string"},"conversation_id":{"type":"string","description":"Alias used by the public resume surface."},"org_id":{"type":"string"},"project_id":{"type":"string"},"assistant_id":{"type":"string"},"widget_key_id":{"type":["string","null"]},"channel":{"type":"string","enum":["web_chat","whatsapp"]},"mode":{"type":"string","enum":["live","test","preview"],"description":"Set by the server from the key the chat came in on: test chats run the draft, preview chats send nothing out."},"status":{"type":"string","enum":["open","ended"]},"ended_reason":{"type":["string","null"],"enum":["visitor_ended","idle_timeout","max_messages","error",null]},"resumable":{"type":"boolean","description":"Public resume surface: ended only because the visitor went quiet, so the next message reopens it."},"handoff_requested_at":{"type":["string","null"],"format":"date-time"},"taken_over_at":{"type":["string","null"],"format":"date-time"},"taken_over_heartbeat_at":{"type":["string","null"],"format":"date-time"},"taken_over_by":{"type":["string","null"]},"taken_over_by_name":{"type":["string","null"]},"taken_over":{"type":"boolean"},"first_taken_over_at":{"type":["string","null"],"format":"date-time","description":"First time a human took over; unlike taken_over_at, never cleared."},"handoff_answered_at":{"type":["string","null"],"format":"date-time","description":"First takeover at or after handoff_requested_at."},"handoff_in_hours":{"type":["boolean","null"],"description":"Whether the bot told the visitor advisors were available when they first asked for a person, by the assistant's working hours (none configured counts as available). Null when unknown, as on requests from before 7 October 2026 without a stored answer."},"handoff_status":{"type":"string","enum":["none","pending","answered","missed","abandoned"],"description":"missed: the chat ended before anyone took over. abandoned: a human took over and then left the visitor waiting; it takes precedence over the others."},"handoff_answer_seconds":{"type":["integer","null"]},"human_outcome":{"type":["string","null"],"enum":["handled","abandoned",null],"description":"Null when no human took part. handled: every visitor message to the human got a human reply. abandoned: at least one did not, before the bot replied, the chat ended, or (chat still open) 5 minutes passed. Recomputed when a seat is released, when the chat ends, and on each analysis run."},"human_metrics":{"anyOf":[{"$ref":"#/components/schemas/HumanMetrics"},{"type":"null"}]},"needs_review":{"type":"boolean","description":"Operator list only: the analysis flagged the human advisor's replies with severity 2 or more."},"visitor_id":{"type":["string","null"]},"parent_origin":{"type":["string","null"]},"last_message_at":{"type":["string","null"],"format":"date-time"},"message_count":{"type":"integer"},"lead_id":{"type":["string","null"]},"metadata":{"type":["object","null"],"additionalProperties":true},"callback":{"anyOf":[{"type":"object","properties":{"phone":{"type":"string"},"slot_start":{"type":"string","format":"date-time"},"note":{"type":"string"},"requested_at":{"type":"string","format":"date-time"}},"required":["phone","requested_at"]},{"type":"null"}]},"ended_at":{"type":["string","null"],"format":"date-time"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"messages":{"type":"array","items":{"$ref":"#/components/schemas/ConversationMessage"}},"costs":{"type":"array","items":{"type":"object","additionalProperties":true}},"lead":{"anyOf":[{"$ref":"#/components/schemas/Lead"},{"type":"null"}]},"summary":{"type":["string","null"],"description":"Operator list only: the latest background summary, when analysis is on."},"analysis":{"anyOf":[{"$ref":"#/components/schemas/ConversationAnalysis"},{"type":"null"}],"description":"Operator detail only: the latest background summary and extracted facts."},"live_handoff":{"type":"boolean","description":"Operator detail only: whether the org takes web chats over live. When false, takeover is refused with 409 live_handoff_disabled."}},"required":["status"]},"ConversationAnalysis":{"type":"object","description":"Written after the conversation has been quiet for 60 seconds, and again when it ends, when `channels.web_chat.analysis.enabled` is set.","properties":{"summary":{"type":["string","null"]},"structured":{"type":"object","additionalProperties":true,"description":"The configured `channels.web_chat.analysis.fields` the chat stated, keyed by field key."},"through_seq":{"type":"integer","description":"The last message seq the run read."},"human_review":{"anyOf":[{"$ref":"#/components/schemas/HumanReview"},{"type":"null"}],"description":"Null when no human took part."},"needs_review":{"type":"boolean","description":"human_review has a flag of severity 2 or more."},"updated_at":{"type":"string","format":"date-time"}},"required":["summary","structured","through_seq","updated_at"]},"ConversationAttachment":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"content_type":{"type":"string","enum":["image/jpeg","image/png","image/webp","image/gif","application/pdf","application/vnd.openxmlformats-officedocument.wordprocessingml.document"]},"size":{"type":"integer","description":"Bytes."},"url":{"type":"string","format":"uri","description":"Signed download link (GET /public/chat/attachments/{id}), valid for 24 hours (7 days in webhooks)."}},"required":["id","name","content_type","size","url"]},"ConversationHandoffSummary":{"type":"object","properties":{"conversations":{"type":"integer"},"requested":{"type":"integer"},"pending":{"type":"integer"},"answered":{"type":"integer"},"missed":{"type":"integer"},"requested_in_hours":{"type":"integer","description":"Requests made while advisors were said to be available."},"requested_out_of_hours":{"type":"integer","description":"Requests made outside working hours, when the bot offers a callback instead."},"missed_in_hours":{"type":"integer"},"missed_out_of_hours":{"type":"integer"},"taken_over":{"type":"integer","description":"Conversations a human took over, requested or not."},"abandoned":{"type":"integer","description":"Conversations a human took over and then left the visitor waiting."},"handled":{"type":"integer","description":"Conversations a human took part in where every visitor message to them got a human reply."},"unanswered_messages":{"type":"integer","description":"Visitor messages to a human that no human answered, summed."},"callbacks":{"type":"integer","description":"Conversations where the visitor left a callback request."},"live_handoff":{"type":"boolean","description":"Whether the org takes web chats over live. When false, requests for a person are callback requests only."},"answer_seconds":{"$ref":"#/components/schemas/SecondsPercentiles"},"first_response_seconds":{"$ref":"#/components/schemas/SecondsPercentiles","description":"From the first takeover to the first human message."},"longest_wait_seconds":{"$ref":"#/components/schemas/SecondsPercentiles","description":"Each conversation's longest wait for the human, answered or not."}},"required":["conversations","requested","pending","answered","missed","requested_in_hours","requested_out_of_hours","missed_in_hours","missed_out_of_hours","taken_over","abandoned","handled","unanswered_messages","callbacks","live_handoff","answer_seconds","first_response_seconds","longest_wait_seconds"]},"ConversationMessage":{"type":"object","properties":{"id":{"type":"string"},"conversation_id":{"type":"string"},"org_id":{"type":"string"},"project_id":{"type":"string"},"role":{"type":"string","enum":["user","assistant","agent","tool","system"]},"content":{"type":"string"},"rich":{"$ref":"#/components/schemas/RichContent"},"tool_calls":{"type":["array","null"],"items":{"type":"object","additionalProperties":true}},"tool_call_id":{"type":["string","null"]},"seq":{"type":"integer"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","role","content","created_at"]},"ConversationTakeover":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["open","ended"]},"taken_over_at":{"type":["string","null"],"format":"date-time"},"taken_over_heartbeat_at":{"type":["string","null"],"format":"date-time"},"taken_over_by":{"type":["string","null"]}},"required":["id","status","taken_over_at","taken_over_heartbeat_at","taken_over_by"]},"ConversationTakeoverHeartbeat":{"type":"object","properties":{"taken_over_heartbeat_at":{"type":"string","format":"date-time"}},"required":["taken_over_heartbeat_at"]},"ConversationTakeoverRequest":{"type":"object","properties":{"agent":{"$ref":"#/components/schemas/AgentIdentityInput"}}},"DeleteResult":{"type":"object","properties":{"id":{"type":"string"},"deleted":{"type":"boolean"}},"required":["id","deleted"]},"Error":{"type":"object","description":"Every error body carries a string code in `error` (`{\"error\":\"not_found\"}`), often a `message`, and sometimes fields that belong to the code beside them. Branch on the code and the HTTP status, never on `message`.","properties":{"error":{"type":"string","description":"Machine-readable code, for example `invalid_request`, `unauthorized`, `not_found`, `read_only_key`, `insufficient_role`, `rate_limited`, `internal_error`, or a route-specific one such as `number_attached`."},"required":{"type":"string","description":"`insufficient_role` only: the role the route needs."},"actual":{"type":"string","description":"`insufficient_role` only: the caller's role."},"message":{"type":"string","description":"Human-readable detail. The wording can change; do not parse it."},"issues":{"type":"array","items":{"type":"object","additionalProperties":true},"description":"Optional. On `invalid_request`: the validation issues, each with the failing `path` and a `message`."},"request_id":{"type":"string","description":"`internal_error` only: the request id (also sent as the `x-request-id` header) to quote when reporting the failure."}},"required":["error"],"additionalProperties":true},"HumanMetrics":{"type":"object","description":"What the human advisor's part of a conversation came to.","properties":{"first_response_seconds":{"type":["integer","null"],"description":"From the first takeover to the first human message."},"longest_wait_seconds":{"type":["integer","null"],"description":"The longest a visitor waited on the human, answered or not."},"unanswered_count":{"type":"integer","description":"Visitor messages to the human that no human answered."},"agent_message_count":{"type":"integer"}},"required":["first_response_seconds","longest_wait_seconds","unanswered_count","agent_message_count"]},"HumanReview":{"type":"object","description":"The post-chat review of the human advisor's replies. left_mid_chat and slow_reply come from the transcript's timing; the other codes from the analysis model, which reads only the advisor's messages.","properties":{"flags":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","enum":["unanswered_question","spelling_grammar","unprofessional_tone","broken_promise","wrong_fact","other","left_mid_chat","slow_reply"]},"severity":{"type":"integer","minimum":1,"maximum":3},"evidence":{"type":"string","maxLength":200,"description":"A paraphrase; never contact details."}},"required":["code","severity","evidence"]}},"reviewed_through_seq":{"type":"integer","description":"The last message seq the model review read."}},"required":["flags","reviewed_through_seq"]},"Integration":{"type":"object","description":"A configured provider credential. Metadata only: the secret is encrypted at rest and never returned.","properties":{"provider":{"type":"string","description":"Provider slug, e.g. openai, deepgram, elevenlabs, twilio."},"key_version":{"type":"integer","description":"Master-key version the secret is sealed under."},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["provider","key_version","created_at","updated_at"]},"IntegrationCreate":{"type":"object","properties":{"project_id":{"type":"string","description":"Required only for an org-wide key."},"secret":{"type":"string","minLength":1,"maxLength":8192,"writeOnly":true,"description":"The raw provider secret (API key, or a JSON blob for multi-field providers). Encrypted at rest; never returned."}},"required":["secret"]},"IntegrationDeleteResult":{"type":"object","properties":{"provider":{"type":"string"},"deleted":{"type":"boolean"}},"required":["provider","deleted"]},"IntegrationStatus":{"type":"object","properties":{"provider":{"type":"string"},"project_id":{"type":"string"},"configured":{"type":"boolean"},"verified":{"type":"boolean","description":"True when the provider accepted the key on save. False when it could not be checked (no check for this provider, a scoped key, or the provider was unreachable); the key is stored either way."}},"required":["provider","configured","verified"]},"KnowledgeBase":{"type":"object","properties":{"id":{"type":"string","description":"`kb_...`"},"org_id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"enabled":{"type":"boolean"},"active_publication_id":{"type":["string","null"],"description":"The live publication, or null before the first publish."},"version":{"type":"integer","description":"The content counter: bumped when sources change and when `name`, `description` or `enabled` is updated. Approvals, publishing, test picks and `retrieval_mode` leave it as it is."},"retrieval_mode":{"type":"string","enum":["vector_store","hybrid"]},"test_publication_id":{"type":["string","null"],"description":"The publication test chats search instead of the live one, or null."},"test_base_publication_id":{"type":["string","null"],"description":"The live publication when the test one was picked; promoting is refused once live has moved on."},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","org_id","name","description","enabled","active_publication_id","version","retrieval_mode"]},"KnowledgeBaseDetail":{"allOf":[{"$ref":"#/components/schemas/KnowledgeBase"},{"type":"object","properties":{"permission":{"type":"string","enum":["viewer","editor","publisher"],"description":"What the caller may do. API keys are `publisher`."},"can_manage_access":{"type":"boolean"},"configured":{"type":"boolean","description":"Whether this deployment can index and search knowledge."},"sources":{"type":"array","items":{"$ref":"#/components/schemas/KnowledgeSource"}},"publications":{"type":"array","items":{"$ref":"#/components/schemas/KnowledgePublication"}},"attachments":{"type":"array","items":{"type":"object","properties":{"assistant_id":{"type":"string"},"name":{"type":"string"},"chat":{"type":"boolean"},"voice":{"type":"boolean"},"priority":{"type":"integer"}},"required":["assistant_id","name","chat","voice","priority"]}},"pending":{"type":"array","description":"What publishing each open assistant draft would change for this base.","items":{"type":"object","properties":{"assistant_id":{"type":"string"},"name":{"type":"string"},"change":{"type":"string","enum":["attach","update","detach"]},"chat":{"type":"boolean"},"voice":{"type":"boolean"},"priority":{"type":"integer"}},"required":["assistant_id","name","change","chat","voice","priority"]}},"grants":{"type":"array","items":{"type":"object","properties":{"user_id":{"type":"string"},"role":{"type":"string","enum":["viewer","editor","publisher"]}},"required":["user_id","role"],"additionalProperties":true}}},"required":["permission","sources","publications","attachments","pending","grants"]}]},"KnowledgePublication":{"type":"object","properties":{"id":{"type":"string","description":"`kpub_...`"},"status":{"type":"string","enum":["queued","indexing","ready","failed"]},"error":{"type":["string","null"]},"retrying":{"type":"boolean","description":"True while a `failed` publication will be retried automatically: a provider indexing failure (`indexing_failed`, `indexing_timeout` or `provider_file_missing`), fewer than five attempts so far, and no source change since it was built. Keep polling while it is true; otherwise retry it yourself, or build again after a change."},"hybrid_status":{"type":["string","null"],"enum":["queued","indexing","ready","failed",null],"description":"The hybrid search index for this publication, or null when it has none."},"base_version":{"type":"integer","description":"The base version it was built from."},"created_at":{"type":"string","format":"date-time"},"published_at":{"type":["string","null"],"format":"date-time"}},"required":["id","status","base_version","created_at"]},"KnowledgeRevisionPreview":{"type":"object","properties":{"revision_id":{"type":"string"},"title":{"type":"string"},"text":{"type":"string"},"url":{"type":["string","null"]}},"required":["revision_id","title","text"]},"KnowledgeSearchResult":{"type":"object","properties":{"status":{"type":"string","enum":["unattached","skipped","ok","empty","unavailable"],"description":"`empty`: nothing matched or nothing is published. `unavailable`: search failed this time."},"evidence":{"type":"array","items":{"type":"object","properties":{"knowledge_base_id":{"type":"string"},"publication_id":{"type":"string"},"source_id":{"type":"string"},"revision_id":{"type":"string"},"title":{"type":"string"},"url":{"type":["string","null"]},"text":{"type":"string"},"score":{"type":"number","description":"Similarity, for ordering only; not a confidence."}},"required":["knowledge_base_id","publication_id","source_id","revision_id","title","url","text","score"]}},"elapsed_ms":{"type":"integer"}},"required":["status","evidence","elapsed_ms"]},"KnowledgeSource":{"type":"object","properties":{"id":{"type":"string","description":"`ksrc_...`"},"title":{"type":"string"},"kind":{"type":"string","enum":["text","url","file"]},"url":{"type":["string","null"]},"audience":{"type":"string","enum":["public","private"]},"revoked_at":{"type":["string","null"],"format":"date-time"},"expires_at":{"type":["string","null"],"format":"date-time"},"revision":{"type":["object","null"],"description":"The latest revision.","properties":{"id":{"type":"string","description":"`krev_...`"},"approved":{"type":"boolean"},"characters":{"type":"integer"},"content_hash":{"type":"string"},"created_at":{"type":"string","format":"date-time"}},"required":["id","approved","characters","content_hash","created_at"]}},"required":["id","title","kind","audience","revision"],"additionalProperties":true},"Lead":{"type":"object","properties":{"id":{"type":"string"},"org_id":{"type":"string"},"project_id":{"type":"string"},"assistant_id":{"type":"string"},"conversation_id":{"type":["string","null"]},"name":{"type":["string","null"]},"email":{"type":["string","null"],"format":"email"},"phone":{"type":["string","null"]},"fields":{"type":["object","null"],"additionalProperties":{"type":"string"}},"source":{"type":"string","enum":["web_chat","whatsapp"]},"mode":{"type":"string","enum":["live","test","preview"],"description":"The mode of the chat that captured it: test and preview leads are left out of lists unless asked for."},"outcome":{"type":["string","null"],"enum":["open","booked","lost",null],"description":"What became of the lead, as your CRM reports it (PUT /leads/{id}/outcome)."},"outcome_value":{"type":["string","null"],"description":"Booking value as a decimal string."},"outcome_currency":{"type":["string","null"]},"outcome_at":{"type":["string","null"],"format":"date-time"},"outcome_ref":{"type":["string","null"],"description":"The CRM's own id for the booking."},"customer_id":{"type":["string","null"],"description":"The customer's own id for this person, set with PUT /v1/leads/{id}/outcome."},"attribution":{"anyOf":[{"$ref":"#/components/schemas/ChatAttribution"},{"type":"null"}],"description":"Where the lead's chat came from. Returned by the list and detail reads; null when none was recorded."},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","org_id","project_id","assistant_id","source"]},"LeadOutcomeInput":{"type":"object","properties":{"outcome":{"type":"string","enum":["open","booked","lost"]},"value":{"type":"number","minimum":0,"description":"Booking value, stored to two decimal places."},"currency":{"type":"string","pattern":"^[A-Z]{3}$","description":"ISO 4217 code in capitals, for example `GBP`. Required when value is given."},"at":{"type":"string","format":"date-time","description":"When it happened, ISO 8601 with an offset. Default: now."},"ref":{"type":"string","maxLength":128,"description":"Your CRM's id for the booking."},"customer_id":{"type":"string","minLength":1,"maxLength":128,"description":"The customer's own id for this person in their CRM (trimmed). Stored on the lead and used to link this person's chats across devices. Left unchanged when omitted."}},"required":["outcome"],"dependentRequired":{"value":["currency"]}},"LibraryVoice":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"public_owner_id":{"type":"string"},"gender":{"type":"string"},"accent":{"type":"string"},"language":{"type":"string"},"preview_url":{"type":"string","format":"uri"},"category":{"type":"string"}},"required":["id","name","public_owner_id"]},"OrgMember":{"type":"object","properties":{"user_id":{"type":"string"},"email":{"type":"string","format":"email"},"name":{"type":["string","null"]},"role":{"$ref":"#/components/schemas/Role"},"created_at":{"type":"string","format":"date-time"}},"required":["user_id","email","name","role","created_at"]},"OutboundCallCreate":{"type":"object","properties":{"assistant_id":{"type":"string","minLength":1},"to":{"type":"string","minLength":1,"description":"E.164 number to dial, for example `+14155550123`."},"from":{"type":"string","minLength":1,"description":"Optional E.164 caller ID, registered and active in the assistant's organization and project. LiveKit calls dial from it; Vapi calls record it but dial from the Vapi phone number configured for the deployment."},"metadata":{"type":"object","additionalProperties":true,"description":"Your own context, stored on the call and sent in its webhooks."},"variables":{"type":"object","propertyNames":{"pattern":"^[A-Za-z_][A-Za-z0-9_.-]*$","maxLength":64},"additionalProperties":{"type":"string","maxLength":2000},"maxProperties":50,"description":"Per-call values; win over assistant defaults; unresolved keys render as empty strings."},"voice_provider":{"type":"string","enum":["livekit","vapi"],"description":"Voice runtime override; defaults to livekit."},"mode":{"type":"string","enum":["live","test"],"default":"live","description":"test runs the saved draft (live config when there is none) with test restrictions: only declared tools marked read-only that the platform runs server-side (none with their own endpoint), no transfers, DTMF or provider tools, voicemail hangs up. Its webhooks carry livemode: false and reach test subscriptions only. LiveKit only."}},"required":["assistant_id","to"]},"OutboundCallCreated":{"type":"object","properties":{"call_id":{"type":"string"},"room":{"type":"string"},"status":{"type":"string","enum":["ringing"]},"unresolved_variables":{"type":"array","items":{"type":"string"},"description":"Present only when a referenced {{key}} had neither a per-call value nor a default."}},"required":["call_id","room","status"]},"OutboundCallCreatedVapi":{"type":"object","description":"An outbound call placed with `voice_provider: vapi`.","properties":{"call_id":{"type":"string"},"provider_call_id":{"type":"string","description":"Vapi's id for the call."},"status":{"type":"string","enum":["ringing"]},"unresolved_variables":{"type":"array","items":{"type":"string"},"description":"Present only when a referenced {{key}} had neither a per-call value nor a default."}},"required":["call_id","provider_call_id","status"]},"PhoneNumber":{"type":"object","properties":{"id":{"type":"string"},"org_id":{"type":["string","null"]},"project_id":{"type":["string","null"]},"e164":{"type":"string"},"provider":{"type":"string"},"provider_number_id":{"type":["string","null"],"description":"The provider's own identifier for the number: Twilio's `PN…` SID."},"status":{"type":"string","enum":["active","inactive","released"]},"capabilities":{"type":["object","null"],"additionalProperties":true,"description":"What the provider says the number can do: `{ voice, sms, mms }`."},"routing_mode":{"type":"string","enum":["trunk","pv_webhook"]},"mode":{"type":"string","enum":["live","test"],"description":"Test mode, apart from carrier routing: calls to or from a test number are test calls."},"assistant_id":{"type":["string","null"]},"provider_metadata":{"type":["object","null"],"additionalProperties":true},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","e164","provider","status","routing_mode","mode"]},"PhoneNumberAttach":{"type":"object","properties":{"assistant_id":{"type":"string","minLength":1},"routing_mode":{"type":"string","enum":["trunk","pv_webhook"]}},"required":["assistant_id","routing_mode"]},"PhoneNumberImport":{"type":"object","properties":{"project_id":{"type":"string","description":"Required only for an org-wide key."},"e164":{"type":"string","pattern":"^\\+[1-9]\\d{1,14}$","description":"The number in E.164 format, e.g. +14155550123."},"provider":{"type":"string","minLength":1,"description":"Provider slug; defaults to twilio."},"provider_metadata":{"type":"object","additionalProperties":true,"description":"Carrier identifiers, for example `{\"twilioNumberSid\":\"PN...\"}` for a Twilio number."}},"required":["e164"]},"PhoneNumberMode":{"type":"object","properties":{"routing_mode":{"type":"string","enum":["trunk","pv_webhook"]}},"required":["routing_mode"]},"PhoneNumberPurchase":{"type":"object","properties":{"number":{"type":"string","minLength":1,"description":"The number to purchase in E.164 format, as the search returned it."},"project_id":{"type":"string","description":"Required only for an org-wide key or session."}},"required":["number"]},"PhoneNumberRelease":{"type":"object","properties":{"id":{"type":"string"},"deleted":{"type":"boolean"},"released":{"type":"boolean","description":"Whether the carrier released the number. False when it was not carrier-managed or the deployment has no carrier connection; the registry row is deleted either way."}},"required":["id","deleted","released"]},"PhoneNumberUpdate":{"type":"object","properties":{"mode":{"type":"string","enum":["live","test"]}},"required":["mode"],"additionalProperties":false},"PreviewLink":{"type":"object","properties":{"id":{"type":"string","description":"Also the page address: /preview/{id} on the console."},"assistant_id":{"type":"string"},"project_id":{"type":"string"},"key":{"type":"string","description":"The preview key the page loads the widget with."},"access":{"type":"string","enum":["public","team"]},"expires_at":{"type":["string","null"],"format":"date-time"},"expired":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"}},"required":["id","assistant_id","project_id","key","access","expires_at","expired","created_at"]},"PreviewPage":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"access":{"type":"string","enum":["public","team"]},"key":{"type":"string","description":"Absent for a team-only link read by a non-member."},"assistant_id":{"type":"string"},"name":{"type":"string","description":"The widget's display name."},"subtitle":{"type":["string","null"]},"expires_at":{"type":["string","null"],"format":"date-time"},"org_id":{"type":"string","description":"Present only when the key is withheld."}},"required":["id","access"]}}},"Project":{"type":"object","properties":{"id":{"type":"string"},"org_id":{"type":"string"},"name":{"type":"string"},"webhook_secret_set":{"type":"boolean","description":"Whether an agent webhook secret is configured. The secret itself is write-only and never returned."},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","org_id","name","webhook_secret_set"]},"ProjectCreate":{"type":"object","properties":{"name":{"type":"string","minLength":1}},"required":["name"]},"ProjectPatch":{"type":"object","description":"At least one field is required.","minProperties":1,"properties":{"name":{"type":"string","minLength":1},"webhook_secret":{"type":["string","null"],"minLength":16,"description":"HMAC key the agent signs this project's lifecycle webhooks with. Write-only; null clears it, and the project's next call mints a new one."}}},"ProviderLibraryResponse":{"type":"object","properties":{"provider":{"type":"string"},"source":{"type":"string","enum":["live","unavailable"]},"voices":{"type":"array","items":{"$ref":"#/components/schemas/LibraryVoice"}},"next_page":{"type":"integer","minimum":0},"live_error":{"type":"string"}},"required":["provider","source","voices"]},"ProviderModel":{"type":"object","properties":{"provider":{"type":"string","minLength":1},"model":{"type":"string","minLength":1}},"required":["provider","model"],"additionalProperties":true},"ProviderModelsResponse":{"type":"object","properties":{"provider":{"type":"string"},"source":{"type":"string","enum":["live","unavailable"]},"models":{"type":"array","items":{"$ref":"#/components/schemas/TtsProviderModel"}},"live_error":{"type":"string"}},"required":["provider","source","models"]},"ProviderRegistry":{"type":"object","properties":{"meta":{"$ref":"#/components/schemas/RegistryMeta"},"capabilities":{"$ref":"#/components/schemas/RegistryCapabilities"}},"required":["meta","capabilities"]},"ProviderVoice":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"language":{"type":"string"},"gender":{"type":"string"},"accent":{"type":"string"},"languages":{"type":"array","items":{"type":"string"}},"models":{"type":"array","items":{"type":"string"}},"preview_url":{"type":"string","format":"uri"}},"required":["id","name"]},"ProviderVoicesResponse":{"type":"object","properties":{"provider":{"type":"string"},"source":{"type":"string","enum":["live","unavailable"]},"voices":{"type":"array","items":{"$ref":"#/components/schemas/ProviderVoice"}},"live_error":{"type":"string"}},"required":["provider","source","voices"]},"PublicChatLead":{"type":"object","description":"What this visitor has given in this conversation: contact details and the fields the assistant asks for, nothing from other chats or a CRM.","properties":{"fields":{"type":"object","additionalProperties":{"type":"string"}},"complete":{"type":"boolean","description":"An email or phone and every required field are in."}},"required":["fields","complete"]},"PublicConversation":{"type":"object","properties":{"conversation_id":{"type":"string"},"status":{"type":"string","enum":["open","ended"]},"ended_reason":{"type":["string","null"],"enum":["visitor_ended","idle_timeout","max_messages","error",null],"description":"Why it ended. Null while it is open."},"resumable":{"type":"boolean","description":"Ended only because the visitor went quiet: the next message reopens it."},"handoff_requested_at":{"type":["string","null"],"format":"date-time"},"taken_over":{"type":"boolean","description":"A person holds the conversation now."},"lead_name":{"type":"string","description":"First name of the captured lead. Present only when there is one."},"lead":{"anyOf":[{"$ref":"#/components/schemas/PublicChatLead"},{"type":"null"}],"description":"Present only when the assistant's web chat `dynamic_ui.lead_panel` is on: what this visitor has given so far, or null before there is a lead."},"messages":{"type":"array","items":{"$ref":"#/components/schemas/PublicMessage"}}},"required":["conversation_id","status","ended_reason","resumable","handoff_requested_at","taken_over","messages"]},"PublicLeadSchema":{"type":"object","description":"The lead details a chat asks for, as the visitor's page may show them.","properties":{"require_complete":{"type":"boolean","description":"No lead is saved until every required field and an email or phone are in."},"fields":{"type":"array","items":{"type":"object","properties":{"key":{"type":"string"},"label":{"type":"string","description":"The field's short name for people to read."},"type":{"type":"string","enum":["string","email","phone"]},"required":{"type":"boolean"}},"required":["key","label","type","required"]}}},"required":["require_complete","fields"]},"PublicMessage":{"type":"object","description":"A message as the visitor sees it. `agent` is a person who took over the chat.","properties":{"id":{"type":"string","description":"`msg_...`"},"role":{"type":"string","enum":["user","assistant","agent"]},"content":{"type":"string"},"rich":{"$ref":"#/components/schemas/RichContent"},"seq":{"type":"integer","description":"Position in the conversation; pass the highest you have as `after_seq` when polling."},"created_at":{"type":"string","format":"date-time"}},"required":["id","role","content","rich","seq","created_at"]},"PublicWidgetConfig":{"type":"object","description":"The assistant's public web chat settings, as GET /public/v1/config (the widget's GET /public/chat/widget-config) and conversation start return them. Never includes instructions, model settings or secrets.","properties":{"name":{"type":"string","description":"Display name: `appearance.display_name`, or the assistant's name."},"greeting":{"type":"string"},"quick_replies":{"type":"array","items":{"type":"string"}},"hero_topics":{"type":"array","items":{"type":"string"},"description":"Topic buttons shown before the first message."},"appearance":{"type":"object","additionalProperties":true,"description":"Colours, position, display name and avatar."},"proactive":{"type":"object","additionalProperties":true,"description":"When and where to open a teaser on its own."},"strings":{"type":"object","additionalProperties":{"type":"string"},"description":"Interface text overrides."},"call":{"type":["object","null"],"additionalProperties":true,"description":"A phone number to offer in the header, when set."},"handoff_message":{"type":["string","null"],"description":"What to show when the visitor asks for a person."},"powered_by":{"additionalProperties":true},"feedback":{"type":"object","additionalProperties":true,"description":"Whether to ask for reply feedback and a conversation rating."},"attachments":{"type":"object","description":"Present only when the assistant takes files and this deployment can store them.","properties":{"enabled":{"type":"boolean","enum":[true]},"max_bytes":{"type":"integer"},"max_files":{"type":"integer","description":"Per message."},"accept":{"type":"array","items":{"type":"string"},"description":"Accepted content types."}}},"analytics":{"type":"object","properties":{"data_layer":{"type":"boolean"},"gtag":{"type":"boolean"},"event_prefix":{"type":"string"},"require_consent":{"type":"boolean"}},"description":"Off for test and preview chats, so they never reach your analytics."},"identity_scope":{"type":"string","description":"An opaque id for the project, the same for every key in it. Use it to name where you keep a visitor id, so a rotated or second key keeps the same visitors."},"mode":{"type":"string","enum":["test","preview"],"description":"Present only for a test key or a preview link; a live key's config has no `mode`."},"ui":{"type":"object","properties":{"style":{"type":"string","enum":["quiet"]}},"description":"Present only when the assistant uses the quiet chat style."},"dynamic_ui":{"type":"object","properties":{"contact_links":{"type":"boolean","enum":[true]}},"description":"Present only when the assistant shows contact details as links."},"lead_capture":{"$ref":"#/components/schemas/PublicLeadSchema","description":"The lead details the chat asks for, so your page can show what it has captured. Present only when lead capture and `dynamic_ui.lead_panel` are both on."},"voice":{"type":"object","properties":{"enabled":{"type":"boolean","enum":[true]},"max_seconds":{"type":"integer","description":"Each voice session's length cap."}},"required":["enabled","max_seconds"],"description":"Present only when the assistant takes voice in the browser and voice calls can be placed: offer a microphone button (POST /public/v1/conversations/{id}/voice)."}},"required":["name","identity_scope"]},"RecordingAccess":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"expires_at":{"type":"string","format":"date-time"}},"required":["url","expires_at"]},"RegistryCapabilities":{"type":"object","properties":{"llm":{"$ref":"#/components/schemas/RegistryCapability"},"stt":{"$ref":"#/components/schemas/RegistryCapability"},"tts":{"$ref":"#/components/schemas/RegistryCapability"},"realtime":{"$ref":"#/components/schemas/RegistryCapability"}},"required":["llm","stt","tts","realtime"]},"RegistryCapability":{"type":"object","properties":{"providers":{"type":"array","items":{"$ref":"#/components/schemas/RegistryProvider"}}},"required":["providers"]},"RegistryMeta":{"type":"object","properties":{"sdk_pin":{"type":"string","const":"livekit-agents==1.8.6"},"engine_default":{"type":"string","const":"livekit"},"generated_from":{"type":"string","const":"conversational-voice-runtimes@931b1f8 (LiveKit, 2026-10-23)"}},"required":["sdk_pin","engine_default","generated_from"]},"RegistryModel":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"tier":{"type":"string","enum":["recommended","standard","legacy","deprecated","eol"]},"eol_date":{"type":"string","format":"date"},"notes":{"type":"string"},"unsupported_params":{"type":"array","items":{"type":"string"}},"param_overrides":{"type":"object","description":"Per-model refinement of provider params, keyed by param key. An entry also opts the model into a param gated by applies_to_models.","additionalProperties":{"$ref":"#/components/schemas/RegistryParamOverride"}},"doc_url":{"type":"string","format":"uri"},"verified_at":{"type":"string","format":"date"}},"required":["id","tier"]},"RegistryParam":{"type":"object","properties":{"key":{"type":"string"},"type":{"type":"string","enum":["number","string","text","boolean","enum","string_list","number_list","object","object_list","key_value"]},"min":{"type":"number"},"max":{"type":"number"},"step":{"type":"number"},"integer":{"type":"boolean"},"enum_values":{"type":"array","items":{"type":"string"}},"enum_labels":{"type":"object","additionalProperties":{"type":"string"}},"allow_custom":{"type":"boolean"},"advanced":{"type":"boolean"},"default":{},"applies_to_models":{"type":"array","items":{"type":"string"}},"label":{"type":"string"},"help":{"type":"string"},"fields":{"type":"array","items":{"$ref":"#/components/schemas/RegistryParam"}},"item_min":{"type":"number"},"item_max":{"type":"number"}},"required":["key","type"]},"RegistryParamOverride":{"type":"object","properties":{"enum_values":{"type":"array","items":{"type":"string"}},"default":{},"min":{"type":"number"},"max":{"type":"number"}}},"RegistryProvider":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"byok_required":{"type":"boolean"},"inference_served":{"type":"boolean"},"engines":{"type":"array","items":{"type":"string","enum":["livekit","pipecat"]}},"models":{"type":"array","items":{"$ref":"#/components/schemas/RegistryModel"}},"params":{"type":"array","items":{"$ref":"#/components/schemas/RegistryParam"}},"custom_models_allowed":{"type":"boolean"},"requires_voice_id":{"type":"boolean"},"voices":{"type":"array","items":{"$ref":"#/components/schemas/RegistryVoice"}}},"required":["id","label","byok_required","inference_served","engines","models","params","custom_models_allowed"]},"RegistryVoice":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}}},"required":["id","name"]},"RichContent":{"type":["object","null"],"properties":{"quick_replies":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string"},"value":{"type":"string"}},"required":["label","value"]}},"buttons":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["link"]},"label":{"type":"string"},"url":{"type":"string","format":"uri"}},"required":["type","label","url"]}},"metadata":{"type":"object","additionalProperties":true,"properties":{"page":{"type":"object","properties":{"path":{"type":"string"},"title":{"type":"string"}},"required":["path","title"],"description":"Visitor messages from the web widget: the page they sent the message from."},"channel":{"type":"string","enum":["voice"],"description":"A turn spoken in a voice session of this conversation rather than typed."},"voice_session_id":{"type":"string","description":"The voice session the turn was spoken in."}}},"attachments":{"type":"array","items":{"$ref":"#/components/schemas/ConversationAttachment"},"description":"Files sent with the message. Each `url` is a signed link that works without credentials."}}},"Role":{"type":"string","enum":["viewer","member","admin","owner"]},"SecondsPercentiles":{"type":"object","properties":{"median":{"type":["integer","null"]},"p90":{"type":["integer","null"]}},"required":["median","p90"]},"TtsProviderModel":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"provider_id":{"type":"string","description":"Underlying model company identifier for multi-provider catalogs."},"provider_name":{"type":"string","description":"Underlying model company display name."},"description":{"type":"string"},"languages":{"type":"array","items":{"type":"string"}}},"required":["id"]},"VisitorJourney":{"type":"object","properties":{"visitor_id":{"type":["string","null"]},"visitors":{"type":"array","description":"The visitor ids whose visits are listed: this conversation's own, plus those of conversations in the same organization whose lead shares an email, phone number or customer id. At most 10.","items":{"type":"object","properties":{"visitor_id":{"type":"string"},"linked_by":{"type":"string","enum":["this_chat","email","phone","customer_id"]},"conversation_ids":{"type":"array","items":{"type":"string"}}},"required":["visitor_id","linked_by","conversation_ids"]}},"visits":{"type":"array","items":{"type":"object","properties":{"visit_id":{"type":"string"},"visitor_id":{"type":"string"},"site":{"type":["string","null"],"description":"The site of the visit's earliest event."},"visit_number":{"type":["integer","null"]},"started_at":{"type":"string","format":"date-time"},"last_at":{"type":"string","format":"date-time"},"landing_page":{"type":["string","null"]},"source":{"type":"string","description":"utm_source, else the referrer host, else direct."},"page_views":{"type":"integer"},"pages":{"type":"array","items":{"type":"string"},"description":"The first 10 distinct pages, in the order visited."},"opened_reason":{"type":["string","null"],"enum":["proactive","visitor","api",null]},"sent":{"type":"boolean"},"conversation_ids":{"type":"array","items":{"type":"string"}},"is_this_conversation":{"type":"boolean"}},"required":["visit_id","visitor_id","site","visit_number","started_at","last_at","landing_page","source","page_views","pages","opened_reason","sent","conversation_ids","is_this_conversation"]}},"truncated":{"type":"boolean","description":"True when the visitor has more than 20 visits."}},"required":["visitor_id","visitors","visits","truncated"]},"WebCallCreate":{"type":"object","properties":{"assistant_id":{"type":"string","minLength":1},"metadata":{"type":"object","additionalProperties":true,"description":"Your own context, stored on the call and sent in its webhooks."},"variables":{"type":"object","propertyNames":{"pattern":"^[A-Za-z_][A-Za-z0-9_.-]*$","maxLength":64},"additionalProperties":{"type":"string","maxLength":2000},"maxProperties":50,"description":"Per-call values; win over assistant defaults; unresolved keys render as empty strings."},"voice_provider":{"type":"string","enum":["livekit","vapi"],"description":"Voice runtime override; defaults to livekit."},"mode":{"type":"string","enum":["live","test"],"default":"live","description":"test runs the saved draft (live config when there is none) with test restrictions: only declared tools marked read-only that the platform runs server-side (none with their own endpoint), no transfers, DTMF or provider tools, voicemail hangs up. Its webhooks carry livemode: false and reach test subscriptions only. LiveKit only."}},"required":["assistant_id"]},"WebCallCreated":{"type":"object","properties":{"call_id":{"type":"string"},"room":{"type":"string"},"token":{"type":"string","description":"LiveKit join token for the browser, valid for 15 minutes."},"url":{"type":"string","description":"LiveKit server URL to connect to."},"unresolved_variables":{"type":"array","items":{"type":"string"},"description":"Present only when a referenced {{key}} had neither a per-call value nor a default."}},"required":["call_id","room","token","url"]},"WebCallCreatedVapi":{"type":"object","description":"A browser call started with `voice_provider: vapi`: open `web_call_url`, when present, to join.","properties":{"call_id":{"type":"string"},"provider_call_id":{"type":"string"},"web_call_url":{"type":"string","format":"uri"},"unresolved_variables":{"type":"array","items":{"type":"string"}}},"required":["call_id","provider_call_id"]},"WebhookDelivery":{"type":"object","properties":{"id":{"type":"string"},"event_id":{"type":"string"},"subscription_id":{"type":"string"},"project_id":{"type":"string"},"event":{"type":"string"},"status":{"type":"string","enum":["pending","delivered","failed"]},"attempts":{"type":"integer"},"last_status":{"type":["integer","null"],"description":"HTTP status of the latest attempt."},"last_error":{"type":["string","null"]},"last_attempt_at":{"type":["string","null"],"format":"date-time"},"delivered_at":{"type":["string","null"],"format":"date-time"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","event_id","subscription_id","event","status","attempts","created_at"]},"WebhookEvent":{"type":"object","properties":{"event_id":{"type":"string"},"call_id":{"type":"string"},"event":{"type":"string"},"org_id":{"type":["string","null"]},"project_id":{"type":["string","null"]},"payload":{"type":"object","additionalProperties":true},"received_at":{"type":"string","format":"date-time"}},"required":["event_id","call_id","event","payload","received_at"]},"WebhookSubscription":{"type":"object","properties":{"id":{"type":"string"},"org_id":{"type":"string"},"project_id":{"type":"string"},"url":{"type":"string","format":"uri","description":"Public HTTPS destination. Private addresses and redirects are refused at delivery."},"events":{"type":"array","items":{"type":"string"},"description":"Subscribed event types; empty = all."},"mode":{"type":"string","enum":["live","test"],"description":"live hears about live chats and calls; test only about test chats (test keys and the console's test chat) and test calls (mode test, test numbers, the console's test call), whose events carry livemode: false. Fixed at creation."},"disabled_at":{"type":["string","null"],"format":"date-time","description":"Set while the subscription is turned off."},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","org_id","project_id","url","events"]},"WebhookSubscriptionCreate":{"type":"object","properties":{"project_id":{"type":"string","description":"Required only for an org-wide key."},"url":{"type":"string","format":"uri","pattern":"^https://","description":"Public HTTPS destination. Private addresses and redirects are refused at delivery."},"events":{"type":"array","items":{"type":"string","enum":["call.started","call.connected","call.ended","conversation.started","conversation.message.created","conversation.handoff_requested","conversation.callback_requested","conversation.takeover.started","conversation.takeover.released","conversation.analyzed","conversation.ended","lead.captured"]},"description":"Event types to deliver; empty or omitted means all. An unknown name is refused with 400."},"mode":{"type":"string","enum":["live","test"],"default":"live","description":"Which chats it hears about. It cannot be changed later: create a new subscription instead."},"secret":{"type":"string","minLength":1,"description":"Optional; a whsec_… secret is minted when omitted."},"enabled":{"type":"boolean","description":"False creates the subscription turned off. Default true."}},"required":["url"]},"WebhookSubscriptionCreated":{"description":"A new subscription, with its signing secret. This is the only response that shows the secret.","allOf":[{"$ref":"#/components/schemas/WebhookSubscription"},{"type":"object","properties":{"secret":{"type":"string","description":"HMAC-SHA256 signing key (`whsec_` and 48 hex characters when minted). Verify each delivery by computing the HMAC of the raw body with this whole string as the key."}},"required":["secret"]}]},"WebhookSubscriptionPatch":{"type":"object","minProperties":1,"properties":{"url":{"type":"string","format":"uri","pattern":"^https://","description":"Public HTTPS destination. Private addresses and redirects are refused at delivery."},"events":{"type":"array","items":{"type":"string","enum":["call.started","call.connected","call.ended","conversation.started","conversation.message.created","conversation.handoff_requested","conversation.callback_requested","conversation.takeover.started","conversation.takeover.released","conversation.analyzed","conversation.ended","lead.captured"]},"description":"Replaces the event list; empty means all. An unknown name is refused with 400."},"secret":{"type":"string","minLength":1,"description":"A new signing secret. Write-only."},"enabled":{"type":"boolean","description":"False turns deliveries off; true turns them back on."},"mode":{"type":"string","enum":["live","test"],"description":"Accepted only when it is the subscription's current mode, so a read can be sent back; a change is refused."}}},"WhatsAppConnection":{"type":"object","properties":{"id":{"type":"string"},"org_id":{"type":"string"},"name":{"type":"string"},"waba_id":{"type":"string","description":"The WhatsApp Business Account id."},"api_version":{"type":"string"},"configured":{"type":"boolean","description":"Credentials are stored. False after a disconnect."},"enabled":{"type":"boolean","description":"False while paused: messages are not answered."},"webhook_path":{"type":"string","description":"The path of the callback URL to give Meta (on the API origin)."},"numbers":{"type":"array","items":{"$ref":"#/components/schemas/WhatsAppNumber"}}}},"WhatsAppNumber":{"type":"object","properties":{"id":{"type":"string"},"connection_id":{"type":"string"},"phone_number_id":{"type":"string"},"display_phone_number":{"type":"string"},"verified_name":{"type":["string","null"]},"assistant_id":{"type":["string","null"]},"enabled":{"type":"boolean"},"mode":{"type":"string","enum":["live","test"]},"failed_replies":{"type":"integer","description":"List only: replies that could not be sent."},"failed_turns":{"type":"integer","description":"List only: incoming messages the assistant failed to answer."}}},"WidgetKey":{"type":"object","properties":{"id":{"type":"string"},"org_id":{"type":"string"},"project_id":{"type":"string"},"assistant_id":{"type":["string","null"],"description":"The assistant a pinned key is bound to; null for a project-scoped key."},"key":{"type":"string","description":"Public, revocable widget key, `pk_live_...`."},"kind":{"type":"string","enum":["site","test"],"description":"site keys go on the live website; test keys on a staging site or localhost."},"allowed_origins":{"type":"array","items":{"type":"string"},"description":"Test keys: where the key loads. Site keys use the assistant's allowed_origins."},"revoked_at":{"type":["string","null"],"format":"date-time"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","org_id","project_id","assistant_id","key"]}},"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"Secret API key from the console (Settings > API keys): `Authorization: Bearer vak_...`"},"conversationToken":{"type":"http","scheme":"bearer","description":"Conversation token (`ct_...`) returned when a public chat conversation starts."}}}}