Errors and limits
Handle API errors, idempotency, rate limits, and WebSocket close codes.
HTTP and SSE errors
Every /v1 error uses the following stable shape, including authentication and validation failures.
{
"code": "RATE_LIMITED",
"message": "Request rate limit exceeded",
"details": null,
"request_id": "req_01K...",
"retryable": true
}In SSE, the same data is sent in the terminal error event. Always retain request_id for investigation.
| Code | Typical HTTP | Retry? | Action |
|---|---|---|---|
INVALID_API_KEY | 401 | No | Check the key, its checksum, and the Authorization header. Deleted keys also return this code. |
API_KEY_EXPIRED | 401 | No | The key expired or its rotation grace period ended; use the replacement key. |
API_KEY_REVOKED | 401 | No | Use an active credential. |
INSUFFICIENT_SCOPE | 403 | No | Create a key with the required scope. |
IP_NOT_ALLOWED | 403 | No | Call from an IP in the key's allowlist. |
ASSISTANT_INACTIVE | 403 | No | Reactivate the assistant. |
FEATURE_NOT_AVAILABLE | 403 | No | Check the owner's plan, feature access, and requested model. |
FILE_UPLOAD_DISABLED | 403 | No | Enable file uploads for the assistant. |
VOICE_DISABLED | 403 | No | Enable voice for the assistant. |
ACTOR_CONFLICT | 409 | No | Use a different external_user_id. |
INSUFFICIENT_CREDITS | 429 | Not immediately | The owner must add credits. |
RATE_LIMITED | 429 | Yes | Wait for Retry-After. |
IDEMPOTENCY_CONFLICT | 409 | No | Use a new key or the original body. |
REQUEST_IN_PROGRESS | 409 | Yes | Wait, then retry the same key and body. |
CONVERSATION_NOT_FOUND | 404 | No | Check assistant, external user, and UUID. |
MESSAGE_NOT_FOUND | 404 | No | Check the message ID and external user. |
FILE_NOT_FOUND | 404 | No | Upload the file for the same external user; details.file_id names it. |
RESPONSE_NOT_FOUND | 404 | No | Check the ID and external user; retry after the response finishes. |
INVALID_CURSOR | 400 | No | Pass last_id from the same conversation as after. |
VALIDATION_ERROR | 422 | No | Fix the request; details.errors lists the fields. |
UNSUPPORTED_FILE_TYPE | 415 | No | Upload a supported type whose contents match its extension. |
FILE_TOO_LARGE | 413 | No | Upload a file of at most 25 MiB. |
INVALID_AUDIO_FORMAT | 400 | No | Use a supported codec, rate, and channel count. |
CONTEXT_TOO_LARGE | 400 | No | Start a new conversation. |
UPSTREAM_UNAVAILABLE | 503 | Yes | Back off and retry with a limit. |
RATE_LIMIT_UNAVAILABLE | 503 | Yes | Back off and retry; the API fails closed. |
INTERNAL_SERVER_ERROR | 500 | Yes | Retry with backoff and report the request_id. |
Retry only when retryable is true or this documentation explicitly permits it. Use exponential backoff with jitter, cap the number of attempts, and keep the same idempotency key for the same logical operation.
Rate limits
Responses can include:
| Header | Meaning |
|---|---|
RateLimit-Limit | Maximum requests in the current window. |
RateLimit-Remaining | Requests remaining. |
RateLimit-Reset | Seconds until the current window resets. |
Retry-After | Seconds to wait after a 429. |
X-Request-ID | Identifier for tracing and support. |
Limits are shared by all keys of an assistant, so adding keys does not add capacity. Default limits:
| Bucket | Limit |
|---|---|
| All API requests | 120/minute |
| Chat responses | 30/minute |
| Voice session creation | 20/minute |
The Enterprise plan allows 600 requests, 120 chat responses, and 60 voice sessions per minute. Limits use a sliding one-minute window. Read response headers instead of hard-coding these values.
WebSocket errors
{
"type": "error",
"sequence": 8,
"error": {
"code": "INVALID_EVENT_ORDER",
"message": "Audio frames require input_audio.start",
"retryable": false
}
}Close codes:
| Code | Meaning |
|---|---|
1000 | Normal completion. |
1002 | Protocol violation. |
1008 | Authentication, policy, ordering, or limit violation. |
1009 | JSON or binary frame is too large. |
1011 | Provider or server failure. |
Never reconnect with the same session token. For retryable failures, request a new token from your backend and open a new connection. For 1008, fix the credential, origin, event order, or limit issue first.
Contacting support
Send the endpoint, timestamp, HTTP status or close code, request_id, session_id, and public credential prefix. Never send the full API key, session token, raw audio, or unnecessary personal data.