Skip to main content

Errors

Every error response carries a single typed envelope under an error key. Branch on type, not HTTP status.

Shape

Fields

Types (branch on these)

Selected codes worth knowing

Retry rules

Never silently retry invalid_request / authentication / permission / not_found / conflict / idempotency_conflict / unprocessable — they’re deterministic.

Rate limiting

  • Surfaced as 429 rate_limited with Retry-After header (seconds).
  • Applied per API key and per endpoint (e.g. exports are individually rate-capped).
  • Slow down, don’t hammer.
  • The RateLimit-* response headers standardized in RFC 9240 are not shipped yet. Don’t read them. Use Retry-After and the error’s retry_after_ms.

Logging & support

  • Always log request_id on every error. When you email support@moda.app, include it.
  • Log type and code; don’t log message as the signal (it can change without notice).
  • Redact the API key from any log line.

Error handling template (TypeScript)

Error handling template (Python)

Common wrong guesses

  • Branching on HTTP status only. Status codes collapse types (both rate_limited and idempotency_conflict can be 409-adjacent in practice). Branch on type.
  • Parsing message. Can change without notice. Use type and code for logic; message for display only.
  • Retrying not_found / permission. Deterministic.
  • Ignoring Retry-After on 429. You’ll be back to rate-limited in seconds.
  • Expecting RateLimit-Limit / RateLimit-Remaining / RateLimit-Reset response headers. Not shipped yet. Use Retry-After and retry_after_ms.
  • Not including request_id in support requests. Without it, logs are hard to find.

Upstream