What is idempotency in REST, and how do you make a POST idempotent?
Quick Answer
An operation is idempotent if performing it once or many times has the same effect. GET, PUT, DELETE are idempotent; POST is not (retrying can double-create). To make a POST safely retryable, the client sends an Idempotency-Key header; the server records the key with the result and returns the stored response for any repeat.
Idempotent means doing it again doesn't change the outcome. PUT and DELETE are idempotent by design; POST isn't, so if the response gets lost and the client retries, you can create two orders or charge a card twice. The fix is an idempotency key: the client generates a GUID for the operation and sends it in an Idempotency-Key header. The server checks whether it's seen that key — if it has, it returns the stored result; if not, it does the work, saves the key with the response, and returns it. A unique constraint on the key handles two retries arriving at once. It's exactly what Stripe does.
Why it matters: networks drop responses. The client's POST /payments succeeds on the server but the response is lost; the client retries and charges the card twice.
The pattern:
- Client generates a unique key (GUID) per logical operation and sends
Idempotency-Key: <key>. - Server, in a transaction: check if the key exists → if so, return the saved status/body; if not, do the work, save
(key → response), return it. - Handle the race (two requests, same key arriving together) with a unique constraint on the key.
- Expire old keys (e.g. 24h).
Stripe, PayPal and most payment APIs implement exactly this.