Skip to main content
Every Puntego worker endpoint speaks the same error language: a predictable JSON envelope plus a standard HTTP status code. Read the status to decide how to react, and read the reason string to know exactly what happened.

The error envelope

A failed request returns a small JSON body with a single error 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.
Some responses add a context field next to 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

A 429 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.
The fix for a 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

The requesting origin is not on the workspace allowlist. Add the exact origin in Dashboard → Settings → Domains, then reload. See domains.
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.
The visitor JWT is missing, malformed, or past its roughly 15-minute lifetime. Re-run /boot to mint a fresh session and retry. See authentication.
A limiter tripped. Read the bucket field to see which one, wait for the Retry-After interval, then retry. See rate limits.
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 error string for precise messaging.
  • Treat 401 on /chat as “refresh the session”, not “fail” — re-boot and retry once.
  • Always honor Retry-After on 429; never retry tighter than the header asks.
  • Treat 5xx as transient and retry with backoff; treat 400 and 403 as 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.