Webhooks connect systems without constant polling. This guide walks through setting up webhooks from registration to production deployment.
Step 1: Choose Your Endpoint URL
Decide where webhooks will be delivered. This is your public-facing endpoint that receives POST requests.
Requirements:
- Must be publicly accessible via HTTPS
- Should return status 200 quickly
- Needs to handle the webhook payload
For development, use ngrok to expose local servers:
ngrok http 3000
This creates a public URL like https://abc123.ngrok.io. Use this as your webhook URL during development.
For production, deploy a dedicated endpoint:
// Express.js endpoint
app.post('/api/webhooks/petstore', express.json(), (req, res) => {
// Process webhook
res.status(200).send('OK');
});
This endpoint at https://yourapp.com/api/webhooks/petstore receives all notifications.
Step 2: Create the Endpoint Handler
Build the server-side handler to process incoming webhooks.
const express = require('express');
const crypto = require('crypto');
const app = express();
app.use(express.json());
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET;
app.post('/api/webhooks/petstore', (req, res) => {
// 1. Verify signature
const signature = req.headers['x-webhook-signature'];
const payload = JSON.stringify(req.body);
if (!verifySignature(payload, signature, WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
// 2. Extract event details
const { event, data, timestamp } = req.body;
console.log(`Received event: ${event}`);
// 3. Process based on event type
switch (event) {
case 'order.created':
handleNewOrder(data);
break;
case 'order.shipped':
handleOrderShipped(data);
break;
case 'inventory.low':
handleLowInventory(data);
break;
default:
console.log(`Unknown event: ${event}`);
}
// 4. Return quickly
res.status(200).send('OK');
});
function verifySignature(payload, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(`sha256=${expected}`)
);
}
// Start server
app.listen(3000, () => console.log('Webhook server running'));
This handler verifies the signature, processes the event, and responds quickly.
Step 3: Register Your Webhook
Connect your endpoint to the Pet Store API. Use the API or dashboard.
Via API:
curl -X POST "https://api.petstoreapi.com/v1/webhooks" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://yourapp.com/api/webhooks/petstore",
"events": [
"order.created",
"order.shipped",
"order.cancelled",
"inventory.low"
],
"active": true
}'
Via Dashboard:
- Log into the Pet Store API dashboard
- Navigate to Webhooks settings
- Add your URL
- Select events to subscribe to
- Save configuration
Step 4: Test Your Implementation
Verify everything works before deploying to production.
Test with curl:
Send a test payload to your endpoint:
curl -X POST "https://yourapp.com/api/webhooks/petstore" \
-H "Content-Type: application/json" \
-H "x-webhook-signature: sha256=test" \
-d '{
"event": "test.event",
"data": { "message": "Test webhook" },
"timestamp": "2024-01-15T10:00:00Z"
}'
Use the dashboard test feature:
Most APIs provide a test button. Click it to send a sample webhook. Check your logs for the incoming request.
Verify idempotency:
Send the same event twice. Your handler should process it only once:
const processedEvents = new Set();
app.post('/api/webhooks/petstore', (req, res) => {
const eventId = req.body.eventId || req.body.timestamp;
if (processedEvents.has(eventId)) {
console.log(`Duplicate event: ${eventId}`);
return res.status(200).send('Already processed');
}
processedEvents.add(eventId);
// Process the event...
});
Step 5: Handle Failures Gracefully
Production webhooks need robust error handling.
Process asynchronously:
app.post('/api/webhooks/petstore', (req, res) => {
// Return immediately
res.status(200).send('OK');
// Process in background
queueWebhook(req.body).catch(err => {
console.error('Failed to queue webhook:', err);
});
});
Implement dead letter queue:
const webhookQueue = [];
const maxRetries = 3;
const retryDelay = 60000;
async function processWebhook(event) {
try {
await handleEvent(event);
} catch (error) {
event.retryCount = (event.retryCount || 0) + 1;
if (event.retryCount < maxRetries) {
// Retry after delay
setTimeout(() => {
processWebhook(event);
}, retryDelay * event.retryCount);
} else {
// Move to dead letter
await saveToDeadLetterQueue(event, error);
}
}
}
Monitor health:
let webhookSuccessCount = 0;
let webhookFailureCount = 0;
app.post('/api/webhooks/petstore', (req, res) => {
res.status(200).send('OK');
processWebhook(req.body)
.then(() => webhookSuccessCount++)
.catch(() => webhookFailureCount++);
});
// Expose metrics
app.get('/api/health/webhooks', (req, res) => {
res.json({
success: webhookSuccessCount,
failure: webhookFailureCount,
successRate: webhookSuccessCount / (webhookSuccessCount + webhookFailureCount)
});
});
Step 6: Secure Your Endpoints
Production webhooks face attack attempts. Protect your endpoints.
Verify every signature:
app.post('/api/webhooks/petstore', (req, res) => {
const signature = req.headers['x-webhook-signature'];
if (!signature) {
return res.status(401).send('Missing signature');
}
// Verify...
});
Limit by IP if supported:
Some services publish their IP ranges. Configure your firewall:
const ALLOWED_IPS = ['203.0.113.0', '198.51.100.0'];
app.use((req, res, next) => {
const clientIp = req.ip;
if (!ALLOWED_IPS.includes(clientIp)) {
return res.status(403).send('Forbidden');
}
next();
});
Log everything:
app.post('/api/webhooks/petstore', (req, res) => {
console.log('Webhook received:', {
event: req.body.event,
timestamp: new Date().toISOString(),
ip: req.ip,
userAgent: req.headers['user-agent']
});
res.status(200).send('OK');
});
Step 7: Deploy to Production
Move from development to production.
- Deploy your endpoint to a stable server
- Update webhook URL to your production URL
- Enable TLS with valid certificates
- Configure monitoring for failures
- Set up alerts for webhook errors
// Production webhook handler
app.post('/api/webhooks/petstore',
express.json({ limit: '1mb' }),
rateLimit({ windowMs: 60000, max: 100 }),
(req, res) => {
// Handle webhook
}
);
Pet Store API Webhook Configuration
The Pet Store API makes webhook setup straightforward. Register your endpoint, choose events, and start receiving notifications.
Check docs.petstoreapi.com for the complete webhook API. You'll find event details, payload schemas, and signature verification steps.
Webhooks keep your systems synchronized without constant polling. Set them up once and receive updates automatically.