Skip to content

Idempotency Keys

Request-identity artifact — instantiates Asynchronous Replica Convergence

Attaches a caller-minted unique key to a logical operation so a retried request carries the same identity and can be recognized as the same operation, not a new one.

Idempotency Keys give each logical operation a stable, unique name that travels with the request, so that when a client retries after a timeout or dropped connection, the retry carries the same key and the receiver can tell it is the same operation rather than a second one. Its defining contribution is the identity itself — a caller-minted token, unique per intent — which is the hook every downstream dedup, guard, or ledger keys on. It supplies the name; it deliberately does not enforce what happens when the name repeats.

Example

A traveler taps "Confirm booking" in a hotel app on a flaky connection. The request reaches the server and creates reservation #A19 — but the response is lost, so the app, seeing no confirmation, retries. Because the app minted an idempotency key when the user first tapped — a UUID bound to that intent — and sent it on both attempts, the server recognizes the second request as the same booking and returns the existing reservation instead of booking a second room and charging twice. The key is the entire reason the retry is safe: without it, two identical requests are indistinguishable from two genuine bookings. Note what the key is tied to — the user's single intent to book — not to each network attempt, which is the distinction that makes it work.

How it works

The distinguishing property is a client-minted identity carried end to end:

  • Mint once per intent. The client generates a unique key when the user forms the intent, not per network attempt, so every retry of that intent shares one key.
  • Carry it on the wire. The key rides with the request (commonly a dedicated header), so it survives retries across the whole path.
  • Record key → outcome. The receiver stores the key with the operation's result the first time it is seen.
  • Return, don't repeat. A request bearing a known key returns the stored outcome instead of executing again.

Tuning parameters

  • Key scope — per intent versus per attempt; this is the make-or-break dial — a key minted per attempt defeats the whole mechanism, since every retry looks new.
  • Key lifetime — how long key→outcome mappings are retained; it must cover the longest realistic retry window, or a late retry re-executes.
  • Who mints it — the client, or a gateway on the client's behalf; client-minted keys survive more of the path but require client cooperation.
  • Payload binding — whether the key is bound to the request body, so reusing a key with different content is rejected rather than silently returning the old result.
  • Collision resistance — how the key is generated (UUID, content hash); weak generation risks two distinct intents colliding onto one key.

When it helps, and when it misleads

Its strength is enormous leverage for a tiny artifact: a single stable token makes retries and network uncertainty safe, which is why "create"-style operations that are not naturally idempotent lean on it.[1] Its failure modes come from misuse of the identity, not the identity itself. A key minted per attempt makes every retry look new; a key reused across different intents causes the opposite error, suppressing a genuinely new operation as a duplicate; and a payload-unbound key can return a stale result for a changed request. The classic misuse is treating the key as the enforcement — but the key only names the operation; something downstream must actually act on the name. The discipline is to mint once per intent, bind the key to the payload, set a lifetime that outlives retries, and always pair the key with a consumer or guard that reads it.

How it implements the components

  • operation_identity_key — it is the operation identity: a stable, unique, on-the-wire name for one logical operation, minted at intent and preserved unchanged across every retry.

The key only names the operation; it does not enforce it. Turning that name into at-most-once effects (idempotence_guard, side_effect_boundary) is Deduplicating Message Consumer's work; a record's version_token and state_consistency_guard are Optimistic Concurrency Check's; and resolving a conflict once one is detected (conflict_resolution_rule) belongs to Data Diff and Merge Tool.

Notes

The key is an identity artifact, inert on its own — its entire value is in what reads it. Pair it with a Deduplicating Message Consumer on a stream, or a key→outcome store behind an API. And note the identity it carries names an operation; the superficially similar version_token of Optimistic Concurrency Check names a record's version — one answers "which request?", the other "which version of the data?".

References

[1] In HTTP an operation is idempotent when performing it many times has the same effect as performing it once; PUT and DELETE are defined that way, while POST is not — which is exactly why POST-style "create" operations are the ones that need an explicit idempotency key.