Webhooks
Event catalogue
Every webhook event you can subscribe to and the payload shape it carries.
Each event uses the same envelope. The shape of data varies
by channel.
{
"id": "whe_…",
"type": "<event.name>",
"created_at": "2025-05-24T12:34:56.789Z",
"data": { ... }
}
Email events
email.sent
Fires when the upstream network accepts the email for delivery. This is the first signal that the message has left ZevSend.
email.delivered
Fires when the recipient’s mail server accepts the message.
email.bounced
Permanent delivery failure — the address is invalid or the inbox refused the message. The recipient is automatically added to your team’s suppression list.
{
"id": "whe_…",
"type": "email.bounced",
"created_at": "…",
"data": {
"message": {
"public_id": "eml_…",
"from_address": "noreply@acme.com",
"to_address": "deleted@example.com",
"subject": "Your receipt",
"status": "bounced",
"sandbox": false,
"sent_at": "…"
},
"reason": "smtp; 550 5.1.1 No such user"
}
}
email.complained
Recipient marked the message as spam. The recipient is automatically added to your team’s suppression list.
email.failed
Delivery permanently failed for a non-bounce reason — e.g. the upstream network rejected the message before submitting.
SMS events
sms.sent
Fires when the network accepts the SMS for delivery.
sms.delivered
Fires when the recipient’s handset confirms receipt via a network delivery report.
{
"id": "whe_…",
"type": "sms.delivered",
"created_at": "…",
"data": {
"message": {
"public_id": "sms_…",
"to_phone": "+2348012345678",
"status": "delivered",
"sandbox": false,
"sent_at": "…"
}
}
}
sms.failed
Synchronous rejection at submit time or a permanent failure
reported by a later delivery report. The reason field holds
the network’s diagnostic where available.
WhatsApp events
whatsapp.sent
Fires when the network accepts the template send.
whatsapp.delivered
Fires when the recipient’s device confirms receipt.
whatsapp.read
Fires when the recipient opens the conversation. This event is best-effort — recipients with read receipts disabled never trigger it.
{
"id": "whe_…",
"type": "whatsapp.read",
"created_at": "…",
"data": {
"message": {
"public_id": "wam_…",
"to_phone": "+2348012345678",
"status": "read",
"sandbox": false,
"sent_at": "…"
}
}
}
whatsapp.failed
Rejected at submit time or a permanent delivery failure. The
reason field surfaces the network’s error message.
Ordering and duplicates
We deliver events in order whenever the network behind them provides ordering, but you should not depend on perfect ordering. Two safe patterns:
- Treat each event as a state assertion, not a transition.
An
email.deliveredfor a message that’s already atbouncedin your store is a stale event and you should ignore it. - Use the
idfield as your dedupe key. We retry on failure, so the same event may arrive more than once.
Updated at, Thursday, October 1, 2026