Skip to main content

Errors

Every error uses the same envelope. The HTTP status is authoritative; the code field lets you branch on specific conditions without parsing English text.

Envelope

{
  "error": {
    "type": "invalid_request_error" | "permission_denied" | "rate_limit_error" | "server_error",
    "code": "VALIDATION_ERROR",
    "message": "Human-readable summary of the first issue",
    "param": "email",                    // optional: which field failed
    "issues": [                          // optional: full field-level list
      { "field": "email", "message": "Invalid email" }
    ]
  },
  "meta": {
    "request_id": "b28f10a3-..."         // include when contacting support
  }
}

HTTP status mapping

  • 2xx. success.
  • 400. validation error, malformed body or query.
  • 401. missing or invalid API key.
  • 403. key valid but lacks scope, or tenant hit PAYG limit.
  • 404. resource not found (or belongs to another tenant. response is identical to avoid enumeration).
  • 405. endpoint doesn't support this method.
  • 409. Idempotency-Key reused with different body.
  • 429. rate-limited or auth-throttled.
  • 5xx. server-side. Include the request_id in support tickets so we can trace.

All error codes

CodeHTTPMeaning
INVALID_API_KEY401Missing, invalid, revoked or expired API key
AUTH_RATE_LIMITED429Too many failed auth attempts from this IP
SCOPE_INSUFFICIENT403API key does not grant the required scope
VALIDATION_ERROR400Request body/query failed Zod validation (see issues array)
RESOURCE_NOT_FOUND404Resource id does not exist or belongs to another tenant
METHOD_NOT_ALLOWED405Endpoint does not support this HTTP method
INVALID_STATE422Resource is in a state that does not allow this operation (e.g. sent quote cannot be edited)
PAYG_LIMIT403Free plan resource limit reached (upgrade required)
IDEMPOTENCY_KEY_TOO_LONG400Idempotency-Key header exceeds 128 chars
IDEMPOTENCY_KEY_CONFLICT409Same Idempotency-Key used with different body
IDEMPOTENCY_KEY_QUOTA_EXCEEDED429Too many unique Idempotency-Keys today
RATE_LIMITED429Request rate exceeded (per-key or global backstop)
REQUEST_TOO_LARGE413Request body exceeds 1 MB (Workspace API cap)
MIDDLEWARE_ORDER500Server misconfiguration. report to support
INTERNAL500Unexpected server error. check request_id in support ticket

Soft-delete + recycle bin

DELETE endpoints use a 30-day recycle bin. Resources are hidden from default list queries but recoverable via POST /:id/restore. After 30 days a daily purge cron hard-deletes the row for good (including nullifying FK links and purging attachments).

Requires a separate .delete scope — not covered by .write. This makes destructive access an explicit grant per key. See Scopes.

Request-ID for support

Every response includes an X-Request-Id header matching the meta.request_id field. When opening a support ticket, always include this. we use it to look up the exact log line.

Errors - Workspace API - Staffify