Mobile clients often retry API requests when they don't receive a response. How would you design the Create Order API so that retries do not create duplicate orders?
How I frame it
Mobile networks drop responses after server processed request — client retries POST /orders thinking it failed. Without idempotency, user gets two orders for one checkout. POST is not safe to retry by default.
API design
- Required header:
Idempotency-Key: <uuid>— one UUID per checkout attempt, stable across retries of that attempt. - Server store:
idempotency_keys(key, request_hash, order_id, response_body, status)with unique index onkey. - First request: Create order, persist mapping, return
201+ body. - Retry same key: Return same
201and body — client sees success, one order in DB. - Same key, different cart:
409 Conflict— client bug detected.
Mobile: POST /orders Idempotency-Key: K1 (timeout) Mobile: POST /orders Idempotency-Key: K1 (retry) Server: key exists → return order #8821 (no new INSERT)
Implementation notes
- Store response before returning to client if possible — or use PROCESSING state until commit.
- TTL keys 24–72h — longer than max client retry window.
- New checkout session → new idempotency key (user explicitly starts over).
Weak answer
"Client checks if order exists before creating." Race between two retries; only server-side dedupe is reliable.
Follow-up questions
Two concurrent requests with same key?
Unique constraint on key: first insert wins and processes; second waits or reads completed record — same as coupon redemption race.
Idempotency for PATCH / cancel?
PUT/PATCH often naturally idempotent; POST create is the critical one. Cancel can use idempotency or state machine guard.