Idempotency-Key header. The header protects you from duplicate side
effects on network retries; natural-key upsert on key resources protects
you from duplicates across longer time horizons.
The header
- Client-generated. UUIDs are recommended; any unique string up to 255 characters works.
- Scoped per
(organizationUuid, key). Keys don’t collide across customers. - Stored with the response for 24 hours.
Behavior matrix
| Request | Server behavior |
|---|---|
First request with key K, body B1 | Executes normally. Stores key + hash of B1 + response. Returns response. |
Retry with key K, body B1 (identical) | Returns cached response without re-executing. Side effects are not re-applied. |
Retry with key K, body B2 (different) | Returns 422 with type: idempotency-key-reused. No side effects. |
| Request without header on a write endpoint | 400 Bad Request. Header is required. |
Key K after 24h window | Treated as a new key — executes normally. |
Example — safe create-then-retry
- Generate one key per logical operation. Not per HTTP attempt.
- Persist the key if your retry might span a process restart — reuse the same key after restart.
- Never reuse a key for different data — even small changes trigger
422. Fresh data = fresh key.
Natural-key upsert (second layer)
Some resources are upserted on a business-level natural key independent of theIdempotency-Key header:
| Resource | Natural key |
|---|---|
| Buyer | (sellerId, vatNumber) |
| Order | (sellerId, externalOrderId) |
| Invoice | (orderUuid, type) — one preliminary + one final per order |
| Payment | orderUuid — one payment record per order |
- Same body →
200 OKwith existing resource. Idempotent regardless of theIdempotency-Key. - Different body →
409 Conflictwith current resource. UsePATCHfor explicit updates.
Idempotency-Key cache missed (e.g.
24h elapsed between retries, or a different key was generated).
Why two layers
- Header handles short-window network-retry dedup with exact-response replay.
- Natural key handles cross-channel dedup — the same buyer created via API and then “discovered” via web app still converges to one record.
What happens when I send Idempotency-Key with a GET request?
Nothing — the header is ignored. Reads are naturally idempotent; the
header is parsed only on write methods.
Next
Optimistic concurrency
Preventing lost updates on concurrent edits.
Error reference
422 idempotency-key-reused and related problems.