The Runtime Theory
easyApplicationDSA#backend#api#idempotency#reliability

Design an idempotent API endpoint

Probes whether you understand idempotency as concrete machinery — where the key lives, where it's checked, and how duplicates are blocked — not just a buzzword.

The Runtime Theory Team2 min readasked at stripe · twilio · shopify

This question tests whether you understand idempotency as a mechanism rather than a marketing term. The interviewer wants the concrete machinery: where the key lives, where it's checked, and what happens on a duplicate.

The mental model: make the side effect a function of the key, not of the request.

The client generates a key — a UUID, Stripe-style — and sends it in an Idempotency-Key header. The server's contract: two requests carrying the same key run the side effect at most once, and the duplicate gets the original response.

The check happens before the side effect, against a store that can enforce uniqueness — typically a database table with a unique constraint on the key column. The lookup path: if the key exists, return the stored response and do no work. If it misses, insert the key with status in-progress, run the handler, and on completion update the row atomically with the response body and status done. The unique constraint is what makes the race safe: two concurrent duplicates both miss, both try to insert, one wins, and the loser hits a constraint violation and waits for the winner to finish before returning the stored result.

Two design details matter. First, the retention window: keys must outlive the client's retry horizon — Stripe keeps them for 24 hours. Second, the boundary of protection: idempotency guards the write path. Reads are naturally idempotent, and a non-idempotent effect like an increment must be expressed as an absolute operation ("set balance to X") rather than a relative one ("add 5") — or it must be rewritten so the key lookup short-circuits it entirely.

Tradeoffs and edge cases: the key store is now on the critical path of every write, so it must be fast and highly available — in effect it's a lock. Expired keys mean an old retry can double-execute, so clients must not retry outside the window. And a request that crashes mid-handler leaves a stale in-progress row; the server needs a timeout after which a stuck key is treated as expired and re-executed, rather than returning a half-finished result forever.

This answer walks

Follow-ups they'll push on

  1. 01How long should the idempotency key be stored, and who decides?
  2. 02What happens if two duplicate requests arrive concurrently?
  3. 03How do you handle a client that reuses a key for a different request?

More interviews in this topic

One dispatch a week

The trace behind each question, the tradeoff that explains it, and one technical dispatch per week — no noise.

One technical dispatch per week. No noise.