On this page
webhook delivery
An event-driven architectural pattern where one service sends automated HTTP POST requests to another service the moment an event occurs, replacing continuous polling.
Reference Card
- Origin/Prior Art: Introduced conceptually in 2007 by Jeff Lindsay as “User-defined HTTP callbacks.”
- Related Practices: Event-driven architecture, Exponential backoff, Idempotency.
- Key Concepts: HTTP POST callbacks, HMAC signatures, Delivery guarantees (At-least-once).
- Primary Failure Modes: Duplicate deliveries causing double-processing, Silent delivery failures, Unauthenticated endpoints.
Introduction
If your application integrates with a payment processor (like Stripe) and needs to know when a customer’s credit card is successfully charged, the application could run a background script that asks the payment processor, “Is it charged yet?” every five seconds. This is called polling. It wastes massive amounts of bandwidth and CPU on both sides, as 99% of the responses will simply be, “No.”
A Webhook reverses this model. Instead of the application asking, the payment processor promises to tell the application exactly when the event happens. The application provides a URL, and the moment the credit card is charged, the payment processor fires an HTTP POST request to that URL containing the transaction data. It is a “push” model that enables instant, event-driven integrations.
Mental Model
Think of waiting for a package delivery. Polling is walking out to your mailbox every five minutes to see if the mail carrier has arrived. It is exhausting and inefficient. A Webhook is the mail carrier ringing your doorbell the exact second they drop the package on your porch. You can relax inside until the event happens.
Operational Pattern
A robust Webhook architecture involves responsibilities for both the Sender and the Receiver.
The Sender (e.g., Stripe, GitHub):
- Maintains a queue of events to send.
- Dispatches an HTTP POST request to the Receiver’s registered URL.
- Expects a strict HTTP
200 OKresponse within a tight timeout window (e.g., 5 seconds). - Implements an exponential backoff retry mechanism (e.g., retrying at 1 hour, 4 hours, 12 hours) if the Receiver is down or returns a
500 Internal Server Error.
The Receiver (Your Application):
- Exposes a public HTTP endpoint specifically designed to catch the incoming payload.
- Instantly acknowledges receipt by returning a
200 OKbefore attempting complex processing (like writing to a slow database). - Validates the cryptographic signature of the payload to ensure it actually came from the trusted Sender.
Failure Modes
The Timeout Trap
If your application receives a Webhook, does 10 seconds of heavy database processing, and then returns the 200 OK, the Sender has likely already timed out at 5 seconds. The Sender assumes the delivery failed and places the event back in the queue to retry later. Your application will receive the exact same Webhook an hour later and process the heavy database transaction a second time. Receivers must acknowledge the payload immediately, place the raw data onto an internal message queue (like RabbitMQ or AWS SQS), and process it asynchronously.
The Double-Charge (Lack of Idempotency)
Because network connections are inherently unreliable, Webhook Senders only guarantee “At-Least-Once” delivery, not “Exactly-Once.” Your application will inevitably receive duplicate Webhooks for the exact same event. If the event is “Credit Card Charged,” and your code grants the user 100 account credits every time it runs, a duplicate Webhook will result in the user getting 200 credits for a single payment. Receivers must be idempotent. They must track the unique event_id provided by the Sender and silently ignore the Webhook if that ID has already been processed.
Security
Because Webhook endpoints must be exposed to the public internet, anyone can send an HTTP POST request to them. An attacker could craft a fake payload saying, “This user just paid $1,000,” and post it to your URL.
To prevent this, Senders must sign the payload using a secret key shared only with the Receiver. The signature is usually sent in a custom HTTP header (e.g., Stripe-Signature). The Receiver must calculate the cryptographic hash (usually HMAC SHA-256) of the incoming payload using the shared secret and verify that it matches the header. If it does not match, the request must be dropped immediately.
Operational Guidance
Always build an administrative dashboard or log table that tracks every incoming Webhook, the timestamp, the payload, and the HTTP status code your application returned. When a customer complains that their payment didn’t process, the first thing an engineer will need to verify is whether the Webhook was ever actually received from the payment processor, or if it was rejected due to an internal error.
Related topics
Sources & further reading
- Stripe Documentation: Webhooks - Stripe