Event Reference
Each webhook request is a JSON list of events. Every event has the same shape; data.details holds what's specific to the event type.
Event types
| Type | Sent when |
|---|---|
delivered | The recipient's mail server accepted the email. This doesn't mean it reached the inbox; the server may still file it as spam. |
deferred | The recipient's server temporarily refused the email. SendMero keeps trying, so a delivered, bounced or expired event usually follows. |
bounced | The recipient's server permanently refused the email. |
expired | SendMero stopped trying after the email was deferred for too long. |
complained | The recipient marked the email as spam, where their mailbox provider reports this. |
rejected | SendMero accepted the request but couldn't send the email, for example because the API key was revoked or the sending domain isn't ready. details.reason says why. |
suppressed | Coming soon. The email wasn't sent because the recipient is on your suppression list. |
unsubscribed | The recipient unsubscribed through an unsubscribe link. details.unsubscribe.scope is group (one of your unsubscribe groups, or the sending domain's own group) or workspace (all your mail), and details.unsubscribe.group names the group. |
resubscribed | An unsubscribe was removed, because the recipient switched a group back on or resubscribed, or because you removed the entry in the dashboard. details.resubscribe.scope and details.resubscribe.group say which unsubscribe it was. |
When you choose All events, new types are included automatically, so ignore types your code doesn't know.
Fields
| Field | Type | Meaning |
|---|---|---|
id | string | Unique event ID (evt_…). Use it to ignore duplicates. |
type | string | One of the types above. |
created_at | string | When SendMero recorded the event (ISO 8601, UTC). |
test | boolean | Only present, as true, on events from the Test button. |
data.message_id | string | The id returned when you sent the email (msg_…). |
data.recipient | string or null | The recipient this event is about. |
data.domain | string or null | Your sending domain. |
data.occurred_at | string | When it happened (ISO 8601, UTC). Use this to order events. |
data.attempt | number | The delivery attempt this event came from. |
data.metadata | object | The metadata you sent with the email, or {}. |
data.details | object | Details for this event type, described below. May be {}. |
details
details.smtp
Included when the recipient's mail server replied.
| Field | Type | Meaning |
|---|---|---|
code | number or null | SMTP status code, for example 250 or 550. |
enhanced_code | string or null | Enhanced status code, for example 5.7.1. |
response | string or null | The server's reply text. |
remote_ip | string or null | The address of the server that replied. |
details.bounce
Included on bounced and expired events (expired is always soft).
| Field | Type | Meaning |
|---|---|---|
type | string | hard (the address won't work, stop sending to it), soft (a temporary problem that lasted too long), block (refused because of the sender or content, for example reputation) or admin (refused by a policy). |
classification | string or null | A more specific reason, when known. |
details.reason
A short machine-readable reason, on events that have one but no bounce, such as rejected (for example domain_not_ready:…).
Example: delivered
[
{
"id": "evt_8d2f0c1e5b7a4f6d9e3c2b1a0f9e8d7c",
"type": "delivered",
"created_at": "2026-09-25T10:00:04.512Z",
"data": {
"message_id": "msg_fb8076ffc18c225d4fa617f8b00ed038",
"recipient": "someone@example.com",
"domain": "mail.yourdomain.com",
"occurred_at": "2026-09-25T10:00:03.981Z",
"attempt": 1,
"metadata": { "order_id": "88231" },
"details": {
"smtp": { "code": 250, "enhanced_code": "2.0.0", "response": "OK", "remote_ip": "142.250.27.26" }
}
}
}
]
See Webhooks for setting up endpoints, retries and securing your endpoint.