Webhooks
Updated June 6, 2026Imagine you ordered a package online. You could:
Option A: Call the delivery company every hour — "Is it here yet? What about now?"
Option B: Give them your phone number and say "Call me when it arrives."
Option B is how webhooks work. Instead of your system constantly asking "did something happen?", you register a URL and the other system calls you when it does.
That's the whole idea. Webhooks are HTTP callbacks — a way for one service to notify another the moment an event occurs.
Push vs Pull for Events
The alternative to webhooks is polling: your system repeatedly asks an external API "did anything change?" This is wasteful, adds latency (you only know about events as fast as your polling interval), and puts unnecessary load on both systems.
With webhooks, the source system takes responsibility for delivery. As soon as the event happens, it sends an HTTP POST to your registered endpoint with a payload describing the event.
| Polling | Webhooks | |
|---|---|---|
| Who initiates | Your system | External system |
| Latency | Up to polling interval | Near-instant |
| Efficiency | Wasted requests when no events | Only fires on events |
| Complexity | Simple to implement | Need a publicly accessible endpoint |
| Works if your server is down | Yes (you catch up on next poll) | Events may be lost (unless retried) |
Real Examples
GitHub Webhooks
When you push code to a repo, GitHub fires a webhook to your CI/CD system with the commit details. Your build pipeline starts immediately, with no polling GitHub's API every minute to check for new commits.
Stripe Webhooks
When a payment succeeds, fails, or a subscription renews, Stripe sends a webhook to your backend. Your server listens for payment_intent.succeeded and then fulfills the order, sends a receipt, or updates the subscription status. Without webhooks, you'd have to poll Stripe's API constantly to check payment states.
Twilio
SMS delivery receipts, incoming message notifications, call status updates, all delivered via webhooks to your registered endpoint. Your app knows a text message was delivered the moment Twilio knows.
Why should a webhook endpoint return 200 OK before processing the event instead of after?
Webhook Reliability: The Problems Nobody Talks About
Webhooks are simple in concept but surprisingly tricky to get right in production.
Problem 1: Your Server Might Be Down
If your endpoint is temporarily unavailable when an event fires, you'll miss it. The sending service has to retry. Most serious webhook providers (Stripe, GitHub) have retry logic with exponential backoff. But if your service is down for too long, events may be dropped. Design accordingly.
Problem 2: Duplicate Delivery
Retry logic creates a new problem: duplicate events. If your server processes an event but times out before returning 200 OK, the sender doesn't know it succeeded and retries. Now you've processed the same event twice.
The solution is idempotency. Design your webhook handlers so that processing the same event multiple times has the same effect as processing it once. Stripe events include a unique id — you can track which IDs you've processed and skip duplicates.
Problem 3: Is This Actually From Who It Claims?
A webhook is just an HTTP POST. Anyone who knows your webhook URL can send fake events to it. A malicious actor could send a fake "payment succeeded" event to trigger order fulfillment without actually paying.
The solution is signature verification. Stripe signs every webhook payload with an HMAC-SHA256 signature using a secret key that only you and Stripe know. Your endpoint verifies the signature before trusting the payload:
const signature = request.headers['stripe-signature'];
const event = stripe.webhooks.constructEvent(
request.body,
signature,
process.env.STRIPE_WEBHOOK_SECRET
);Always verify webhook signatures.
Problem 4: Ordering
Webhooks from the same source don't always arrive in order. Network delays mean event B might arrive before event A. Design your handlers to be order-agnostic where possible, or use the event timestamp to detect out-of-order delivery.
Building a Solid Webhook Receiver
Production webhook architecture — verify, queue, process idempotently
A production-grade webhook endpoint should:
- Return 200 immediately — acknowledge receipt fast, before any processing
- Push to a queue — decouple ingestion from business logic
- Verify the signature — never trust unauthenticated payloads
- Be idempotent — handle duplicate events safely
- Log everything — store the raw payload before processing; invaluable for debugging
- Handle retries gracefully — track processed event IDs in a database
What is the purpose of HMAC-SHA256 signature verification in webhook delivery?
Webhook Management at Scale
Once you're sending webhooks (not just receiving them), you inherit the sender's reliability problems:
- What if the recipient's server is down? You need retry logic with exponential backoff
- What if you need to send the same event to multiple endpoints? Consider a fan-out pattern
- How do you debug failed deliveries? You need a delivery log with status codes and timestamps
- How do you handle a slow recipient that times out? Use async delivery workers, not synchronous HTTP calls in your request path
Services like Svix, Hookdeck, and Convoy exist specifically to handle webhook infrastructure so you don't have to build it from scratch.
Your webhook handler receives the same Stripe event twice due to a retry. How do you handle this safely?
Summary
Webhooks are HTTP callbacks. Instead of polling for events, you register an endpoint and the source system calls you when something happens. They're used everywhere: GitHub triggers CI builds, Stripe notifies you of payment events, Twilio delivers SMS status updates. The concept is simple, but production reliability requires signature verification (so you know it's really from the sender), idempotency (so duplicate delivery doesn't corrupt your data), and async processing (so your endpoint acknowledges quickly without timing out). When integrating with external services, webhooks are almost always the right pattern over polling.
Saved on this device only
Sign in to sync progress across devices