Overview

An operation is idempotent when repeating it with the same input changes nothing beyond the first run. Design every retried operation this way, including webhook handlers, queue consumers, and payment calls, because networks and queues deliver duplicates. An idempotency key or an upsert usually costs one column.

Rules

  • HTTP: GET, HEAD, OPTIONS, PUT, and DELETE are idempotent by definition; POST and PATCH are not. Make a POST retry-safe with an Idempotency-Key header: the server stores the key with the first response and replays it for repeats (a convention popularized by Stripe).
  • A key must be unique per logical operation, and reuse with a different payload must be rejected.
  • Databases: UPDATE ... SET status = 'paid' is idempotent; SET balance = balance - 5 is not. Use upserts or ON CONFLICT DO NOTHING keyed on a request or message ID.
  • Consumers: record the processed message ID in the same transaction as the side effect.
INSERT INTO payments (idempotency_key, amount_cents) VALUES ($1, $2)
ON CONFLICT (idempotency_key) DO NOTHING;