What Is a Webhook? How They Work and Why They Matter
A webhook is an HTTP POST request a service sends to your server when something happens. How they work, what you need to receive them, and the failure modes to plan for.
A webhook is an HTTP POST request that an external service sends to your server when something happens. Your payment processor fires one when a charge succeeds, GitHub fires one when code is pushed, and Shopify fires one when an order ships. No polling, no persistent connection, no SDK. Just an HTTP endpoint that receives POST requests.
The basic idea
When an event occurs, the external service sends a POST to a URL you've configured, with a JSON body describing what happened. Your server receives it, processes it, and returns a 2xx response to confirm receipt.
External service Your server
| |
| POST /webhooks/stripe |
| { "type": "charge.succeeded",|
| "data": { ... } } |
|------------------------------>|
| |
| 200 OK |
|<------------------------------|
That's it. No persistent connection, no polling interval, no SDK required. Just an HTTP endpoint that receives POST requests.
Why not just use an API?
With a regular API, you make the request when you need data. Webhooks flip this: the external service makes the request when something happens.
Consider a payment flow. Without webhooks, you'd have to poll your payment processor every few seconds asking "did that payment succeed yet?" With a webhook, Stripe calls your server the moment the charge completes. Your server learns about it in under a second, with no wasted requests in between.
The pattern works for any event-driven integration: a new GitHub commit, a customer cancelling a subscription, inventory dropping below a threshold. Polling for all of these is slow and wasteful. Webhooks are instant and efficient.
What you need to receive a webhook
Three things:
1. A publicly accessible URL. Your server needs an endpoint that accepts POST requests from the internet. During development, tools like ngrok or Cloudflare Tunnel expose your local server over a public URL so you can test without deploying.
2. A handler that responds quickly. Webhook providers have short timeout windows (Stripe gives you 30 seconds, Shopify gives you 5). Your handler should return a 200 immediately and do the actual processing asynchronously. If your handler does database writes, sends emails, or calls other services before responding, you will hit timeouts.
3. Signature verification. Providers sign their requests with an HMAC-SHA256 digest using a shared secret. Always verify this before processing. Without it, anyone who discovers your webhook URL can send fake events.
// Stripe example
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
app.post('/webhooks/stripe', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(req.body, sig, process.env.STRIPE_WEBHOOK_SECRET);
} catch (err) {
return res.status(400).send(`Webhook error: ${err.message}`);
}
// Acknowledge receipt immediately
res.json({ received: true });
// Process asynchronously
processEvent(event).catch(console.error);
});
Use express.raw() (not express.json()) for webhook routes. HMAC verification requires the exact raw request body bytes. JSON middleware that parses and re-serialises the body will break signature verification.
Common webhook patterns
Return 200 first, process second. Queue the raw payload as soon as it arrives and return immediately. Process it in a background job. This keeps your endpoint fast and prevents timeouts from slow downstream operations.
Make your handler idempotent. Providers retry deliveries when your endpoint doesn't respond or returns an error. Your handler may receive the same event twice. Check the event ID before processing and skip it if you've already handled it.
Store the raw event. Log the full payload before processing it. When something goes wrong, having the original event makes debugging much easier. Without it, you're guessing.
What providers send
Each webhook event typically includes:
- An event type (e.g.
payment_intent.succeeded,push,order/created) - A unique event ID
- A timestamp
- A data object with the full resource that changed
The exact structure varies by provider, but the JSON payload almost always has enough context to reconstruct what happened without making additional API calls.
What can go wrong
Your endpoint is temporarily unavailable. Deployments, cold starts, and brief outages happen. Providers retry on failure, but retry policies vary. Some providers give up after a few hours. A missed event may need manual replay.
Signature verification fails. Usually caused by body parsing middleware that modifies the raw bytes before your verification code sees them. Keep the raw body untouched until verification is complete.
Duplicate events. Retries are at-least-once delivery, so the same event can arrive more than once. Idempotency handling in your application prevents double-processing.
Payload size. Most providers send payloads well under 1 MB, but large push events or order objects can get close to limits. Make sure your endpoint doesn't have a restrictive body size cap.
Understanding these failure modes upfront saves a lot of debugging later. Webhooks are simple to receive and powerful at scale, but they require a bit more operational care than a regular API call.
If you want to go deeper: webhook signature verification covers the HMAC pattern in detail, why webhooks fail goes through the most common production problems, webhooks vs polling weighs the trade-offs, and the Stripe and GitHub setup guides cover provider-specific implementation.
Webhook Watchtower captures, inspects, and replays webhooks across all your sources. If you're building a webhook integration and want full visibility into what's arriving and whether it's being delivered, it's worth a look.
Frequently asked questions
What is a webhook?
A webhook is an HTTP POST request that a service sends to a URL you control the moment something happens, like a payment succeeding or code being pushed. Instead of you repeatedly asking an API whether anything has changed, the service notifies you as soon as it does. The event data is delivered in the request body.
What does webhook mean?
The name describes the mechanism: a hook into another system's events, delivered over the web. You register a URL with a provider, and the provider calls that URL whenever a relevant event occurs. It's a push notification for servers rather than phones.
What is the difference between a webhook and an API?
With a normal API you make the request when you want data. With a webhook the provider makes the request to you when there's something new. They're complementary: you often call a provider's API to fetch or confirm details, and receive its webhooks to know when to act.
What do I need to receive a webhook?
A publicly reachable HTTPS endpoint that accepts POST requests and returns a 2xx quickly. In production you also want signature verification so you can trust the sender, plus storage and retries so a delivery is never silently lost. Tools like Webhook Watchtower handle the capture, verification, and retry parts for you.
Share this post
Webhook Watchtower
Stop debugging webhooks in logs.
Capture every event, replay failures, and get alerted when deliveries fail, without changing your existing setup.
Try it free →Related posts
Monitoring Webhook Health: The Metrics That Actually Matter
Uptime doesn't tell you whether webhooks are working. A short list of the signals that actually indicate a healthy webhook pipeline, and what each one catches that the others miss.
Alerting on Webhook Failures Without Alert Fatigue
Notify on every failed delivery attempt and you'll train people to ignore the channel. A pattern for alerting on what actually needs a human, and staying quiet about what doesn't.
IP Allowlisting vs Signature Verification: What Actually Secures a Webhook Endpoint
IP allowlisting feels like a solid first line of defence for a webhook endpoint. It isn't, and understanding why makes the case for signature verification much clearer.