Learn
Webhooks
Real-time events sent to your server.
Event types
| Event | When |
|---|---|
email.sent | Accepted for delivery |
email.delivered | Reached the recipient's mail server |
email.delivery_delayed | Temporary problem; we keep retrying |
email.bounced | Permanently rejected (address is suppressed) |
email.complained | Marked as spam (address is suppressed) |
email.opened | Opened (when images load) |
email.clicked | A link was clicked |
email.failed | Could not be sent (e.g. invalid recipient) |
email.scheduled / email.canceled | Scheduling changes |
email.received | An email arrived on a receiving domain |
Payload
{
"type": "email.delivered",
"created_at": "2026-11-14T10:15:02.120Z",
"data": {
"email_id": "4ef9a417-02e9-4d39-ad75-9611e0fcc33c",
"from": "Acme <onboarding@yourdomain.com>",
"to": ["user@example.com"],
"subject": "Hello world",
"tags": { "category": "welcome" }
}
}Verify the signature
Every request carries svix-id, svix-timestamp and svix-signature headers, signed with your endpoint's whsec_… secret. Use the open-source svix library:
import { Webhook } from 'svix';
app.post('/webhooks/email', express.raw({ type: 'application/json' }), (req, res) => {
try {
const event = new Webhook(process.env.WEBHOOK_SECRET).verify(req.body.toString(), req.headers);
// handle event.type …
res.sendStatus(200);
} catch {
res.sendStatus(400);
}
});from svix.webhooks import Webhook
@app.post("/webhooks/email")
def hook():
event = Webhook(os.environ["WEBHOOK_SECRET"]).verify(request.get_data(), request.headers)
return "", 200Retries
If your endpoint doesn't return 2xx, we retry after 5 seconds, 5 minutes, 30 minutes, 2 hours, 5 hours and 10 hours. Recent deliveries and their responses are shown in Webhooks.
Was this page helpful?Send feedback