# Idempotency

Networks fail. If your `issue` call times out you cannot know whether the ticket was issued. Idempotency makes retrying safe.

Endpoints that create something that must never happen twice **require** an `Idempotency-Key` header:

- `POST /flights/bookings` (hold)
- `POST /flights/bookings/{id}/issue`
- `POST /flights/bookings/{id}/post-booking-requests`
- `POST /flights/bookings/{id}/post-booking-requests/{requestId}/accept`

```http
POST /flights/bookings/7c1e…/issue
Authorization: Bearer tam_live_…
Idempotency-Key: 5b0c1f7e-8d53-4bd8-a0f4-4c31d0b5e8a2
```

## Rules

- Use a **new unique value** (a UUID v4 is ideal) for each new operation, and **the same value** when retrying that operation.
- Store the key with your order before you call, so a retry after a crash reuses it.
- Keys are remembered for **24 hours**, per agency.

| Situation | Result |
|---|---|
| First request | Runs normally. |
| Same key, same body, first request finished | The **stored response is replayed** (same status and body) with header `Idempotent-Replayed: true`. Nothing runs again. |
| Same key, same body, first request still running | `409 idempotency.in_progress` — wait a second and retry. |
| Same key, **different** body | `422 idempotency.key_reused` — you reused a key for another operation. |
| Header missing | `400 idempotency.key_required`. |

## Extra protection on issue

Even with a *new* key, calling `issue` on a booking that is already `issuing` or `ticketed` does **not** start a second ticketing attempt: it returns the booking's current state with `200`. Double ticketing is impossible through the API.
