The Retry That Charged Forty Patients Twice

Decide what you would do from the brief alone, including whether you would change anything at all. Everything below it is available, but the exercise stops working if you open it first.

The brief you were given

The payment provider retried a webhook after our endpoint timed out. Forty patients were charged twice, and one claim was submitted twice to the payer.

The trap — the fix that looks like good design and is not

Adding a processed_webhooks table and an if (await seen(eventId)) return at the top of the handler. The duplicate charges stop in staging immediately, the change is fifteen lines, and it is the standard recipe — which is why it ships. The check and the work are not atomic, so two retries arriving concurrently — exactly what a timeout produces — both read "not seen" and both proceed, and the window is widest precisely when the system is slow. Marking the event seen *before* doing the work closes that hole and opens a worse one: a process that dies mid-handler converts a double charge into a silent no-charge that nothing will ever retry. The identity has to be attached to each effect, not checked next to it.

Read this even if you are confident. It is here rather than behind a button because it is the answer most teams actually ship, it passes review, and the cost of it does not arrive until the change after this one.