How do you make a POST endpoint safe to retry?

Web API & REST API Design Advanced 3–5 Yrs5+ Yrs Architecture 1 min read

Quick Answer

Have the client send an idempotency key (a unique GUID per logical operation) in a header. The server records the key with the result of the first request; a retry with the same key returns the stored result instead of processing again. This turns an unsafe POST (create order, charge card) into something safe to retry after a timeout.

The core problem is that when a POST times out, the client doesn't know if the server processed it, so a blind retry might charge the card twice. The fix is an idempotency key: the client generates a GUID for the operation and sends it as a header, the same key on every retry. The server keeps a table of key-to-result; first time it does the work and stores the response, and any later request with that key just replays the stored response. You have to handle two of them arriving at once, which a unique constraint on the key sorts out. Stripe's API is the canonical example of this.

  • Store (idempotencyKey, requestHash) → response with a TTL; reject a reused key with a different body (409).
  • Handle the race: two concurrent requests with the same key — use a unique constraint / INSERT that one wins, the other waits or returns the in-progress state.
  • Alternative for creates: let the client generate the resource id (PUT /orders/{clientGuid}) so a retry is a natural no-op.
  • Networks are “at least once” — the client genuinely can't tell a lost response from a lost request, so the server must dedupe.