Module 12 / Distributed system pitfalls

Module 12 · lesson 4 of 8 · Read 9 min

Idempotency keys

Making a retried request do the work only once.

We keep coming back to the same customer. They tapped "Pay" for a ₹480 biryani, the call to the payments service timed out, and nobody knows whether the card was charged. Retrying might charge them twice. Not retrying might lose a payment that happened.

An idempotency key gets you out of this corner. It lets the client retry as often as it needs to, while the server makes sure the work happens only once. It's a small idea with a few sharp edges, and every system that moves money uses it.

The idea

The client attaches a unique ID to the request. That ID names the operation, not the attempt. The server remembers which IDs it has already handled, and what it answered.

POST /charges
Idempotency-Key: 6f1c2a9e-8d4b-4c1e-9a7f-2b3d5e6f7a80
{ "order_id": 9123, "amount": 48000, "currency": "INR" }
  • First time the server sees this key: do the charge, store the result under the key, reply.
  • Any later request with the same key: don't charge again. Look up the stored result and send that back.
%% caption: The retry carries the same key, so the server returns the first result instead of charging again.
sequenceDiagram
  autonumber
  participant C as Customer app
  participant P as Payments service
  participant B as Bank
  C->>P: POST /charges, key K
  P->>B: charge ₹480
  B-->>P: charged
  P--xC: reply lost, timeout
  C->>P: retry, same key K
  Note over P: seen K before
  P-->>C: stored reply, same charge ID
The retry carries the same key, so the server returns the first result instead of charging again.

From the client's point of view, the retried request behaves exactly like the original. It got the same answer, and the world changed only once. The POST has become idempotent.

An idempotency key is a unique ID the client sends with a request so that the server can recognise a retry and return the original result instead of doing the work again.

Stripe's API accepts exactly this header, and many other payment APIs offer something similar. Tiffin's own payments service should too.