Webhook Best Practices
This guide covers production-ready patterns for receiving and processing SendAPI webhook events reliably.
Respond Quickly
Your webhook endpoint should return a 200 OK response within 5 seconds. If your processing takes longer, acknowledge the event immediately and offload heavy work to a background queue.
// ✅ Good — acknowledge immediately, process later
app.post('/webhook', (req, res) => {
const event = req.body;
queue.add('process-webhook', event); // Background job
res.status(200).json({ received: true });
});
// ❌ Bad — blocks until processing completes
app.post('/webhook', async (req, res) => {
await processEvent(req.body); // Might take 10+ seconds
res.json({ received: true });
});Handle Duplicates (Idempotency)
SendAPI may deliver the same event more than once (e.g., during retries). Use the event id to deduplicate:
app.post('/webhook', async (req, res) => {
const eventId = req.body.id;
// Check if we've already processed this event
const exists = await db.webhookEvents.findOne({ eventId });
if (exists) {
return res.status(200).json({ received: true }); // Already processed
}
// Store and process
await db.webhookEvents.create({ eventId, data: req.body });
await processEvent(req.body);
res.status(200).json({ received: true });
});Verify Event Authenticity
When SDK support is available, verify webhook signatures to ensure events are genuinely from SendAPI and haven't been tampered with. Until then, restrict your webhook endpoint by IP or use a secret URL path.
Event Types
| Event | Description |
|---|---|
sms.delivered | SMS was successfully delivered to the handset |
sms.failed | SMS delivery failed |
whatsapp.message.sent | WhatsApp message was sent |
whatsapp.message.delivered | WhatsApp message was delivered |
whatsapp.message.read | WhatsApp message was read |
whatsapp.message.received | Inbound WhatsApp message received |
whatsapp.session.connected | WhatsApp session paired successfully |
whatsapp.session.disconnected | WhatsApp session lost connection |
email.delivered | Email was delivered to the inbox |
email.opened | Recipient opened the email |
email.clicked | Recipient clicked a link |
email.bounced | Email bounced (hard or soft) |
Retry Policy
If your endpoint returns a non-2xx status code or times out, SendAPI retries with exponential backoff:
| Attempt | Delay |
|---|---|
| 1st retry | 1 minute |
| 2nd retry | 5 minutes |
| 3rd retry | 30 minutes |
| 4th retry | 2 hours |
| 5th retry | 12 hours |
After 5 failed attempts, the event is marked as failed and can be manually retried from the dashboard.