Using webhooks with EmailEngine
EmailEngine can notify your application about mailbox, delivery, and tracking events by sending an HTTP POST request—called a webhook—to a URL you define. Each request contains a JSON payload that describes the event. Enable or disable individual events from Configuration → Webhooks.
Event | Name | Description |
---|---|---|
messageNew | New email | A new email was detected in a folder. |
messageDeleted | Message deleted | A previously present email was removed from a folder. |
messageUpdated | Flags updated | Flags of an email were updated. |
messageMissing |
Message missing | An expected message was not found; indicates a sync error. |
messageSent | Message accepted | A queued email was accepted by the MTA. |
messageDeliveryError | SMTP error | Delivery to the SMTP server failed; EmailEngine retries automatically. |
messageFailed | Message bounced | Delivery to the MTA failed permanently. |
messageBounce | Bounce received | A bounce email was received. |
messageComplaint | Complaint received | A feedback‑loop complaint was detected. |
mailboxReset |
Mailbox reset | UIDValidity of a folder changed. |
mailboxDeleted |
Folder deleted | A previously present folder was removed. |
mailboxNew |
New folder | A new folder was detected. |
authenticationError |
Authentication failure | Account authentication failed. |
authenticationSuccess |
Authentication success | Account authenticated successfully. |
connectError |
Connection failure | Connection to the email server failed. |
accountAdded |
Account added | A new account was registered with EmailEngine. |
accountInitialized |
Account initialized | Initial sync completed for an account. |
accountDeleted |
Account deleted | An account was removed from EmailEngine. |
trackOpen | Email opened | A recipient opened an email. |
trackClick | Link clicked | A recipient clicked a link in an email. |
listUnsubscribe | Unsubscribed | A recipient unsubscribed from a list. |
listSubscribe |
Subscribed | A recipient resubscribed to a list. |
messageNew
webhookTriggered whenever a message is added to a folder—whether by delivery, upload, copy, or move. IMAP itself does not distinguish between incoming messages and messages inserted by other means.
Note
The example below has Text content enabled, thereforedata.text.plain
anddata.text.html
are present. By default these fields are omitted. Enable them via Configuration → Webhooks → Text content → Include text content in new email webhooks, or setnotifyText: true
via the Settings API.
{
"serviceUrl": "https://emailengine-dev.apps.srv.dev",
"account": "postalsys",
"date": "2023-03-14T11:13:10.609Z",
"path": "[Gmail]/All Mail",
"specialUse": "\\All",
"event": "messageNew",
"data": {
"id": "AAAAAQABDFI",
"uid": 68690,
"path": "[Gmail]/All Mail",
"emailId": "1760341395373354319",
"threadId": "1760341395373354319",
"date": "2023-03-14T11:12:45.000Z",
"flags": [],
"labels": [
"\\Important",
"\\Inbox"
],
"unseen": true,
"size": 8271,
"subject": "Impressed by the new features of EmailEngine!",
"from": {
"name": "Andris Reinman",
"address": "andris.reinman@gmail.com"
},
"replyTo": [
{
"name": "Andris Reinman",
"address": "andris.reinman@gmail.com"
}
],
"sender": {
"name": "Andris Reinman",
"address": "andris.reinman@gmail.com"
},
"to": [
{
"name": "",
"address": "andris@postalsys.com"
}
],
"messageId": "<CAPacwgwKMETwvftKuQyieSKmp1C+NJcZtqjZ2pSALZkjB-CCbQ@mail.gmail.com>",
"text": {
"id": "AAAAAQABDFKTkaExkaEykA",
"encodedSize": {
"plain": 1535,
"html": 1630
},
"plain": "Dear Postal Systems OÜ,\n\nI hope this email finds you well...\n",
"html": "<div dir=\"ltr\">Dear Postal Systems OÜ,<br><br>I hope this email finds you well...</div>\n",
"hasMore": false
},
"category": "primary",
"isAutoReply": false,
"seemsLikeNew": true,
"messageSpecialUse": "\\Inbox"
},
"_route": {
"id": "AAABhpMgNhsAAAAE"
}
}
\All
; see data.messageSpecialUse
for the logical folder.\Inbox
). Prefer this over specialUse
.Message‑ID
header value.Date
header, in ISO 8601.true
if the message does not have the \Seen
flag.true
if EmailEngine detects an auto‑reply (vacation, bounce, etc.).true
if EmailEngine has no prior record of this message (≈ 99 % accuracy).plain
and html
bodies. Included only when Text content is enabled.All other top‑level fields (from
, to
, sender
, subject
, …) mirror their respective RFC 5322 headers.
Gmail sorts incoming emails into different categories—promotions, social, primary, updates, and forums—and displays these emails under the corresponding category tabs. Over IMAP, all of these emails still appear in the INBOX
folder. EmailEngine can resolve the category of incoming emails and include this information in the payload of the messageNew
webhook.
Category resolution is not enabled by default. To enable it, navigate to Configuration → Service, and then to the Labs section. Mark the Resolve Gmail categories checkbox and save the settings.
Now, whenever an email lands in the inbox of an email account that EmailEngine tracks, the messageNew
webhook for that email includes a property called category
, whose value is one of the following: social, promotions, updates, forums, or primary.
messageDeleted
Emitted when a message disappears from a folder—either permanently deleted or moved elsewhere.
{
"account": "pangalink",
"date": "2022-01-17T11:54:02.089Z",
"path": "[Gmail]/All Mail",
"specialUse": "\\All",
"event": "messageDeleted",
"data": {
"id": "AAAAAQAANJc",
"uid": 13463
}
}
messageUpdated
Emitted whenever EmailEngine detects that an existing message has changed — most often when its flags are updated (for example, when the user marks the message read or unread).
{
"account": "pangalink",
"date": "2022-01-17T11:56:13.146Z",
"path": "[Gmail]/All Mail",
"specialUse": "\\All",
"event": "messageUpdated",
"data": {
"id": "AAAAAQAANJY",
"uid": 13462,
"changes": {
"flags": {
"added": ["\\Seen"],
"removed": []
}
}
}
}
changes.flags.added
and changes.flags.removed
contain only the deltas: the flags added to or removed from the message since the previous state.
messageSent
Emitted after EmailEngine has successfully handed the message off to the upstream mail server (MTA). The webhook is generated as soon as the server acknowledges acceptance.
{
"account": "<accountId>",
"date": "<ISO-8601 timestamp>",
"event": "messageSent",
"data": {
"messageId": "<final Message-ID>",
"response": "<raw SMTP or API response>",
"envelope": {
"from": "<sender address>",
"to": ["<recipient address>"]
}
}
}
Some MTAs (e.g. Amazon SES, AWS WorkMail, Microsoft Graph) replace the original Message‑ID header. When EmailEngine detects this, it adds originalMessageId
to the payload while messageId
is updated to the value returned by the server.
{
"account": "<accountId>",
"date": "<ISO-8601 timestamp>",
"event": "messageSent",
"data": {
"messageId": "<rewritten Message-ID>",
"originalMessageId": "<Message-ID generated by EmailEngine>",
"response": "<raw SMTP or API response>",
"envelope": {
"from": "<sender address>",
"to": ["<recipient address>"]
}
}
}
messageId
always contains the final value stored on the message accepted by the remote server.
Emitted when EmailEngine abandons delivery after all retry attempts fail.
Because the message is retried in the background, this webhook can be delayed until the final attempt has completed.
{
"account": "example",
"date": "2021-12-23T11:58:50.181Z",
"event": "messageFailed",
"data": {
"messageId": "<97ac5d9a-93c7-104b-8d26-6b25f8d644ec@ekiri.ee>",
"queueId": "610c2c93e608bd37",
"error": "Error: Invalid login: 535 5.7.8 Error: authentication failed: "
}
}
Emitted when EmailEngine detects an ARF‑formatted feedback loop (FBL) report in Inbox. The report is parsed and posted as a messageComplaint
webhook.
Field coverage depends on the reporting provider. Headers such as Message‑ID may be missing.
{
"serviceUrl": "http://127.0.0.1:3000",
"account": "example",
"date": "2022-09-27T12:33:42.635Z",
"event": "messageComplaint",
"data": {
"complaintMessage": "AAAAAQAABvE",
"arf": {
"source": "Hotmail",
"feedbackType": "abuse",
"abuseType": "complaint",
"originalRcptTo": [
"recipient@hotmail.co.uk"
],
"sourceIp": "1.2.3.4",
"arrivalDate": "2021-10-22T13:04:36.017Z"
},
"headers": {
"messageId": "<67d3846d-b819-9c54-5053-a81fdc432425@example.com>",
"from": "user@example.com",
"to": [
"recipient@hotmail.co.uk"
],
"subject": "Hello world",
"date": "2021-10-22T16:04:33.000Z"
}
}
}
Emitted after every failed delivery attempt. EmailEngine keeps retrying until either the message is delivered or the maximum number of attempts is reached. You receive one messageDeliveryError
webhook per attempt.
{
"serviceUrl": "http://127.0.0.1:3000",
"account": "example",
"date": "2022-09-14T15:07:35.832Z",
"event": "messageDeliveryError",
"data": {
"queueId": "1833c8a88a86109a1bf",
"envelope": {
"from": "andris@ekiri.ee",
"to": [
"andris@ethereal.email"
]
},
"messageId": "<29e26263-7125-ff56-4f80-83a5cf737d5e@ekiri.ee>",
"error": "400 Message Not Accepted",
"errorCode": "EPROTOCOL",
"smtpResponseCode": 400,
"job": {
"attemptsMade": 1,
"attempts": 10,
"nextAttempt": "2022-09-14T15:07:45.465Z"
}
}
}
Emitted when EmailEngine receives a bounce (DSN) message.
messageId
but no internal id
. Use Message‑ID to correlate with the original email.messageBounce
event for each recipient.Field coverage depends on the bounce format and what EmailEngine can parse.
{
"account": "pangalink",
"date": "2021-11-28T10:17:29.728Z",
"event": "messageBounce",
"data": {
"bounceMessage": "AAAAAQAABWw",
"recipient": "failed.recipient@example.com",
"action": "failed",
"response": {
"source": "smtp",
"message": "550 5.1.1 <failed.recipient@example.com>: Recipient address rejected: User unknown in relay recipient table",
"status": "5.1.1"
},
"mta": "mx.example.com",
"queueId": "BFC608226A",
"messageId": "<4dbe4a40f37e9c2ba5b25912bc7c8997@example.com>"
}
}
Emitted when the tracking pixel embedded in the email is requested.
Requires Configuration → Service → Labs → Track opens and clicks to be enabled.
False positives are possible, for example when a webmail client caches linked images.
{
"account": "example",
"date": "2022-03-24T08:28:32.992Z",
"event": "trackOpen",
"data": {
"messageId": "<2d4696ea-cb47-7af4-0bc3-81ea7a8008be@example.com>",
"remoteAddress": "1.2.3.4",
"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.83 Safari/537.36"
}
}
Emitted when a tracked link is requested.
Requires Configuration → Service → Labs → Track opens and clicks to be enabled.
False positives may occur when security software or link scanners pre‑fetch URLs.
{
"account": "example",
"date": "2022-03-24T08:27:24.572Z",
"event": "trackClick",
"data": {
"messageId": "<49b6cdb1-5d43-e100-0833-bd61867b7bb3@example.com>",
"url": "https://google.com/",
"remoteAddress": "1.2.3.4",
"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.83 Safari/537.36"
}
}
Emitted when a recipient clicks the List‑Unsubscribe link or when the email client issues a one‑click unsubscribe request.
{
"account": "example",
"date": "2022-03-24T08:27:24.572Z",
"event": "listUnsubscribe",
"data": {
"recipient": "recipient@example.com",
"messageId": "<49b6cdb1-5d43-e100-0833-bd61867b7bb3@example.com>",
"listId": "my-customers-list",
"remoteAddress": "1.2.3.4",
"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.83 Safari/537.36"
}
}
Define additional HTTP headers for webhook requests with the webhooksCustomHeaders
configuration option or via Configuration → Webhooks in the dashboard.
EmailEngine also injects diagnostic headers into every request:
Header | Example value | Description |
---|---|---|
X‑EE‑Wh‑Event‑Id | af8435d9-ceee-4715-be71-08ac9d2dc04a |
UUID for the event. All retries share the same ID. |
X‑EE‑Wh‑Custom‑Route | AAABiL8tBKsAAAAG |
Identifier of the custom webhook route. |
X‑EE‑Wh‑Queued‑Time | 0s |
Time the event spent in the queue before the POST. |
X‑EE‑Wh‑Attempts‑Made | 1 |
Attempt counter. Values > 1 indicate earlier delivery failures. |
X‑EE‑Wh‑Id | 907889 |
ID of the queued webhook entry. |
EmailEngine makes up to 10 delivery attempts per webhook. After the 10th failure the payload is marked undeliverable.
Exponential back‑off, base delay 5000 ms:
delay = 5000 ms × 2^(attempt − 1)
Jobs live in the notify queue (Tools → Bull Board → notify).
Retention limits for completed/failed jobs are set in Configuration → Service → Queue Settings.