Skip to main content
Every failure has the same shape:
Branch on code, not on the HTTP status and not on the message. The status is what a proxy needs; the code is what your integration needs. Messages are written for people and may be reworded. request_id is also on the x-request-id header. Quote it when you ask us about a call — it is how we find that exact request in our logs. Every /v1 response carries x-request-id, not only the failures — a 200, a 202, an SSE stream. The calls worth asking about are often the ones that succeeded and did something surprising, and those need a handle too. Log the header next to whatever your integration records about the call; it costs nothing until the day it is the only thing that helps.

The codes

A 401 also carries WWW-Authenticate: Bearer realm="gobare", error="…" — the standard challenge, so an HTTP client learns the scheme without being configured for it. The Retry column is also on the wire. Every code marked retryable carries a Retry-After header; the ones marked no carry none, and the absence is the signal — project_limit_exceeded is a 429 and provider_unauthorized is a 502, and neither is a wait. A client that honours Retry-After and gives up without one is doing the right thing on every row below without knowing any of them. The numbers are a floor on politeness rather than a prediction. Where we can compute the real wait — the rate limiter, the stream ceiling — we send that instead.

Refusals worth recognising

Several invalid_request refusals exist specifically so a value is never accepted and then ignored. Each names what to do: An environment.profiles id belonging to another organization answers not_found, not permission_denied — an id cannot be probed for existence.

The three 429s

They share a status and recover differently, which is exactly why the code matters more than the status:
  • rate_limit_exceeded — you are asking too fast. Retry-After says how long to wait, and honouring it works. See limits.md.
  • queue_full — this session is saturated. Wait for its turns to drain, or use another session.
  • project_limit_exceeded — you are at the ceiling for concurrent sessions. Retrying never succeeds. Delete something or talk to us.
A client that retries all 429s identically will spin forever on the third.

Internal errors

An unexpected failure is reported as internal_error with a fixed message. Internal errors routinely carry file paths, SQL and provider responses, and none of that belongs in an external response. The real message is in our logs, findable by the request_id you were handed. If you see one, it is worth reporting.

Errors from outside the API

/v1 always answers JSON in this envelope, including 404 for an unknown path — so a JSON parse failure means you did not reach /v1 at all. Check the host and the /v1 prefix. A 413 from an intermediate proxy is likewise not us; our own body ceiling answers invalid_request with 400.

Next

  • input — what each input event accepts, and when each refusal happens
  • troubleshooting — a symptom rather than a code
  • limits — the ceilings behind the 429s