# Idempotency

Networks fail, requests time out, and clients retry. Idempotency lets you retry a
mutating request **without risking a duplicate** — a second ticket issued, a
second charge created. You send a unique key with the request; if you send the
same key again, the platform returns the **original response** instead of doing
the work twice.

## How it works

Add an `Idempotency-Key` header to a mutating `POST`:

* The **first** request with a given key runs normally, and its response is
  stored against that key.
* A **retry** with the same key **and the same request** returns the stored
  response verbatim — same status code, same body. The operation runs only once.
* A retry with the same key but a **different request body** is rejected with
  `409` and an `idempotency_error` (code `idempotency_key_reuse`). Reusing a key
  for a different request is always a bug.

Stored responses are kept for **24 hours**. After that, the same key is treated
as new.

> **Note:** Scope an `Idempotency-Key` to a single logical operation. Generate a fresh key
> for each new request you make, and reuse that same key only when retrying that
> exact request.


## Recommended key format

Use a **random UUID (v4)** per operation. It's collision-resistant and easy to
generate in any language.

```bash
KEY=$(uuidgen)   # e.g. 3f9b1c2e-7a4d-4e1b-9c2a-1d6f0b8e5a21
```

## Which endpoints support it

The `Idempotency-Key` header is honored on mutating `POST` endpoints, most
importantly:

* `POST /v1/customers` — create a customer
* `POST /v1/events` — create an event
* `POST /v1/events/:id/tiers` — add a ticket tier
* `POST /v1/events/:id/tickets` — issue a ticket
* `POST /v1/payment_intents` — create a payment intent
* `POST /v1/tickets/:id/refund` — refund a ticket
* `POST /v1/tickets/:id/upgrade` — upgrade a ticket
* `POST /v1/tickets/:id/attendance` — record a check-in
* `POST /v1/payouts` — request a payout

Sending the header on an endpoint where it isn't needed is harmless — it's simply
ignored. Omitting it runs the request normally with no replay protection.

## Example: a retried request

The same key is sent twice. The first call issues one ticket; the retry returns
that same ticket without issuing a second one.

```bash
KEY=$(uuidgen)

# First attempt — times out on your side, but the platform processed it.
curl https://api.ticketconnect.example/v1/events/evt_123/tickets \
  -H "Authorization: Bearer sk_test_your_key_here" \
  -H "Idempotency-Key: $KEY" \
  -H "Content-Type: application/json" \
  -d '{ "customer_id": "user_42", "tier": "General Admission" }'

# Safe retry — identical key + body → returns the original response, no duplicate.
curl https://api.ticketconnect.example/v1/events/evt_123/tickets \
  -H "Authorization: Bearer sk_test_your_key_here" \
  -H "Idempotency-Key: $KEY" \
  -H "Content-Type: application/json" \
  -d '{ "customer_id": "user_42", "tier": "General Admission" }'
```

Both calls return the same ticket with the same `id`. See
[Errors](#errors) for the `idempotency_error` envelope.
