Get started

Errors & limits

Every 4xx/5xx response is application/problem+json with a stable string code. Branch on the code — never on the human-readable title or detail.

Error codes

Every 4xx/5xx response carries application/problem+json with a stable string code extension. Branch on the code — never on title or detail strings.

CodeHTTPMeaningRemediation
auth.unauthorized401
Unauthorized
The request did not include valid credentials.
Send an `Authorization: Bearer <jwt>` header, or `X-Api-Key: pa_…` (secret) / `X-Api-Key: pk_…` (publishable) for an API key.
auth.forbidden403
Forbidden
The credentials are valid but lack permission for this operation.
Confirm the user owns the resource or has the required role.
auth.invalid_credentials401
Invalid credentials
Email/password combination did not match any account.
Verify the credentials. Repeated failures are throttled by the per-IP login limit.
auth.api_key_invalid401
API key invalid
The provided API key did not match a known prefix, or its hash did not validate.
Re-copy the key from the API keys page. If you regenerated the key, the old value is permanently invalid.
auth.api_key_disabled401
API key disabled
The API key has been disabled by an administrator.
Regenerate the key from the persona's settings, or contact support if the disable was unexpected.
auth.publishable_key_forbidden403
Publishable key not allowed
Publishable keys (pk_…) may only call chat, search, widget meta, subscription limits, and widget session start. This endpoint requires the persona's secret key.
Call this endpoint server-side with the secret key (pa_…). Never embed the secret key in public HTML.
auth.not_owner403
Not the resource owner
The authenticated account is not the owner of the requested resource.
Log out and sign in with the account that owns this resource.
auth.origin_not_allowed403
Origin not allowed
The request originated from a domain that is not in the account's allowlist.
Add the calling domain to the account's allowed origins, or contact support.
auth.consent_required403
Consent required
The end user has not granted the consent required for this operation.
Prompt the end user to complete the consent flow before retrying.
auth.user_not_initialized403
User not initialized
The authenticated account is missing required setup data.
Complete account onboarding (profile, billing, etc.) and retry.
limits.rate_exceeded429
Rate limit exceeded
Too many requests. Check the `Retry-After` header for the recommended wait time. The `extensions.scope` field hints at the partition (`user`, `ip`, `session`, `api_key_slot`).
Honor `Retry-After`. If `scope` is `api_key_slot`, switching to the other key slot may unblock immediately.
limits.concurrent_session429
Concurrent session limit
The account already has the maximum number of active sessions.
Close another active session or upgrade the subscription tier.
limits.waiting_room_full503
Waiting room full
The waiting room queue is at capacity.
Back off and retry after a short delay.
limits.quota_exceeded402
Quota exceeded
The account has consumed its allotted quota (minutes, knowledge units, storage, etc.).
Upgrade the subscription tier or wait for the next billing cycle.
limits.subscription_required402
Subscription required
This operation requires an active paid subscription.
Purchase a subscription on the pricing page.
limits.session_time_limit409
Session time-of-day limit
Sessions cannot start at the current time per the persona's schedule.
Retry within the persona's configured availability window.
limits.session_interaction_limit429
Session interaction limit (WebSocket close 4005)
Returned only as WebSocket close 4005, never as HTTP. The live session reached the persona's per-session interaction cap (PublishSettings.SessionInteractionLimit, counted in billing units — Full mode = 2 per turn, Chat mode = 1).
End-user UI should show a session-ended message; starting a new session is allowed (subject to cross-session limits).
limits.session_duration_limit409
Session duration limit (WebSocket close 4006)
Returned only as WebSocket close 4006, never as HTTP. The live session exceeded the persona's per-session duration cap (PublishSettings.SessionDurationLimitSeconds, wall-clock — includes Standby/idle time).
End-user UI should show a session-ended message; starting a new session is allowed (subject to cross-session limits).
upload.file_too_large413
File too large
The uploaded file exceeds the per-endpoint size limit.
Compress the file or split it. See the endpoint reference for per-endpoint size caps.
upload.invalid_signature400
Invalid file signature
The file content does not match the declared content type (e.g. an `.exe` renamed to `.pdf`).
Verify the file is genuinely the type its extension claims.
upload.unsupported_type415
Unsupported file type
The file's content type is not accepted by this endpoint.
Convert the file to one of the supported types listed in the endpoint reference.
upload.page_limit_exceeded413
Page limit exceeded
The document exceeds the maximum page count for ingestion.
Split the document into smaller files.
resource.not_found404
Resource not found
No resource exists at the addressed URL, or it is not visible to the caller.
Verify the ID, then the caller's permissions.
resource.conflict409
Resource conflict
The operation conflicts with the resource's current state (duplicate, version skew, etc.).
Re-read the resource and retry with current state.
validation.failed400
Validation failed
One or more request fields failed validation. The `errors` extension contains per-field details.
Inspect `extensions.errors` and correct the highlighted fields.
server.error500
Server error
An unexpected server-side error occurred.
Retry with backoff. If the failure persists, contact support with the response's `traceId` extension if present.

Rate limits

When a limit is exceeded the response is 429 Too Many Requests with a Retry-After header (seconds) and ProblemDetails body code = limits.rate_exceeded. The extensions.scope field tells you whether the partition was per-user, per-IP, or per-API-key-slot.

PolicyLimitWindowPartitionPurpose
AuthSensitive530 secondsper IP
scope: ip
Login, register, password reset, email confirmation. Strict per-IP limit so credential-stuffing campaigns from a single source get throttled fast.
AuthRefresh301 minuteper IP
scope: ip
Refresh-token exchange. Looser than AuthSensitive but still per-IP to bound rotation abuse.
PersonaUploads201 minutepersona_or_user_or_ip
scope: persona
Persona texture/asset uploads. Per-user when authenticated, per-IP otherwise.
KnowledgeUploadsRate
internal services bypass
601 minutepersona_or_user_or_ip
scope: persona
Knowledge document uploads (PDF/DOCX/etc.). Limit is sourced from KnowledgeAcceptorSettings.RateLimit so the same knob controls accept-time check and throttle.
SubscriptionChange510 minutespersona_or_user_or_ip
scope: persona
Subscription tier changes and cancellations. Tight to prevent oscillation abuse.
MutationDefault
internal services bypass
3001 minutepersona_or_user_or_ip
scope: persona
Generic per-user safety net for state-changing endpoints (POST/PUT/PATCH/DELETE) without a stricter category-specific policy. Loose by design — never bites a real user, catches scripted abuse.
Global
internal services bypass
3001 minuteper IP
scope: ip
Per-IP safety net applied to every request. Loose by design — never bites a real user, catches scripted abuse on untagged endpoints. Stricter category-specific policies apply on top.
SessionSnapshot301 minuteper session
scope: session
Snapshot uploads per active session (matches expected camera-capture cadence with headroom). Partitioned by sessionId.
SessionRead2401 minuteper session
scope: session
Per-session image / snapshot reads. Looser than upload — the same client may pull many cached assets.
SessionWebSocket301 minuteper IP
scope: ip
WebSocket upgrade attempts per source IP. Bounds concurrent-connection abuse before a session is fully established.
V1Search1201 minuteapi_key
scope: api_key
Knowledge Search (POST /v1/search) per persona API key. Quota (0.5 interaction per call) bites first for sustained traffic; this bounds bursts.
V1Chat601 minuteapi_key
scope: api_key
Headless chat (POST /v1/chat) per persona API key. Quota (1 interaction per call) and the per-session turn lock bite first for sustained traffic; this bounds bursts.
WaitingRoomQueue301 minuteper user (per IP if anonymous)
scope: user
Queue join / finalize / cancel actions. Per-user when authenticated, per-IP for anonymous guests so shared egress (corporate proxy, mobile CGN) doesn't share a bucket between legitimate users.
WaitingRoomQueueWs601 minuteper IP
scope: ip
WebSocket upgrade attempts for queue position polling. Per-IP so one host can't open thousands of long-lived sockets to exhaust capacity.