← Blog

Idempotency Keys: Make Retries Safe

Networks fail. Clients retry. Without protection, a retried POST /payments charges the customer twice.

The idea

The client sends a unique Idempotency-Key header with each logical request. The server stores the key with the response. If the same key arrives again, the server returns the stored response instead of running the operation.

Minimal flow

  1. Client generates a UUID and sends it with the request.
  2. Server checks the key in a store (a DB table with a unique index works).
  3. Key not found: run the operation, save key and response, return it.
  4. Key found: return the saved response.
CREATE TABLE idempotency_keys (
  key text PRIMARY KEY,
  response jsonb NOT NULL,
  created_at timestamptz DEFAULT now()
);
sequenceDiagram
  Client->>Server: POST /payments (Idempotency-Key: abc)
  Server->>Store: key abc exists?
  Store-->>Server: no
  Server->>Server: charge card, save response
  Server-->>Client: 201 Created
  Client->>Server: retry with same key
  Server->>Store: key abc exists?
  Store-->>Server: yes, stored response
  Server-->>Client: 201 Created (no second charge)

Pitfalls

  • Insert the key and run the operation in one transaction, or two concurrent retries both pass the check.
  • Reject a reused key sent with a different payload.
  • Expire old keys (24 hours is common).