Idempotency keys, minus the folklore
Safe retries are a protocol, not a magic request header.
A client can lose the response after a server has committed the work. Retrying is correct, but repeating the side effect is not. An idempotency key gives both sides a stable name for that attempt.
Store the result, not only the key
The useful record contains a key, a fingerprint of the relevant request fields, the processing state, and the final response. Returning the original status and body makes the retry indistinguishable from the successful first call.
key text primary key
request_hash bytea not null
state text not null
status_code integer
response_body jsonb
expires_at timestamptz not nullReject accidental reuse
If the same key arrives with a different request fingerprint, return a conflict. Silently attaching a new operation to an old key is worse than rejecting the retry because the client can no longer reason about the outcome.
I keep the transaction boundary close: reserve the key and perform the business write in one transaction where possible. For slow external work, the record moves through an explicit processing state and competing requests receive a retryable response.
Expiration is part of the contract
Keys need a documented retention window. Cleanup can lag without affecting correctness, but deleting earlier than promised can duplicate old operations. The modest version of this design has been more reliable than clever middleware because every state is inspectable in the database.