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

# Idempotent requests

> Retry any POST safely with the Idempotency-Key header

A timeout or dropped connection on a `POST` leaves you unsure whether Tavrn processed it. Without a guard, retrying a retrieval request can open (and bill) a second retrieval. Send an `Idempotency-Key` header and the retry is safe: Tavrn runs the operation once and replays the same result for every retry.

## How it works

Every `POST` on `/v2` accepts an optional `Idempotency-Key` header: an opaque string of 1-255 characters that you generate per operation (a UUID v4 is recommended). Tavrn stores the first outcome for that key and compares each later request against it.

| Request with the same key | Result |
| - | - |
| Same body, first request finished | The original status and body are replayed, with `Idempotent-Replayed: true` |
| Different body | `422 idempotency_key_reused_with_different_payload` |
| First request still running | `409 idempotency_key_in_use` - retry after it completes |
| First request answered with a `5xx` | Nothing is stored; the retry executes again |

Rules:

* Keys are scoped to your organization, so a retry still replays after you rotate your API key.
* Keys are kept for **24 hours**. A key reused after that runs as a new request.
* `2xx` and `4xx` outcomes are stored and replayed (a replayed `400` is the same `400`). `5xx` outcomes are not.
* JSON key order does not matter; any other change to the body counts as a different payload.
* Requests without the header behave exactly as before. The header is only honoured on `POST`; it is ignored on `GET`, `PATCH` and `DELETE`.

```bash theme={null}
curl -X POST https://enterprise.tavrn.ai/v2/retrieval-requests \
  -H "Authorization: Bearer $TAVRN_API_KEY" \
  -H "Idempotency-Key: 7f1c3b58-2a2d-4b31-9c0e-6a7f0b8d2e11" \
  -H "Content-Type: application/json" \
  -d '{
    "case_id": "case_abc123",
    "patient": { "name": "Jane Doe", "date_of_birth": "1989-04-12" },
    "facility_id": "fac_abc123",
    "requested_records": [{ "type": "medical_records" }],
    "auth_form": { "upload_id": "11111111-1111-4111-8111-111111111111", "file_name": "jane_doe_hipaa.pdf" }
  }'
```

Retrying the exact same call returns the same `201`, the same `id` and `Idempotent-Replayed: true`.

## Idempotency vs duplicate detection

These are two different protections:

* **`Idempotency-Key`** is about *your retry*: the same request, sent again, produces one result. It is exact, deterministic and needs the header.
* **`409 duplicate_request_detected`** is about *your data*: a new retrieval for a patient and facility that already has an active one is refused unless you pass `allow_duplicate: true`. It is a heuristic over the request content and applies whether or not you send an `Idempotency-Key`.

A retry with an `Idempotency-Key` never trips duplicate detection, because the operation only runs once.

## Errors

| Code | HTTP | Meaning |
| - | - | - |
| `idempotency_key_invalid` | `400` | The header is empty, longer than 255 characters or contains control characters |
| `idempotency_key_in_use` | `409` | The first request with this key is still being processed |
| `idempotency_key_reused_with_different_payload` | `422` | The key was already used with a different body or on a different endpoint |
