A client that retried a timed-out payment request and created two, and no way for them to have avoided it.
POST /api/payments
Idempotency-Key: 6f0a8c2e-1d4b-7a91-b3c5-0e8f2a4d9b17
the contract, documented:
the key is yours to generate, one per logical operation
we store the response for 24 hours
a repeat with the same key returns the STORED response,
with the original status code
a repeat with the same key and a DIFFERENT body is a
409, because that is a bug on your side
The conflict case is the part that has to be specified: without it, a client that reuses a key for a different request gets the old response and never finds out. Storing the response rather than just the identifier is what makes a retry after a timeout safe — the client gets the same 201 and the same body, which is exactly what a successful first attempt would have given them.