API error codes

Every failure from the public API is an RFC 9457 problem document. Its code member is stable and untranslated, so branch on the code rather than on the human-readable detail.

The problem document

Responses use the application/problem+json content type and carry type, title, status, code, and detail. The type member links back to this page, anchored at the code. The detail member names the specific reason a call was refused — a full seat plan, an unknown role id — so it is what to read when deciding what to do next, while code stays the thing to branch on. When the edge supplies a ray id it is echoed as requestId, which is the value to quote in a support request, and validation failures add an errors array with one entry per rejected value.

Codes

The HTTP status is derived from the code, so a client can rely on either. Treat an unrecognized code as a server error.

CodeHTTP statusMeaning
INPUT_PARSE_ERROR400The body, query, or path parameters failed schema validation. The errors array locates each rejected value and names the constraint it violated.
BAD_REQUEST400The request was understood but is not valid in this context.
NOT_AUTHORIZED401No credential was presented, or it is malformed, expired, or revoked. The WWW-Authenticate header points at the resource metadata an OAuth client needs to start the connect flow.
FORBIDDEN403The credential is valid but lacks the required scope, is a team-scoped key used outside its own team or on an account-level operation, its owner lacks the team permission this operation needs, or a plan limit such as the team's seat count blocks it. The detail says which.
NOT_FOUND404The addressed resource does not exist, or is not visible to this credential.
CONFLICT409The request conflicts with the current state of the resource.
PRECONDITION_FAILED409A rule of the operation was not met — a plan limit, or an invitation that was already used, for example.
RATE_LIMITED429Too many requests for this credential or IP address. Authenticated credentials allow 300 requests per 60 seconds; failed authentication attempts use a separate per-IP limit. Wait the number of seconds given in retry-after.
INTERNAL_SERVER_ERROR500Something failed on the server. Details are deliberately omitted; retry, and quote requestId if it keeps happening.

Field errors

An INPUT_PARSE_ERROR carries an errors array with one entry per rejected value. Each entry locates the value and names what was wrong with it: in says which part of the request it came from, using the same vocabulary as an OpenAPI parameter (body, query, path, header, cookie); pointer is an RFC 6901 JSON Pointer, so /scopes/1 addresses the second element and an empty string addresses the whole payload; code is a stable, untranslated identifier from the table below; and params carries the constraint that was violated, such as {"min": 2}, when the code has one. Codes describe the shape of the failure rather than any user-facing copy, so they are safe to branch on.

CodeMeaning
requiredThe value is absent. Send the member, or omit the request until you can.
invalid_typeThe value is of the wrong type. params.expected names the JSON Schema type the operation documents.
invalid_valueThe value is the right type but not one the operation accepts — a value outside an enumeration, or one rejected by a rule specific to this operation. The OpenAPI document lists the permitted values.
invalid_formatThe value does not match the format the member requires. params.format names it, such as email.
min_lengthThe string or array is shorter than allowed. params.min is the minimum.
max_lengthThe string or array is longer than allowed. params.max is the maximum.
min_valueThe number is below the allowed range. params.min is the minimum.
max_valueThe number is above the allowed range. params.max is the maximum.

Retrying safely

429 and 5xx responses are worth retrying with backoff, honoring retry-after when it is present. Better still, pace off RateLimit-Remaining and RateLimit-Reset, which every response carries, and slow down before the limit is reached. Other 4xx codes will fail again unchanged and need a different request, a different credential, or a scope the credential does not yet hold.