Core concepts

Errors

The standard error envelope and every HTTP status the API returns.

Errors use standard HTTP status codes. Unlike successful responses, an error is not wrapped in a data envelope — it is a flat object you can read directly.

Error shape

Every error carries statusCode, a human-readable message, the requestId (quote it in support requests), a timestamp and the request path. Validation errors also include a fields map of per-field messages.

json · 400 validation error
{
  "statusCode": 400,
  "message": "Validation failed",
  "error": "Bad Request",
  "fields": {
    "email": ["email must be an email"],
    "password": ["password must be longer than or equal to 8 characters"]
  },
  "requestId": "req_9f3a...",
  "timestamp": "2026-09-06T09:00:00.000Z",
  "path": "/api/v1/auth/login"
}

message may be a single string or an array of strings when several validation messages apply. In production, server errors never include a stack trace.

Status codes

StatusMeaning
400 Bad RequestValidation failed — a missing/invalid field, an unknown field (bodies are strictly whitelisted), or a bad relation. Check fields.
401 UnauthorizedMissing, expired or invalid access token. Log in again or refresh.
403 ForbiddenAuthenticated, but your role or company scope does not permit this action.
404 Not FoundThe resource does not exist, or is outside your company scope.
409 ConflictA uniqueness conflict (e.g. a duplicate code), or an idempotent replay of an already-applied write (idempotentReplay: true).
412 Precondition FailedA dependent capability is not configured (for example, starting a payment-gateway checkout when no gateway is set up).
429 Too Many RequestsA rate limit was exceeded — see rate limits.
500 Internal Server ErrorAn unexpected server error. Retry later and quote the requestId.
504 Gateway TimeoutThe request exceeded the server’s per-request time budget. Retry, or split the work.

Idempotent replays

When you retry a write that carried an Idempotency-Key and the original already succeeded, the API returns 409 with idempotentReplay: true. This is not a failure — the change was applied exactly once. Treat it as success and stop retrying.

json · 409 idempotent replay
{
  "statusCode": 409,
  "message": "This change was already applied.",
  "idempotentReplay": true,
  "requestId": "req_...",
  "timestamp": "2026-09-06T09:00:00.000Z",
  "path": "/api/v1/customers"
}

Always log the requestId

Every response — success or error — carries a requestId (in the error body, in meta on success, and in the X-Request-Id response header). Logging it makes any issue far faster to trace.