Skip to Content
WebhooksEvent Types

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.

EventWhen it firesHeader value
SendWe accept the API call + queue the emailSend
DeliveryRecipient MX returns 250 OKDelivery
BounceHard or soft bounceBounce
ComplaintRecipient marks as spam (FBL report)Complaint
OpenRecipient opens the email (pixel loaded)Open
ClickRecipient clicks a link in the emailClick
RejectWe refused to send (suppression list, etc.)Reject
RenderingFailureTemplate variable substitution failedRenderingFailure
DeliveryDelaySoft bounce — will retryDeliveryDelay

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.