The framing of a webhook as “a message from a queue you do not own” is the useful mental model. Once you make that shift, signature verification, idempotency, ordering, and retry handling stop looking like separate security checklist items and become properties of the same consumer design.
One detail I’d emphasize is making the acknowledgement boundary explicit: after authentication and an atomic event claim, persist the event durably before returning 2xx. From there, the provider is no longer part of your processing reliability loop. Your own queue, idempotency state, retry policy, and dead-letter handling become the controls you can actually reason about and test under failure.
Yes, that's a sharper way to put it than I did: the 2xx doesn't mean 'I processed it,' it means 'I have durably taken ownership,' and persisting before the ack is what takes the provider out of your reliability loop. The one place it bites is persisting to something that only looks durable, an in-memory queue, a write that hasn't fsync'd, a broker ack that is really just buffered. Then the 2xx lies in the other direction: the provider stops retrying because you claimed ownership you don't actually hold. The durability of that one write ends up load-bearing for the whole contract.
Mateo Ruiz
The framing of a webhook as “a message from a queue you do not own” is the useful mental model. Once you make that shift, signature verification, idempotency, ordering, and retry handling stop looking like separate security checklist items and become properties of the same consumer design.
One detail I’d emphasize is making the acknowledgement boundary explicit: after authentication and an atomic event claim, persist the event durably before returning 2xx. From there, the provider is no longer part of your processing reliability loop. Your own queue, idempotency state, retry policy, and dead-letter handling become the controls you can actually reason about and test under failure.