The error envelope
A failed request returns a small JSON body with a singleerror field. The value is a namespaced string in the form <namespace>/<reason>, where the namespace is usually the endpoint family (boot, chat, consent, voice, and so on).
Error response
string
A namespaced reason string,
<namespace>/<reason>. Branch on the namespace for routing and on the full string for precise handling.error (for example a human-readable message on plan-limit responses), but error is always present and is the field to switch on.
Rate-limit responses are shaped differently
A429 response carries an extra bucket field that names the limiter that tripped, and the error is always rate_limit/exceeded. Every 429 also includes a Retry-After header in seconds.
Rate-limit response
See rate limits for the buckets, their windows, and how to back off on
Retry-After.Status codes
These are the status codes the worker returns, with representative reason strings for each. Branch on the status first; use the reason for detail.Authentication errors: /boot versus /chat
These two endpoints handle a missing or invalid identity differently, and the distinction is intentional./boot never returns 401. Boot is where a visitor session is minted, so it has no Bearer token to reject. Origin and visitor problems surface as 403 instead (for example boot/origin_not_allowed or boot/invalid_visitor_id)./chat returns 401 when the Authorization: Bearer <visitor-jwt> header is missing or invalid (chat/unauthorized), and also when the token has expired (jwt_expired). The visitor JWT minted at /boot has roughly a 15-minute lifetime, so a long-lived tab can hit an expired token.401 on /chat is to call /boot again to refresh the visitor session, then retry the chat request with the new token. See authentication for the full token lifecycle.
Common situations
403 boot/origin_not_allowed
403 boot/origin_not_allowed
The requesting origin is not on the workspace allowlist. Add the exact origin in Dashboard → Settings → Domains, then reload. See domains.
403 boot/tenant_not_enabled or chat/tenant_not_enabled
403 boot/tenant_not_enabled or chat/tenant_not_enabled
The workspace is not enabled for this surface yet. During a gated rollout, chat is allowed only for workspaces on the production allowlist; confirm the workspace is enabled before sending traffic.
429 rate_limit/exceeded
429 rate_limit/exceeded
A limiter tripped. Read the
bucket field to see which one, wait for the Retry-After interval, then retry. See rate limits.503 boot/killswitch
503 boot/killswitch
The global killswitch is active.
/boot returns 503 and the embed silently no-ops on the page, so visitors see no broken UI. Traffic resumes automatically once the killswitch is cleared.Handling errors well
- Switch on the HTTP status for control flow, and on the
errorstring for precise messaging. - Treat
401on/chatas “refresh the session”, not “fail” — re-boot and retry once. - Always honor
Retry-Afteron429; never retry tighter than the header asks. - Treat
5xxas transient and retry with backoff; treat400and403as terminal until the request or configuration changes.
Authentication
How the visitor JWT is minted at boot and refreshed before it expires.
Rate limits
The buckets, windows, and how to back off cleanly on Retry-After.