Best Practices
1. Always verify signatures
Never process unverified webhook events:
// ✅ Always verify first
app.post('/webhook', (req, res) => {
const result = sdk.getEventFromRequest(req, WEBHOOK_SECRET);
if (!result.isValid) {
return res.status(400).json({ error: result.error });
}
processEvent(result.payload);
});
2. Handle retries gracefully
Make your webhook endpoint idempotent:
const processedEvents = new Set();
app.post('/webhook', (req, res) => {
const result = sdk.getEventFromRequest(req, WEBHOOK_SECRET);
if (!result.isValid) {
return res.status(400).json({ error: result.error });
}
const payload = result.payload;
// Check if already processed
if (processedEvents.has(payload.id)) {
console.log('Event already processed:', payload.id);
return res.json({ received: true });
}
// Process the event
processEvent(payload);
// Mark as processed
processedEvents.add(payload.id);
res.json({ received: true });
});
3. Respond quickly
Return HTTP 200 as soon as possible to avoid timeouts:
app.post('/webhook', (req, res) => {
const result = sdk.getEventFromRequest(req, WEBHOOK_SECRET);
if (!result.isValid) {
return res.status(400).json({ error: result.error });
}
// Respond immediately
res.json({ received: true });
// Process asynchronously
setImmediate(() => {
processEvent(result.payload);
});
});
4. Log events
Keep logs for debugging and monitoring:
app.post('/webhook', (req, res) => {
const result = sdk.getEventFromRequest(req, WEBHOOK_SECRET);
if (!result.isValid) {
console.error('Webhook validation failed:', result.error);
return res.status(400).json({ error: result.error });
}
const payload = result.payload;
console.log('Event received:', payload.id, payload.event.name);
processEvent(payload);
res.json({ received: true });
});
5. Secure your secret
Store webhook secrets securely:
// ✅ Use environment variables
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET;
if (!WEBHOOK_SECRET) {
throw new Error('WEBHOOK_SECRET environment variable is required');
}
Troubleshooting
Common Issues
Signature verification fails
- Check that you're using the correct webhook secret
- Ensure the payload hasn't been modified
- Verify the signature header is being read correctly
Events not being received
- Check that your webhook URL is accessible from the internet
- Verify your webhook is enabled in MOUNTAIN
- Check your server logs for errors
Duplicate events
- Implement idempotency using event IDs
- Check for retry logic in your error handling