Webhook retries need receipts
Reliable delivery starts by recording what the receiver accepted, not by sending the same payload louder
Webhook systems fail in ordinary ways. The receiver times out, a network path drops, a queue worker restarts, a TLS certificate is misconfigured, or the sending service gets a response it cannot classify. The usual answer is to retry.
Retries are necessary. They are also incomplete.
A retry asks the same delivery question again. A receipt answers a better question: what did the receiver accept, reject, or leave unresolved the last time this delivery was attempted? Without that record, the sender only knows that it does not have a clean success. It does not know whether the receiver did nothing, completed the work but failed to respond, accepted the payload and queued it internally, or rejected it for a reason that another retry will not fix.
A delivery attempt is not the same as delivery
The first useful distinction is between attempt and delivery. An attempt means the sender tried to hand off the payload. Delivery means the receiving side accepted responsibility for it, according to a contract both systems understand.
That difference matters because webhook senders often sit near business events. A payment changed state, an account was created, a document was signed, a job finished, or a customer action needs downstream processing. Repeating the handoff blindly can create duplicates, but suppressing retries too aggressively can lose events. The receipt is how the system avoids treating both risks as the same problem.
A good receipt does not have to be complex. It needs a delivery ID, target endpoint, payload identity, attempt number, response status, response class, timestamp, and enough error detail to route the next action. If the receiver returns a durable acknowledgement, store it. If it returns a rejection, store that too. If the request times out, record the uncertainty instead of pretending the event failed cleanly.
Timeouts are unresolved states
Timeouts are where many webhook systems get sloppy. A timeout does not prove the receiver ignored the payload. It proves the sender stopped waiting. The receiver may still be processing, may have committed the event and failed to respond, or may have dropped the request before doing anything useful.
That is why idempotency and receipts belong together. An idempotency key lets the receiver recognize the same logical delivery. A receipt lets the sender remember what happened around each attempt. If the sender retries with the same delivery identity, the receiver can return the original result, reject a conflicting duplicate, or report that the event is still in progress.
This pattern is more useful than a growing retry counter with no context. The counter says how many times the sender tried. The receipt says what each try produced.
Permanent failures should stop the loop
Some failures deserve another attempt. Others deserve a different path. A 500 response, timeout, or temporary network error may justify retrying with backoff. A 401, 403, unsupported event type, invalid signature, malformed payload, or unknown endpoint often needs configuration repair before another delivery will help.
The delivery record should classify those outcomes in plain language. Retryable, blocked, rejected, accepted, uncertain, and disabled are more useful states than success and failure alone. They help operations and support teams understand whether the next action belongs to the sender, receiver, account owner, or product configuration.
This is also where notification design matters. If every failed attempt becomes an alert, teams learn to ignore the system. If only the final failure is visible, they lose the trail that explains how the delivery reached that state. Receipts let a product show the compact history without turning every retry into noise.
Receipts make customer conversations safer
When a customer asks whether an event was sent, a vague answer is not enough. The support or engineering team should be able to say which event was attempted, when it was sent, which endpoint was targeted, what response came back, and what the system will do next. That does not require exposing private payload data. It requires preserving the operating facts around delivery.
This keeps customer communication bounded. The team should not claim that the receiver processed the event if the only evidence is a timeout. It should not claim the sender failed if the receiver returned an accepted response. A receipt makes the answer narrower, which makes it more trustworthy.
The receipt is part of the product
Webhook reliability is often treated as infrastructure plumbing, but customers experience it as product trust. They care whether events arrive, whether duplicates are controlled, whether failures are visible, and whether someone can explain what happened. A product that keeps receipts can answer those questions without sending everyone into raw logs.
The practical frame is simple: give every delivery a stable identity, record each attempt, classify the response honestly, retry only when the state calls for it, and keep enough evidence for the next person to explain the handoff. Retries move the system forward. Receipts keep it accountable.
0 comments