Errors

GET /v1/*

Error envelope, retryable vs final, common codes.

Errors return as { "error": { "code": <string>, "message": <string>, "details"?: <object> } } with the corresponding HTTP status. 4xx codes are not retryable as-is — fix the request and resubmit with a new idempotency key. 5xx codes are retryable with the *same* idempotency key.

Common codes: INVALID_JSON (body not parseable) → 400; INVALID_INPUT (zod schema mismatch, details.issues carries the path + reason) → 400; UNAUTHENTICATED (missing / malformed bearer) → 401; FORBIDDEN (key valid but tenant not entitled to the route) → 403; NOT_FOUND → 404; IDEMPOTENCY_CONFLICT (key replay with different body) → 409; RATE_LIMITED → 429; INTERNAL → 500; UPSTREAM (transient Postgres / Hyperdrive) → 503.

Every error response carries a X-Iso-Compliant-Trace-Id header — quote it when filing a support ticket.

A 200 OK with X-Iso-Compliant-Idempotent-Replay: true is a cache hit, not an error. The body is identical to the original response (including timestamps).

Idempotency

Mandatory header `Idempotency-Key` (UUID or other opaque ≤64 char string). A second request with the same key and the same body returns the cached response and the header `X-Iso-Compliant-Idempotent-Replay: true`. A second request with the same key but a different body returns 409.

Rate limit

Sandbox: 60 requests / minute, 1000 / day. Production: 600 requests / minute soft cap, lifted per tenant on request.

← All docs