Best Practices
Pro Tip: Following these best practices ensures reliable webhook processing and optimal performance.
1. Endpoint requirements
- HTTPS Only: Webhook endpoints must use HTTPS for security
- Response Time: Respond within 5 seconds to avoid timeouts
- Status Codes: Return 2xx status codes to acknowledge receipt
- Error Handling: Return 4xx/5xx status codes to trigger retry mechanism
2. Idempotency
Webhooks may be resent if no acknowledgment is received. Implement idempotency using:
// Use webhookId to prevent duplicate processing
const processedWebhooks = new Set();
app.post('/webhook', (req, res) => {
const {webhookId} = req.body;
if (processedWebhooks.has(webhookId)) {
return res.status(200).send('Already processed');
}
// Process webhook...
processedWebhooks.add(webhookId);
res.status(200).send('OK');
});3. Async processing
For long-running processes, respond immediately and queue for background processing:
app.post('/webhook', async (req, res) => {
// Respond immediately
res.status(200).send('OK');
// Queue for background processing
await addToQueue('webhook-processing', req.body);
});4. Error monitoring
Implement proper logging and monitoring:
app.post('/webhook', (req, res) => {
try {
// Verify HMAC
if (!verifyWebhook(req.body, req.headers['x-joy-loyalty-hmac-sha256'], SECRET_KEY)) {
console.error('Invalid HMAC signature', {
headers: req.headers,
body: req.body
});
return res.status(401).send('Invalid signature');
}
// Process webhook
processWebhook(req.body);
res.status(200).send('OK');
} catch (error) {
console.error('Webhook processing error', {
error: error.message,
stack: error.stack,
body: req.body
});
// Still return 200 to prevent retries for application errors
res.status(200).send('Error logged');
}
});5. Testing
Test your webhook endpoints thoroughly:
# Test webhook endpoint with sample payload
curl -X POST "https://your-server.com/webhook/points" \
-H "Content-Type: application/json" \
-H "X-Joy-Loyalty-Hmac-Sha256: your_test_signature" \
-H "X-Joy-Loyalty-Topic: points/earned" \
-d '{
"webhookId": "test_webhook_123",
"triggeredAt": "2024-01-15T10:30:00.000Z",
"customer": {
"email": "test@example.com",
"name": "Test Customer",
"shopifyCustomerId": "gid://shopify/Customer/123"
}
}'