Skip to main content

The envelope

Every failure, whatever the status, is one JSON object:
  • code is stable and machine-readable. Branch on it, not on message.
  • message is a human-readable explanation and may change.
  • request_id is the UUID of the request. It is also returned as the X-Request-ID header on every response, success or failure. Quote it when reporting a problem.
On the streaming endpoint a failure that happens after the stream has started arrives as an error event with the same three fields; see Streaming.

Codes

What to retry

idempotency_in_progress, conversation_busy, rate_limit_exceeded, api_rate_limit_unavailable, service_unavailable. Reusing the key guarantees you never pay twice for the same message.
validation_error (read message; it names the offending field), idempotency_conflict (use a new key for the new body), permission_denied (use a full key), agent_mismatch (use the agent the key belongs to).
invalid_api_key (the key was deleted or the secret is wrong), agent_disabled, agent_not_found, insufficient_balance (top up the balance first). A retry loop on these only burns the rate limit.
internal_error. Retrying once with the same Idempotency-Key is safe; if it persists, send us the request_id.

A note on 401

The API does not distinguish a deleted key from one that never existed: both are invalid_api_key. If a key that used to work starts returning 401, check the API access card in the app; it was probably rotated or deleted.