Pattern · seen in 6 breakdowns across 5 companies

Idempotency Keys

A unique label the client attaches to a request, so the server tells a retry from a new request and does the work only once, no matter how often the same request is retried.

The mechanism

At its core: when a client retries a request, the server needs to tell whether it is a genuine new request or a repeat of one it already handled. A stable key the client attaches with the request answers that: the first one runs and is saved, and every retry with the same key just replays the saved result.

LIVE ARTIFACTRETRY WITHOUT REDOINGOPEN FULL SCREEN ↗
THE IDEAWhen a request fails, the client often cannot tell if it actually went through, so it retries - and for something like a charge, running it twice is a real problem. An idempotency key fixes this. The client attaches a unique label to the request; the server does the work once and saves the result under that label. Any later request carrying the same label gets the saved result back instead of running again, so the charge happens exactly once no matter how many retries arrive. The key is just an agreement on an identifier - how the server stores and dedupes underneath is a separate problem, and the client's one job is to use a fresh key for each genuinely new operation.
WHAT TO TRYTurn the key off and hit retry a few times - each retry is a fresh charge, so one payment becomes two or three. Turn the key on and retry all you want; the server recognizes the repeat and the charge stays at one.

Retry a charge with no key and you pay twice; retry it with a key and the server charges you once.

Definition

An idempotency key is a unique label the client attaches to a request. The first time the server sees that label, it does the work and saves the result under it. If the same label arrives again - a retry - the server does not redo the work; it just returns the saved result. Same label in, same result out, no matter how many times the request is sent.

ONE KEY, ONE CHARGE
Without a key, a retry double-charges; with the same key, the server replays the first outcome once.
Without a key, a retried request runs again and charges twice; with a stable key on both tries, the server recognizes the repeat and replays the first outcome, so the work happens exactly once.

This is what makes it safe to retry an operation that must not happen twice, such as:

  • charging a customer's card
  • placing an order or booking a seat
  • creating a new account or record
  • moving money from one account to another
  • sending a one-off notification or email

When a request fails, the client cannot always tell whether it went through - the network may have dropped the reply, or the server may have crashed after doing the work. So the client retries, and the key guarantees the work is not repeated. It is one piece of safe retrying, alongside deciding which failures are worth retrying and controlling how often clients retry.

The key itself is just an agreement between the client and the server: as long as both use the same key, the outcome is the same, no matter what the network did in between - dropped replies, timeouts, a crash and restart. How the server actually stores results and avoids doing the work twice - for example by committing the operation in safe atomic phases - is a separate problem. The pattern is the agreement on the identifier, not the implementation behind it.

One thing the client owns: it must generate the key, and generate it carefully. The server trusts the key to mean this exact operation. So use a fresh key for each new operation - reuse an old one for something new, and you silently get the old result back.

When it applies

01The operation is unsafe to repeat, but might be retried. Anything that causes real harm if it runs twice - charging a card, creating a resource, any change you cannot undo - needs a key before it is safe to retry.
02The caller drives the retries and the network is unreliable. When the client decides when to retry, and the server cannot assume a request arrives exactly once, the key is how the server tells repeats apart.
03The work spans several systems and can be left half-done. When an operation touches multiple systems, a retry that redoes only some of the steps leaves them out of sync - a shared key lets each step recognize it has already run.

Tradeoffs

You have to store every key and its result, durably. The server needs a saved record of each key's outcome, kept at least as long as a client might realistically keep retrying, or a late retry finds nothing and runs again.
Every request gets slightly expensive: it has to look up its key first, and the first attempt also writes a record - small per request, but on every single one.
Reusing a key by mistake fails silently. If a client sends a new operation under an old key, it quietly gets the old result and never runs. So how clients generate keys - a fresh, unique one for each real operation - becomes part of the contract.

The same move, 6 ways

Every row is a production system that bet on this pattern — the note says how, in that system's own terms.

Shopify
Shopify Engineering
2022
Shopify's instantiation is provider-side: the centralized payment service tracks each attempt's completed steps under the key and runs recovery steps to rebuild state before continuing, so a retry never re-reaches the financial partner. The ULID key format makes the key itself index-friendly - a 50% INSERT duration reduction in one high-throughput system. Read the breakdown →
Amazon (AWS)
Amazon Builders' Library
2021
The caller attaches a unique identifier to a request, and the service treats any later request carrying the same identifier as the same request, so a retry replays the first outcome instead of doing the work twice. Stripe, Shopify, and Airbnb have all appeared in the library making this call from the client-facing side; AWS's piece is the platform provider's view of the same contract (EC2 calls the identifier a ClientToken). The reason a caller-supplied key beats a hash of the request is intent: identical parameters don't mean identical intent, and only the caller knows whether a second identical request is a mistaken duplicate or a genuine second order. Read the breakdown →
Amazon (AWS)
Amazon Builders' Library
2019
EC2 RunInstances' client token is the same mechanism Stripe exposes as its Idempotency-Key header. The client names the operation, every retry repeats the name, and the server ignores the duplicates, turning a call that was unsafe to retry into a safe one. Same contract, two companies: retry safety is designed in at API-design time, not bolted on by the client. Read the breakdown →
Airbnb
Airbnb Engineering
2019
This is the deepest look the library has at what an idempotency key actually does inside a service. Stripe defines the key as an API contract, Shopify shows why it matters at volume, and AWS pairs it with retry discipline; Airbnb shows the interior. Here the key is three things at once: the row a request locks while it runs, the value the tables are sharded on, and a design decision in its own right, since a random UUID per request behaves differently from a deterministic key like payment-1234-refund (which lets a given refund happen only once, ever). Read the breakdown →
Segment
Segment Blog
2017
The furthest the pattern reaches from where it started: no contract, no stored response replayed, no request/response boundary at all. The messageId (a client-generated unique ID, chosen over more complex schemes to keep the client's job as close to nothing as possible) doubles as the routing key, the primary key, and the identity in a pipeline-wide dedupe ledger that settles the ambiguity downstream because the boundary can't. Across five companies the same key has been a contract term (Stripe), a client discipline (Shopify), a piece of server interior (Airbnb), a platform default (AWS), and here, pure infrastructure - one idea seen from five distances. Read the breakdown →
Stripe
Stripe Engineering
2017
This is where the pattern gets its clearest early statement: a client-generated unique ID sent with each mutating request (Stripe's Idempotency-Key header on all POST endpoints). That one key lets the server handle all three network-failure cases: process a fresh request, pick up and finish an interrupted one, or replay the saved result of one that already completed. The same key going in always produces the same outcome coming out. Read the breakdown →

Often used together

Patterns sharing breakdowns with this one — derived from co-occurrence, threshold ≥2 shared.

Problems this pattern answers

The walls where its breakdowns live — each opens the cross-company comparison.