The envelope
Every non-2xx response, and every error item inside a batch200, has the same two fields:
error is a stable code for your code to branch on; message is the human-readable reason, the same text this documentation quotes. Internal faults add request_id:
Statuses
Codes
Theerror field carries one of these codes. Each section gives the status, what happened, and what to do.
invalid_request
400. A validation failure. The message names the field, with its path for nested ones (Received unknown parameter: rewards[0].window_second).
Fix the field the message names. Field names are snake_case and match the contract. See A request is rejected for an unknown field.
invalid_json
400. The body could not be parsed (Invalid JSON body).
Check the serializer and the Content-Type header.
unauthorized
401. Missing or invalid API key, or API key has been revoked.
Send Authorization: Bearer <key> with a current key. After a rotation, deploy the new key before revoking the old.
forbidden
403. Origin not allowed for this key: a publishable key from an origin not on its allowlist. Or a publishable key on a secret-only endpoint: Declare requires a secret key.
Add the exact https:// origin to the key in the dashboard, or use a secret key from a server.
not_found
404. No such path.
Use /v1/decide, /v1/declare, /v1/events or /v1/identify.
method_not_allowed
405. Only POST is accepted.
Use POST.
conflict
409. State conflicts with the request. Three cases: No live experiment for element "…" under a publishable key; Identity conflicts with an existing user on identify; Experiment already has 50 active arms.
Declare the element with a secret key first; resolve the identity link on your side; conclude an arm before adding another. Do not retry. See The API returns 409 conflict.
lock_timeout
409. Identity changed during selection; retry the request: the user’s identity was being linked while this decision was selecting an arm. The one retryable 409.
Retry the same request once. See The API returns 409 conflict.
idempotency_error
409. idempotency_key was already used with a different request.
Generate a new key for a new logical request; reuse a key only for a byte-for-byte retry. See Idempotency.
rate_limit_exceeded
429. Over 1,000 requests per minute per endpoint for the organization.
Wait Retry-After seconds, then retry with the same keys. Batch events. See Rate limits.
internal_error
500, 502 or another 5xx. A sanitized fault on our side, or the API edge could not complete the call. Carries request_id.
For decide, serve your fallback and retry only with the same idempotency_key. For events, retry with the same id. For identify, retry the same request. Quote request_id to support.
Batch items
In a batch, a failing item is reported inside the200 at its index with the same shape:
Ambiguous failures
A502, any other 5xx from the API edge, or a dropped connection means the request may or may not have been applied. The message says so and names the safe retry:
decide, and retries events only under their stable ids.
X-Request-Id
Every response, success or failure, carries an X-Request-Id header: the id of that request on our side. Quote it when you contact support and we can find the exact request in our logs. Internal faults repeat it in the body as request_id. Browsers can read the header; it is exposed through CORS. The SDK surfaces it as requestId on every result and error.
In the server SDK
The SDK maps these codes and statuses onto its own stable codes with aretryable flag, and never exposes a raw body. The mapping is on Errors in the server SDK.