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": "[email protected]", "destination": ["[email protected]"] }, "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": ["[email protected]"], "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": "[email protected]", "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": "[email protected]" } ] } }

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" } }

Reply

A recipient replied to one of your emails. We catch replies via a unique Reply-To: reply+<token>@mail.splashifypro.com we stamp on every outbound (token encodes the original outbox_id). When the reply lands at our inbound listener, we parse it, look up which outbound it’s responding to, and fire this event.

{ "eventType": "Reply", "mail": { ... }, "reply": { "outbox_id": "f8c5def1-1234-5678-9abc-def012345678", "original_recipient": "[email protected]", "from": "Customer Name <[email protected]>", "subject": "Re: Your order has shipped", "message_id": "<[email protected]>", "in_reply_to": "<[email protected]>", "references": "<[email protected]>", "text_body": "Thanks! When can I expect delivery?...", "html_body": "<div dir=\"ltr\">Thanks! When can I expect...", "received_at": "2026-05-04T09:15:30.123Z" } }

text_body is truncated to 32 KB and html_body to 64 KB — long quoted-thread replies stay inside the webhook envelope without bloating it. Use in_reply_to + references for thread correlation on your side.

Disabling reply capture. If you’d rather replies go directly to the address in your From header (or your own Reply-To if you set one), pass Reply-To: [email protected] on your send request — when the field is non-empty we won’t override it. Replies then route directly to your address and we never see them.

Replies that were already in flight when you change the setting still arrive at the original reply+<token> address until the recipient updates their thread.

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": ["[email protected]"] } }

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.