Event Types
The Email API fires nine event types. Subscribe to all of them or
just the ones you care about via the matching_event_types array
on the destination config.
| Event | When it fires | Header value |
|---|---|---|
Send | We accept the API call + queue the email | Send |
Delivery | Recipient MX returns 250 OK | Delivery |
Bounce | Hard or soft bounce | Bounce |
Complaint | Recipient marks as spam (FBL report) | Complaint |
Open | Recipient opens the email (pixel loaded) | Open |
Click | Recipient clicks a link in the email | Click |
Reject | We refused to send (suppression list, etc.) | Reject |
RenderingFailure | Template variable substitution failed | RenderingFailure |
DeliveryDelay | Soft bounce — will retry | DeliveryDelay |
Every payload starts with the same envelope:
{
"eventType": "<EventName>",
"mail": {
"timestamp": "ISO8601",
"messageId": "uuid",
"source": "from-address",
"destination": ["to-address"]
},
"<eventTypeLowercase>": { ... }
}Below: the event-specific block for each type.
Send
Fires when the API accepts your call. The email is now in the queue — no MX attempt yet.
{
"eventType": "Send",
"mail": {
"timestamp": "2026-05-03T12:34:56.123Z",
"messageId": "550e8400-e29b-41d4-a716-446655440000",
"source": "hello@yourcompany.com",
"destination": ["customer@example.com"]
},
"send": {}
}Delivery
Fires when the recipient’s mailbox provider accepts the email (250 OK). This is the strongest delivery signal — the receiving server has accepted the message for the recipient’s mailbox.
{
"eventType": "Delivery",
"mail": { ... },
"delivery": {
"timestamp": "2026-05-03T12:34:57.456Z",
"recipients": ["customer@example.com"],
"smtpResponse": "250 OK"
}
}Bounce
Hard bounce (Permanent) means the address is dead — recipient is
auto-added to your suppression list. Soft bounce (Transient) means
mailbox-full / server-down / etc. — we retry up to 3 times before
giving up.
{
"eventType": "Bounce",
"mail": { ... },
"bounce": {
"timestamp": "2026-05-03T12:34:58.789Z",
"bounceType": "Permanent",
"bounceSubType": "no_such_user",
"bouncedRecipients": [
{
"emailAddress": "deadbox@example.com",
"diagnosticCode": "550 5.1.1 user unknown"
}
]
}
}Complaint
Recipient marked the email as spam. We received the FBL (feedback- loop) report from their inbox provider and auto-suppressed the address. Complaints are critical to monitor — high complaint rates trigger reputation-based sending pauses.
{
"eventType": "Complaint",
"mail": { ... },
"complaint": {
"timestamp": "2026-05-03T12:35:10.123Z",
"complaintFeedbackType": "abuse",
"complainedRecipients": [
{ "emailAddress": "customer@example.com" }
]
}
}Open
Recipient opened the email — the 1×1 tracking pixel loaded. Privacy- focused mail clients (Apple Mail, etc.) pre-load the pixel proactively, so opens are a directional signal, not an exact one.
{
"eventType": "Open",
"mail": { ... },
"open": {
"timestamp": "2026-05-03T12:36:00.000Z",
"ipAddress": "203.0.113.42",
"userAgent": "Mozilla/5.0 (iPhone; ...)"
}
}Click
Recipient clicked a tracked link. We rewrite every <a href> in the
HTML body through a redirect proxy that records the click + 302s to
the original URL. Unsubscribe links are NOT tracked.
{
"eventType": "Click",
"mail": { ... },
"click": {
"timestamp": "2026-05-03T12:37:00.000Z",
"ipAddress": "203.0.113.42",
"userAgent": "Mozilla/5.0 (Macintosh; ...)",
"link": "https://yourapp.com/dashboard"
}
}Reject
We refused to send. Common reasons: recipient on suppression list, sandbox limit reached, sending paused.
{
"eventType": "Reject",
"mail": { ... },
"reject": {
"reason": "address on suppression list"
}
}RenderingFailure
Template variable substitution failed — {{variable}} referenced
in template body wasn’t supplied at send time, OR was malformed.
{
"eventType": "RenderingFailure",
"mail": { ... },
"renderingFailure": {
"templateName": "welcome",
"errorMessage": "missing required variable: first_name"
}
}DeliveryDelay
Soft bounce — we’ll retry. Fires once on the first retry-eligible soft bounce so you can surface a “delivery delayed” UI without waiting for the final outcome.
{
"eventType": "DeliveryDelay",
"mail": { ... },
"deliveryDelay": {
"timestamp": "2026-05-03T12:34:59.000Z",
"delayType": "TemporaryFailure",
"delayedRecipients": ["customer@example.com"]
}
}Subscribing to a subset
Want only bounces and complaints? Set matching_event_types on the
destination:
curl ... -d '{
"name": "deliverability-alerts",
"destination_type": "WEBHOOK",
"webhook_url": "https://yourapp.com/webhooks/deliverability",
"webhook_secret": "...",
"matching_event_types": ["bounce", "complaint"]
}'Empty array = subscribe to everything. Specific list = events not on the list are skipped.
Multiple destinations per config set
You can attach multiple webhook destinations to a single config set — one for engagement events (open / click), one for deliverability alerts (bounce / complaint), one for archival (everything). Each destination delivers + retries independently.