# Error Recovery

Status codes and how to respond to them.

## Status code matrix

| Code | Meaning | Action |
|------|---------|--------|
| 200 | Success | - |
| 400 | Bad Request | Do not retry - fix the request body |
| 401 | Unauthorized | API key invalid or revoked; rotate |
| 402 | Payment Required | Balance/budget check failed (insufficient balance, or monthly key budget exceeded); top up or raise budget |
| 403 | Forbidden | Key disabled, or a balance/registration gate denied the call |
| 404 | Not Found | Resource missing (or not yours - video tasks 404 cross-merchant); do not retry |
| 409 | Conflict | Resource state conflict (e.g., cancel of a running/succeeded video task) |
| 429 | Too Many Requests | Per-key RPM limit hit; honor `Retry-After`; back off |
| 500 | Internal Server Error | Retry once with backoff; report if persistent |
| 502 | Bad Gateway | Upstream failed / all channels exhausted; retry with backoff |
| 503 | Service Unavailable | Temporary; retry with backoff |
| 504 | Gateway Timeout | Upstream slow; retry once with longer timeout |

Other upstream 4xx codes (e.g. 413, 422) are passed through verbatim
with the upstream body when the provider rejects the input - do not
retry those either.

Error bodies are `{"error": {"message": "...", "type": "..."}}` where
`type` is a stable machine-readable string: `rate_limit_exceeded` (429),
`monthly_budget_exceeded` (402), etc. Branch on `type`, not on prose.

## `Retry-After`

On a 429 from the per-key rate limit, the gateway sets `Retry-After: 60`
(seconds - the worst-case wait for the sliding window to drain). Always
honor it before retrying.

## Idempotency

Most endpoints are not idempotent - retrying a successful chat call
incurs the cost twice. Video submissions are not deduplicated either:
a duplicate `POST /v1/video/generations` creates a new task and a new
charge. To deduplicate, store the returned `task_id` against your own
idempotency key before retrying a submission.

## Exponential backoff

Recommended curve for retried errors (429, 502/503/504):

1. First retry: wait `Retry-After` seconds (this gateway sends a fixed `60`)
2. Second retry: 2x previous (120s)
3. Third retry: 2x previous (240s), capped at 300s
4. After 3 retries, give up and surface the error to the caller

Add +/-20% jitter to avoid thundering-herd.
