larsggu.me › Reference › idempotency key
idempotency key
Idempotency-Key: <client-generated unique string>
A caller-supplied identifier that lets a request be repeated safely, because the server records the outcome against the key and replays it instead of acting twice.
Description
Retrying a read is free. Retrying a write is not, and yet a caller that loses the response to a write has no way of knowing whether the write happened. The idempotency key resolves that by moving the identity of the operation out of the transport and into a value the caller chooses before the first attempt and keeps for every retry of it.
The server stores the key alongside the response it produced. When a request arrives carrying a key it has already seen, it does not perform the operation again; it returns the stored response, ordinarily with a header saying so. The effect is that the caller can retry until it gets an answer, and the number of retries has no bearing on the number of times the operation occurred.
The key has to be generated by the caller, not derived from the request body. A digest of the body looks convenient but makes two genuinely separate operations with identical content collapse into one, which is exactly wrong for anything a customer might legitimately do twice. A random identifier per logical attempt is the correct construction.
Stored keys are retained for a bounded window rather than forever, because the point of the record is to cover the retry period rather than the lifetime of the account. Once the window passes, the same key is treated as new. A caller that retries after the window has expired therefore performs a second operation, which is why retry budgets are ordinarily set well inside the retention window.
Fields
| Field | Form | Meaning |
|---|---|---|
| Idempotency-Key | request header | The caller's identifier for this logical operation. Required on the operations that accept it. |
| Idempotent-Replay | response header | Present when the response was served from the stored record rather than produced now. |
| Retention window | duration | How long the stored outcome remains addressable by the key. |
| Conflict status | 409 | Returned when the key is reused with a materially different body, which indicates a caller bug. |
| Scope | key plus credential | Keys are scoped to the credential that presented them, so two customers cannot collide. |
Example
First attempt and retry
POST /v1/payouts HTTP/1.1
Idempotency-Key: 0f2c4a7e-6b1d-4c22-9f80-1ab5c7d3e004
Content-Type: application/json
{"amount":4200,"currency":"NOK","destination":"acct_71"}
HTTP/1.1 201 Created
{"id":"po_5512","state":"pending"}
# the same request sent again after a timeout
HTTP/1.1 201 Created
Idempotent-Replay: true
{"id":"po_5512","state":"pending"}The second attempt returns the first attempt's response, including its identifier, and creates nothing.
Failure modes
- Deriving the key from a digest of the body, which merges two legitimate identical operations.
- Generating a fresh key on each retry, which defeats the mechanism entirely.
- Storing the key before the operation commits, so a crash mid-write leaves a key that replays a response for work that never happened.
- Applying the key across credentials rather than within one, which lets one caller observe another's outcome.
Related entries
Topic: Transport. Last modified 2026-09-06.