Error shape
All error responses are JSON withContent-Type: application/json:
invalid_request, invalid_client, invalid_grant, unsupported_grant_type, invalid_scope.
Status codes
Common error strings
Idempotency-Key
All generated OpenAPI POST operations and these standalone write endpoints accept an optionalIdempotency-Key request header: POST /api/create-checkout, POST /api/customer-portal, POST /api/notify, POST /api/user-prefs, and POST /api/notification-channels. Use a client-generated key for any POST that your client may retry after a network timeout or retryable 5xx.
Retrying a POST with the same key and an identical request body replays the original completed response instead of re-executing the operation. The replay contract reproduces the original status, body, and Content-Type; success responses echo Idempotency-Key and set Idempotent-Replayed: false on the first response or Idempotent-Replayed: true on replay. Keys are scoped per authenticated caller, fall back to the source IP for unauthenticated endpoints, and are retained for 24 hours after a completed response unless the endpoint documents a shorter replay window.
If the first request is still running, the retry returns 409 idempotency_conflict with Retry-After: 2; retry after that delay with the same key and identical body. If the same key is reused with a different request body, the API returns 422 idempotency_key_reused; do not retry unchanged, because the client must either send the original body or choose a new key for the new operation.
For mutations this avoids duplicating the side effect, including async enqueues such as POST /api/scenario/v1/run-scenario. For batch-read POSTs, replay returns the cached snapshot and that snapshot can be up to 24 hours stale. A 409 idempotency_conflict means the original keyed request is still running and keeps its in-flight lock until it completes. If the original execution itself returns a retryable status (408, 409, 429, or 5xx), that response is not cached and the processing lock is released, so a later retry may re-execute the operation and should only be sent when the endpoint contract is safe for that retry or the client can tolerate re-execution.
Retry strategy
Idempotent reads (GET): retry 429/5xx with exponential backoff (1s, 2s, 4s, cap 30s, 5 attempts). Most GET responses are cached at the edge, so the retry usually goes faster.
Writes: send Idempotency-Key on any POST you may retry. Never auto-retry most 4xx responses; the exception is 409 idempotency_conflict, which means a keyed request is still in flight and can be retried after Retry-After: 2 with the same key and identical body. 5xx responses are not cached for replay even when a key is present; retry with exponential backoff only when the endpoint contract is safe for a repeated execution or the client can tolerate that possibility. Without a key, inspect the endpoint contract before retrying because an unkeyed repeat can duplicate the side effect.
MCP: the server returns tool errors in the JSON-RPC result with isError: true and a text explanation — those are not HTTP errors. Handle them at the tool-call layer.
Debugging
- Every edge response includes
x-vercel-idandx-worldmonitor-deployheaders — include those when reporting issues. - Sentry alerts forward to status.worldmonitor.app.
GET /api/healthandGET /api/seed-healthshow per-seed freshness; a stale seed is the most common root cause of unexpected empty payloads.
