> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aurous-labs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Idempotency

> Safe retries for POST /v1/images, POST /v1/videos, and POST /v1/files.

## Why idempotency keys?

Network failures and timeouts can leave you uncertain whether a `POST` 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 an `Idempotency-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`.

```bash theme={null}
curl -X POST https://api.aurous-labs.com/v1/images \
  -H "X-Api-Key: $AUROUS_API_KEY" \
  -H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "a sunset over mountains", "count": 1}'
```

If you retry with the same key and the same body, the original response is replayed:

```http theme={null}
HTTP/1.1 201 Created
Aurous-Idempotent-Replayed: true
```

## 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 your `POST` 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 no `Retry-After` on 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 generation `id` from that body.

Two rules make this reliable:

1. **Generate and persist your `Idempotency-Key` before the request**, keyed to your own operation record. The `409` does not echo the original generation `id`, so your stored key is your only handle on the in-flight call until it completes.
2. **Only retry-loop on `409 idempotency_key_in_use` if 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), the key is freed as soon as the platform observes the failure — retry with the same key right away. The 24h expiry is only a backstop for the rarer cases where the platform can't free the key right away.
* 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.
* `503` responses are never cached against your key — retry with the same key.
