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": "string",
  "message": "string",
  "details": { "...optional structured context..." }
}
  • 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 unauthorized or forbidden_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

401 application/json
{
  "code": "unauthorized",
  "message": "invalid credentials"
}

Response

400 application/json
{
  "code": "INVALID_REQUEST",
  "message": "missing required field",
  "details": {
    "reason": "missing_required_field",
    "field": "amount.value"
  }
}

Response

412 application/json
{
  "code": "precondition_failed",
  "message": "If-Match did not match current resource version",
  "details": {
    "current_etag": "v3:01HZ9X..."
  }
}