APIs are often about pulling data, but what if you want to receive updates automatically when something changes? That’s where webhooks come in.
Webhooks are a simple but powerful way for services to notify your app in real time. They’re used everywhere: payments, Git repos, auth systems, CRMs, and more.
🔄 What’s a webhook?
A webhook is an HTTP POST request sent from one system to another when an event occurs.
Unlike traditional APIs (which you poll), webhooks are event-driven. The sender is in charge of pushing the data to your app.
Example:
- Stripe → sends a POST to your
/webhooks/stripe
when a payment is successful - GitHub → notifies your CI pipeline when a new commit is pushed
🧱 Basic anatomy
- Event source (e.g. GitHub, Stripe)
- Destination URL (your endpoint)
- Payload (usually JSON with event info)
- Headers (for authentication or signature)
POST /webhooks/stripe HTTP/1.1
Content-Type: application/json
Stripe-Signature: ...
{
"type": "payment_intent.succeeded",
"data": {
"id": "pi_123456",
"amount": 2000
}
}
🛠️ How to build a webhook receiver
1. Create a dedicated route
// Express example
app.post('/webhooks/stripe', express.json(), async (req, res) => {
const event = req.body;
console.log('Received:', event);
res.status(200).send('ok');
});
2. Verify the signature (⚠️ important!)
Always verify the request came from the expected source.
Stripe, GitHub, etc., send a secret-based signature header.
const signature = req.headers['stripe-signature'];
const isValid = verifySignature(signature, req.body, secret);
if (!isValid) return res.status(400).send('Invalid');
> ⚠️ Never trust incoming data without validating the source.
🧪 Test locally with tunneling tools
Webhooks require a public URL, so in local dev, use tools like:
ngrok http 3000
# → https://your-id.ngrok.io/webhooks/stripe
Use that public URL in your webhook provider dashboard.
🔁 Handle retries and idempotency
Webhook senders may retry failed requests.
Best practices:
- ✅ Always respond with
200 OK
as soon as possible - ✅ Make webhook handlers idempotent (safe to run multiple times)
- ✅ Log events and failures for debugging
🔒 Security tips
- ✅ Validate signatures or use a shared secret
- ✅ Never expose webhook routes without auth
- ✅ Avoid long processing in the webhook handler (queue it if needed)
- ✅ Rate-limit incoming requests
✅ Summary
- Webhooks = push-based API calls
- Great for real-time updates without polling
- Easy to build, but require proper security, retries, and testing
- Always validate the sender before trusting data
🧠 Final thought
Webhooks are deceptively simple, until something breaks.
With proper validation, retry handling, and testing in place, you’ll be ready to handle them reliably in any app.
Don’t just receive webhooks, own them.