Why idempotency keys?
Network failures and timeouts can leave you uncertain whether aPOST succeeded. If you retry a generation request and the original call had actually completed, you’d be charged twice. Idempotency keys let you safely retry: the same key + same body returns the original response, never a second one.
How to use
Pass anIdempotency-Key header on POST /v1/images, POST /v1/videos, or POST /v1/files. Use any opaque value, 1 to 256 characters. UUID v4 is recommended. The vendor-prefixed header Aurous-Idempotent-Key is accepted as an alias for Idempotency-Key.
Rules
| Scenario | Behavior |
|---|---|
| Same key, same body — original succeeded | Replay the cached 2xx with Aurous-Idempotent-Replayed: true (never re-billed) |
Same key, same body — original failed with 4xx invalid_request | Re-evaluated, not replayed — fix the input, then retry the same key |
| Same key, same body — original still in flight | 409 idempotency_key_in_use — the first request hasn’t finished; wait and retry to get the replay |
| Same key, different body or route | 409 idempotency_key_in_use — use a fresh key |
| New key | Process normally; cache the result for 24 hours |
| Header omitted | Process normally; never replays |
Recovering after a dropped connection
If yourPOST times out and you don’t know whether it landed, retry with the same Idempotency-Key:
- If the original is still running, you’ll get
409 idempotency_key_in_use. There’s noRetry-Afteron this response — use your own backoff (e.g. 0.5s doubling to ~8s) and retry the same key. - Once it reaches a terminal state, the same key returns the original response with
Aurous-Idempotent-Replayed: true. Read the generationidfrom that body.
- Generate and persist your
Idempotency-Keybefore the request, keyed to your own operation record. The409does not echo the original generationid, so your stored key is your only handle on the in-flight call until it completes. - Only retry-loop on
409 idempotency_key_in_useif you have not changed the body or route. The same code is also returned when a key is reused with a different body or route — that’s a client bug to fix, not a transient state to wait out.
Caveats
- Keys are scoped per team — collisions across teams are impossible.
- Keys expire after 24 hours. After that, the same key starts fresh.
- If a request is interrupted server-side before completing (a rare mid-flight crash), its key is held until it expires (24h), then starts fresh — no manual cleanup needed.
- Body comparison uses canonical JSON (lexicographic key order).
{"a":1,"b":2}and{"b":2,"a":1}are treated as identical. - The header is optional; absent header behaves like a fresh non-idempotent request every time.

