---
title: Idempotency
description: Retry a request with an idempotency key, and the operation still happens only once.
---

With the `X-Idempotency-Key` header you can retry a request without the risk of a duplicate
operation. When a request carries an idempotency key, the operation happens exactly once, even
when you send the request many times.

## How it works

**The first request.** We do the operation and cache the whole response against your
idempotency key. The cache holds the status code, the headers, and the body.

**Every later request.** When a request arrives with the same key, we answer with the cached
response at once. We do not do the operation again.

## Key requirements

**One key per operation.** Generate a new identifier for each distinct operation. Never reuse
a key for a different operation.

**The request fingerprint must not change.** These parts of a retry must be identical to the
first request:

- The request method, such as POST or PUT.
- The request URL, with its path and its query parameters.
- The request body, byte for byte.
- The request headers that change the result, such as `Content-Type` and `Authorization`.

**A modified request gets rejected.** If you send the same idempotency key with different
request data, the API answers `409 Conflict`. This rejection catches the mistake of a key
reused for another operation.

## Response behavior

| Scenario                          | Response                                                               |
| --------------------------------- | ---------------------------------------------------------------------- |
| First request with key            | Normal processing, response cached                                     |
| Retry with identical request      | Cached response returned (same status, headers, body)                  |
| **Concurrent identical requests** | `409 Conflict` with `idempotency_key_locked` (retry after brief delay) |
| Retry with **modified** request   | `409 Conflict` with `idempotency_key_mismatch` (do not retry)          |

### Error handling guidance

**A concurrent collision** (`idempotency_key_locked`) means that another request with the same
key is still in progress. This state is temporary. Wait 100 to 500 ms, then send the identical
request again.

**A request mismatch** (`idempotency_key_mismatch`) means that the key already carried
different request parameters. This is a fault in your code. Generate a new idempotency key for
the new operation.

## Expiration

An idempotency key expires 24 hours after its first use. After that, the same key starts a new
operation.

## Best practices

- Generate the key in your own code, before you send the request.
- Store the key with your request context, so a retry can carry the same key.
- Use an idempotency key on every operation that is not idempotent by itself: POST and PATCH.
- Generate the key from cryptographically random values. A timestamp and a sequential
  identifier are both predictable.

## Example

```bash
# First request
curl -X POST "{BASE_URL}/orders" \
  -H "X-Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"customerId": "cust_123", "items": [...]}'

# Response: 201 Created
# {"orderId": "1b11f175-f0e6-4b6c-8c4b-4eee806123a3", "status": "CONFIRMED", ...}

# Retry (network timeout, uncertain state)
# Use the same idempotency key and request data
curl -X POST "{BASE_URL}/orders" \
  -H "X-Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
  -H "X-API-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"customerId": "cust_123", "items": [...]}'

# Response: 201 Created (identical to first request)
# {"orderId": "1b11f175-f0e6-4b6c-8c4b-4eee806123a3", "status": "CONFIRMED", ...}
# No duplicate order created
```
