# Make retries safe with idempotency keys

An Idempotency-Key header makes retries safe: the same key with the identical payload charges once and serves the same result. Replays, conflicts, and how to choose keys.

Idempotency makes a retry behave like the first attempt. Send an `Idempotency-Key` header, and the identical request charges once however many times you send it. A key is any string of 1 to 200 printable ASCII characters. Keys are namespaced to your API key and never collide with another account's.

curl:

```bash
curl -X POST https://api.paxalabs.com/v1/tts \
  -H "Authorization: Bearer $PAXA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-8146-confirmation" \
  -d '{"text": "สวัสดีครับ ยินดีต้อนรับสู่ Paxa Labs", "voice": "khanomkrok", "model": "paxa-tts-flash-v2"}' \
  --output speech.mp3
```

TypeScript:

```typescript
// Reuse the same key when retrying the identical request: the charge
// happens once and the same audio is served again.
const idempotencyKey = `order-${order.id}-confirmation`;

const response = await fetch("https://api.paxalabs.com/v1/tts", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.PAXA_API_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": idempotencyKey,
  },
  body: JSON.stringify({ text: "สวัสดีครับ ยินดีต้อนรับสู่ Paxa Labs", voice: "khanomkrok", model: "paxa-tts-flash-v2" }),
});
```

## Supported endpoints

Seven billed HTTP endpoints accept the header: [POST /v1/tts](https://paxalabs.com/docs/api/text-to-speech), the [OpenAI-compatible speech endpoint](https://paxalabs.com/docs/api/speech), [POST /v1/translate](https://paxalabs.com/docs/api/translate), [POST /v1/ocr](https://paxalabs.com/docs/api/ocr), [POST /v1/extract](https://paxalabs.com/docs/api/extract), [POST /v1/stt](https://paxalabs.com/docs/api/stt), and the [OpenAI-compatible transcription endpoint](https://paxalabs.com/docs/api/transcriptions). The [live WebSocket](https://paxalabs.com/docs/api/live) does not accept it. Its billing is cumulative per connection, and reconnecting opens a new billing scope. The main use is [streaming](https://paxalabs.com/docs/streaming). A dropped stream retried under the same key replays without a second charge.

## Replays

The first request under a key records the charge. A retry with the same key and the identical payload is verified against that record and served without charging again. Its `x-credits-charged` header, or the body's `usage.credits` for transcription, OCR, extraction, and translation, reports the original charge. One request per key runs at a time, and concurrent duplicates answer [409 idempotency_in_flight](https://paxalabs.com/docs/errors#idempotency_in_flight).

## Conflicts

| Response | When | What to do |
| --- | --- | --- |
| [409 idempotency_in_flight](https://paxalabs.com/docs/errors#idempotency_in_flight) | Another request with this key is running right now. | Wait for it to finish, then retry to replay its outcome. |
| [409 idempotency_refunded](https://paxalabs.com/docs/errors#idempotency_refunded) | The original request failed and was refunded. | Send a new Idempotency-Key. |
| [422 idempotency_mismatch](https://paxalabs.com/docs/errors#idempotency_mismatch) | The key was already used for a different payload. | Reuse a key only to retry the identical request. |

## Choose a key

Derive keys from identifiers you already have, for example `order-8146-confirmation`. One key means one piece of audio: a new attempt at different content needs a new key.

> Without the header, every request charges exactly once under its own internal id, tied to the `x-request-id` response header for support.
