Webhooks
Webhooks
Receive real-time notifications when email events occur.
Overview
Webhooks let you receive real-time HTTP POST notifications when events happen in your send0 account — like an email being delivered, opened, or bounced.
Instead of polling the API, send0 pushes events to your server as they happen.
How it works
- Register a webhook endpoint URL in your dashboard or via the API
- Subscribe to specific event types
- send0 sends a POST request to your URL when events occur
- Your endpoint responds with a
2xxstatus to acknowledge receipt
Webhook payload
Every webhook event has this structure:
{
"id": "evt_3mKp7nQxLvRw",
"object": "event",
"type": "email.delivered",
"created_at": "2026-04-12T10:35:22Z",
"data": {
"id": "em_2xKq9mNpLvRw",
"to": "user@example.com",
"subject": "Welcome!"
}
}Event types
| Event | Trigger | Data fields |
|---|---|---|
email.queued | Email accepted | id, to, subject |
email.delivered | Delivered to inbox | id, to, subject, delivered_at |
email.opened | Recipient opened | id, to, subject, opened_at |
email.clicked | Link clicked | id, to, subject, url, clicked_at |
email.bounced | Email bounced | id, to, bounce_type, reason |
email.complained | Marked as spam | id, to |
email.failed | Delivery failed | id, to, error |
Signature headers
Every webhook request includes these headers for verification:
X-Send0-Signature: t=1712921722,v1=abc123...
X-Send0-Webhook-ID: evt_xxxx
X-Send0-Timestamp: 1712921722Handling webhooks
Express.js
import express from 'express';
import { Send0 } from 'send0';
const app = express();
app.post('/webhooks/send0', express.raw({ type: 'application/json' }), (req, res) => {
const event = Send0.webhooks.verify(
req.body,
req.headers,
process.env.SEND0_WEBHOOK_SECRET!,
);
switch (event.type) {
case 'email.delivered':
console.log(`Email ${event.data.id} delivered to ${event.data.to}`);
break;
case 'email.bounced':
console.log(`Email ${event.data.id} bounced: ${event.data.reason}`);
break;
case 'email.complained':
console.log(`Spam complaint from ${event.data.to}`);
break;
}
res.status(200).json({ received: true });
});Important: Use
express.raw()to get the raw request body. Parsed JSON will cause signature verification to fail.
Retry policy
send0 retries failed webhook deliveries up to 5 times with exponential backoff:
| Retry | Delay |
|---|---|
| 1st | 30 seconds |
| 2nd | 2 minutes |
| 3rd | 15 minutes |
| 4th | 1 hour |
| 5th | 6 hours |
A delivery is considered failed if your endpoint doesn't respond with 2xx within 30 seconds.
Best practices
- Always verify webhook signatures before processing — see Webhook Verification
- Respond with 200 quickly, then process asynchronously
- Make your handler idempotent — you may receive the same event twice
- Use the
idfield to deduplicate events - Log the
X-Request-IDheader for debugging