Every error, and what to do.
A handled request is always 200 { data, errors? }. Transport and auth problems use a status and a stable error code.
The shape
An operation's own failure (a validation error, a missing record) comes back in errors on a 200. Everything below comes back as the HTTP status with the same JSON body, the code in errors[0].extensions.code. Switch on the code, never on the message.
400
raw_query_forbiddenThe body carried a query. Raw GraphQL is never accepted; send an operationId.
What to do: Fix the request.
bad_requestNo string operationId, malformed variables, an empty or over-255-character Idempotency-Key, or an Idempotency-Key on a read.
What to do: Fix the request.
unknown_operationThe operationId is not in the catalog.
What to do: Check the id against the reference.
unknown_variableA variable the operation does not declare. A typo is refused rather than silently dropped.
What to do: Fix the variable name.
401
unauthorizedNo bearer header, or the key is invalid or revoked.
What to do: Use a valid key.
402
subscription_inactiveThe organization has no usable plan. Billing and profile reads still answer.
What to do: Restore the plan in the app.
403
insufficient_scopeThe key lacks the scope the operation requires.
What to do: Mint a key with that scope.
send_consent_not_attestedAn operation that messages a contact, before an org admin made the send consent attestation (setSendConsentAttestation).
What to do: An admin attests once.
outbound_pausedThe organization has paused all outbound in Settings; an API send is automation, which the pause holds.
What to do: Resume outbound in the app.
forbiddenA referenced record does not exist, was deleted, or is outside your organization. The gateway cannot tell those apart.
What to do: Check the id.
409
idempotency_conflictA request with this Idempotency-Key is still in flight.
What to do: Retry with the SAME key once it completes.
429
rate_limitedThe per-key limit for a rate-limited operation, or the per-IP throttle before auth.
What to do: Wait for Retry-After.
concurrency_limitToo many in-flight requests for this key.
What to do: Retry shortly.
500
internal_errorAn unexpected failure. Details are logged on our side and masked here.
What to do: Retry with backoff; reads and keyed writes are safe.
503
temporarily_disabledThe API is switched off for now.
What to do: Wait for Retry-After.
HTTP/1.1 429 Too Many Requests
Retry-After: 30
{
"errors": [
{ "message": "Rate limit exceeded.", "extensions": { "code": "rate_limited" } }
]
}Rate limits
Most operations are not metered. The expensive analytics reads are, per key: over the budget returns 429 rate_limited with a Retry-After header. Too many requests in flight at once returns 429 concurrency_limit. Each operation's reference page says whether it is rate-limited. Honor Retry-After exactly; the TypeScript SDK does.