Errors
Every error response on the public API uses the same JSON envelope, so
an integration can write one parser that handles every failure mode.
HTTP status carries the broad category; the body carries a stable
machine-readable code, a human message, and an optional details
object with structured context.
Error Envelope
Every error response carries the same shape, regardless of the HTTP status:
code— stable, machine-readable identifier for the error. Use this in your branching logic.message— human-readable description, suitable for logs or developer tooling. Not localized; not safe to surface to end users verbatim.details— optional object with error-specific context (the failing field, the conflicting resource id, the precondition that did not match, and so on).
A machine-readable catalog of all currently-defined error shapes is
published at
/errors.json for tooling that wants to
consume it directly.
HTTP Status Mapping
The HTTP status tells you which kind of failure happened. The same
status can carry different code values depending on the situation,
but the high-level meaning is fixed:
| Status | Meaning | Typical causes |
|---|---|---|
400 Bad Request |
The request payload, query, or parameter shape is invalid. | Missing required field, malformed JSON, bad enum value. |
401 Unauthorized |
Authentication failed. | Missing, expired, or invalid bearer token. |
403 Forbidden |
Authenticated, but not allowed to perform this operation in this scope. | Capability not granted, scope outside granted authority. |
404 Not Found |
The referenced resource does not exist or is not visible to the caller. | Wrong id, resource in another tenant, resource retired. |
409 Conflict |
The request conflicts with the current state of the resource. | Idempotency-key mismatch, lifecycle precondition not met. |
412 Precondition Failed |
The If-Match ETag did not match the current resource version. |
Concurrent update; re-read and retry. |
502 Bad Gateway |
An upstream system the platform depends on returned an error. | Transient infrastructure issue; safe to retry. |
503 Service Unavailable |
Operation temporarily not available. | Maintenance, load shedding; retry with backoff. |
For idempotency-related conflicts, see Idempotency And Concurrency.
Code Conventions
code values are stable strings. The platform mixes two casings in the
current contract:
- short snake_case strings such as
unauthorizedorforbidden_capability_scope - screaming-snake-case strings such as
INVALID_REQUEST
Both are stable identifiers — match on the exact string, not on casing. New codes prefer snake_case going forward.
How This Appears In The API
Response
{
"code": "unauthorized",
"message": "invalid credentials"
}Response
{
"code": "INVALID_REQUEST",
"message": "missing required field",
"details": {
"reason": "missing_required_field",
"field": "amount.value"
}
}Response
{
"code": "precondition_failed",
"message": "If-Match did not match current resource version",
"details": {
"current_etag": "v3:01HZ9X..."
}
}