Webhook events
Every event has the same envelope. data always holds the four fields below, plus the ones of its type.
{ "type": "email.bounced", "timestamp": "2026-09-30T12:00:03.000Z", "data": { "email_id": "em_4b9b3c1f0a2e7d5c9e8f1a2b", "from": "hello@example.com", "recipient": "old.address@example.org", "subject": "Your September invoice", "bounce": { "type": "hard", "response": "550 5.1.1 User unknown", "asynchronous": false, "reporting_mta": null }, "suppressed": true }}| Field | Type | Description |
|---|---|---|
type |
string | One of the types below. |
timestamp |
string | When the event happened, ISO 8601 in UTC. |
data.email_id |
string | The id of the email. |
data.from |
string | The sender address. |
data.recipient |
string | The recipient this event is about. An email to three recipients produces three sets of events. |
data.subject |
string | The subject of the email. |
Emails sent with a test key produce no event.
email.delivered
Section titled “email.delivered”The recipient’s mail server accepted the email.
| Field | Type | Description |
|---|---|---|
data.smtp.host |
string or null | The server that accepted it. |
data.smtp.response |
string | Its answer, such as 250 2.0.0 OK. |
email.delivery_delayed
Section titled “email.delivery_delayed”The recipient’s server refused the email for now, or sent a delay notice later. MailPush keeps trying for 72 hours.
| Field | Type | Description |
|---|---|---|
data.attempt |
number or null | The attempt that was refused. null for a delay notice. |
data.next_attempt_at |
string or null | When the next attempt is planned. |
data.smtp |
object | Present for a refusal during sending: host and response. |
data.report |
object | Present for a delay notice: status, diagnostic and reporting_mta. |
email.bounced
Section titled “email.bounced”The recipient’s server refused the email permanently, during sending or in a bounce message received afterwards.
| Field | Type | Description |
|---|---|---|
data.bounce.type |
string | hard (the address does not exist), mailbox_full, policy (the server blocks the sender or the content), message_too_large or unknown. |
data.bounce.response |
string | The server’s answer. |
data.bounce.asynchronous |
boolean | true when the bounce arrived after the server first accepted the email. |
data.bounce.reporting_mta |
string or null | The server that reported an asynchronous bounce. |
data.suppressed |
boolean | true for a hard bounce: the address joins your suppression list. |
email.complained
Section titled “email.complained”The recipient reported the email as spam, and their mailbox provider told MailPush.
| Field | Type | Description |
|---|---|---|
data.complaint.feedback_type |
string | Usually abuse. |
data.complaint.user_agent |
string or null | The provider’s reporting system. |
data.suppressed |
boolean | Always true: the address joins your suppression list. |
Not every provider reports complaints. Gmail does not send them per recipient.
email.failed
Section titled “email.failed”MailPush could not send the email to this recipient: the email could not be signed, or the server kept refusing it temporarily for 72 hours.
| Field | Type | Description |
|---|---|---|
data.reason |
string | Why it failed. |
email.suppressed
Section titled “email.suppressed”The email was not sent to this recipient because the address is on your account’s suppression list.
| Field | Type | Description |
|---|---|---|
data.suppression.reason |
string | hard_bounce or complaint. |
email.opened
Section titled “email.opened”The tracking image of the email was loaded. Sent only when the email tracks opens, at most once an hour per recipient.
| Field | Type | Description |
|---|---|---|
data.machine |
boolean | true when software loaded the image rather than the recipient. Leave those out of your statistics. |
data.machine_reason |
string or null | apple_privacy (Apple Mail’s prefetch) or scanner. null when machine is false. |
email.clicked
Section titled “email.clicked”A tracked link of the email was followed. Sent only when the email tracks clicks, at most once a minute per link.
| Field | Type | Description |
|---|---|---|
data.url |
string | The original address of the link. |
data.machine |
boolean | true when a security scanner or a robot followed the link. |
data.machine_reason |
string or null | scanner, too_fast (less than 5 seconds after delivery) or burst (another link followed within 1 second). null when machine is false. |
A click by the recipient also dates opened_at in GET /v1/emails/:id, but sends no email.opened event.
webhook.test
Section titled “webhook.test”Sent when you click the test button on an endpoint’s page.
| Field | Type | Description |
|---|---|---|
data.webhook_id |
string | The id of the endpoint. |
data.message |
string | A fixed sentence. |