Introduction
Webhooks are HTTP callbacks that enable real-time event notifications. Instead of polling for changes, services push data to your endpoint when events occur. This guide covers webhook implementation and best practices.
What are Webhooks?
Webhooks are user-defined HTTP callbacks triggered by events:
Service A Your Server
β β
β Event occurs β
ββββββββββββββββββββββ>β
β POST /webhook β
β { event: "..." } β
β β
β Process event β
β β
β 200 OK β
β<ββββββββββββββββββββββ
Webhook vs Polling
Polling (inefficient):
// Check every 5 seconds
setInterval(async () => {
const data = await fetch("/api/check");
if (data.changed) {
handleChange(data);
}
}, 5000);
Webhook (efficient):
// Receive instant notification
app.post("/webhook", (req, res) => {
handleEvent(req.body);
res.status(200).send("OK");
});
Webhook Structure
Typical Webhook Payload
{
"event": "user.created",
"timestamp": "2024-12-07T10:00:00Z",
"data": {
"id": 123,
"email": "user@example.com",
"name": "Alice"
}
}
Webhook Headers
POST /webhook HTTP/1.1
Host: your-server.com
Content-Type: application/json
X-Webhook-Signature: sha256=abc123...
X-Webhook-Event: user.created
User-Agent: WebhookService/1.0
Security
1. Signature Verification
Provider signs payload:
const crypto = require("crypto");
function verifySignature(payload, signature, secret) {
const hash = crypto
.createHmac("sha256", secret)
.update(JSON.stringify(payload))
.digest("hex");
return `sha256=${hash}` === signature;
}
app.post("/webhook", (req, res) => {
const signature = req.headers["x-webhook-signature"];
const secret = process.env.WEBHOOK_SECRET;
if (!verifySignature(req.body, signature, secret)) {
return res.status(401).send("Invalid signature");
}
// Process webhook
handleWebhook(req.body);
res.status(200).send("OK");
});
2. HTTPS Only
// β Never accept webhooks over HTTP in production
if (req.protocol !== "https") {
return res.status(400).send("HTTPS required");
}
3. IP Whitelisting
const ALLOWED_IPS = ["192.0.2.0", "203.0.113.0"];
app.post("/webhook", (req, res) => {
const clientIP = req.ip;
if (!ALLOWED_IPS.includes(clientIP)) {
return res.status(403).send("IP not allowed");
}
// Process webhook
});
Implementation
Express.js Webhook Endpoint
app.post("/webhook", async (req, res) => {
try {
// Verify signature
if (!verifySignature(req.body, req.headers["x-signature"])) {
return res.status(401).send("Invalid signature");
}
// Process immediately
res.status(200).send("OK");
// Handle asynchronously
await processWebhook(req.body);
} catch (error) {
console.error("Webhook error:", error);
res.status(500).send("Error");
}
});
Idempotency
const processedEvents = new Set();
app.post("/webhook", async (req, res) => {
const eventId = req.headers["x-event-id"];
// Check if already processed
if (processedEvents.has(eventId)) {
return res.status(200).send("Already processed");
}
// Process event
await handleEvent(req.body);
processedEvents.add(eventId);
res.status(200).send("OK");
});
Best Practices
β Do This
1. Respond quickly
// Acknowledge immediately
res.status(200).send("OK");
// Process asynchronously
setImmediate(() => processWebhook(data));
2. Implement retries
// Provider retries on non-2xx responses
if (processingFailed) {
return res.status(500).send("Retry");
}
3. Log all webhooks
app.post("/webhook", (req, res) => {
console.log("Webhook received:", {
event: req.body.event,
timestamp: new Date(),
payload: req.body,
});
// Process...
});
β Donβt Do This
1. Donβt process synchronously
// β Blocks response
app.post("/webhook", async (req, res) => {
await longRunningProcess(req.body); // Takes 10 seconds
res.status(200).send("OK");
});
// β
Acknowledge immediately
app.post("/webhook", (req, res) => {
res.status(200).send("OK");
processAsync(req.body);
});
2. Donβt trust webhook data
// β No validation
app.post('/webhook', (req, res) => {
await db.users.create(req.body.data); // Dangerous!
});
// β
Validate first
app.post('/webhook', (req, res) => {
const validated = validateSchema(req.body);
await db.users.create(validated);
});
Testing Webhooks
Use our tools:
- Webhook Payload Tester - Test webhook endpoints
Conclusion
Webhooks enable real-time integrations:
Key principles:
- Verify signatures
- Respond quickly
- Process asynchronously
- Handle retries
- Log everything
Benefits:
- Real-time updates
- Efficient (no polling)
- Event-driven architecture
- Scalable
Next Steps
- Test with Webhook Tester
- Learn API Best Practices
- Explore Event-Driven Architecture