Skip to main content

messageFailed

The messageFailed webhook event is triggered when EmailEngine gives up on a queued email. It is a terminal event: no further delivery attempts are made for this message.

When This Event is Triggered​

The messageFailed event fires when the delivery job for a queued message ends without success, because:

  • All delivery attempts were used up: the deliveryAttempts of the submission, or the deliveryAttempts setting, or 10 by default
  • An attempt failed permanently, so the remaining attempts were not spent. For SMTP submissions that is a 5xx server reply other than 503, or, without a server reply, an EAUTH, ENOAUTH, EOAUTH2, ETLS, EENVELOPE, EMESSAGE or EPROTOCOL error
  • The gateway the submission named no longer exists (GatewayNotFound). This is decided before any connection is made, so no messageDeliveryError precedes it

The event is sent for every submission type: SMTP, Gmail API and Microsoft Graph API. For SMTP submissions each failed attempt has already produced a messageDeliveryError event with the details of the error; for API submissions this event is the only report of the failure.

Payload Schema​

Top-Level Fields​

FieldTypeRequiredDescription
serviceUrlstring or nullYesThe configured EmailEngine service URL, null if not set
accountstringYesAccount ID that attempted to send the message
datestringYesISO 8601 timestamp when the webhook was generated
eventstringYesAlways messageFailed
dataobjectYesEvent data object (see below)

The event carries no path or specialUse. The unique event identifier is sent as the HTTP header X-EE-Wh-Event-Id, not in the JSON payload.

Data Object Fields​

FieldTypeRequiredDescription
messageIdstringYesMessage-ID header of the email that failed to send
queueIdstringYesEmailEngine's queue ID for this submission, the same value the Submit API returned
errorstringYesFirst line of the stack trace recorded for the message's first failed attempt, so it starts with the error class name, as in Error: <message>. When a message fails the same way every time, which is the common case, that is also the final error
networkRoutingobject or nullNoLocal address and proxy used for the last SMTP attempt, null when neither was configured. Present for SMTP submissions, absent for submissions handed to the Gmail API or the Microsoft Graph API (see below)

There is no envelope on this event. Correlate by queueId or messageId with the messageSent or messageDeliveryError events, or with your own record of the submission.

Network Routing Object Structure​

FieldTypeDescription
localAddressstringLocal IP address used for the SMTP connection
proxystringProxy URL used for the connection
namestringHostname used in the EHLO greeting
requestedLocalAddressstringThe localAddress requested in the submission, when a different one was used

Example Payload​

Authentication Failure​

{
"serviceUrl": "https://emailengine.example.com",
"account": "user123",
"date": "2025-10-17T08:15:32.456Z",
"event": "messageFailed",
"data": {
"messageId": "<305eabf4-9538-2747-acec-dc32cb651a0e@example.com>",
"queueId": "183e4b18f0ffe977476",
"error": "Error: Invalid login: 535 5.7.8 Error: authentication failed",
"networkRouting": null
}
}

Connection Timeout​

{
"serviceUrl": "https://emailengine.example.com",
"account": "user123",
"date": "2025-10-17T09:30:15.123Z",
"event": "messageFailed",
"data": {
"messageId": "<abc123@example.com>",
"queueId": "184a5c29e1aaf988567",
"error": "Error: Connection timeout",
"networkRouting": null
}
}

Recipient Unknown​

{
"serviceUrl": "https://emailengine.example.com",
"account": "user123",
"date": "2025-10-17T10:45:00.000Z",
"event": "messageFailed",
"data": {
"messageId": "<msg-456@company.com>",
"queueId": "184b6d30f2bbg099678",
"error": "Error: Can't send mail - all recipients were rejected: 550 5.1.1 The email account that you tried to reach does not exist",
"networkRouting": null
}
}

With Network Routing Information​

Every SMTP submission carries the field. It is null unless a local address or a proxy was used:

{
"serviceUrl": "https://emailengine.example.com",
"account": "user123",
"date": "2025-10-17T11:22:33.789Z",
"event": "messageFailed",
"data": {
"messageId": "<campaign-789@marketing.com>",
"queueId": "184c7e41g3cch110789",
"error": "Error: connect ECONNREFUSED 203.0.113.25:587",
"networkRouting": {
"localAddress": "192.168.1.100",
"name": "mail.company.com"
}
}
}

Field Details​

messageId​

The Message-ID header from the original email. This is an RFC 5322 compliant identifier that can be used to:

  • Correlate with the original submission request
  • Match with any messageDeliveryError events that preceded this failure
  • Track the message through your email pipeline

Format: <unique-identifier@hostname.domain>

queueId​

EmailEngine's queue identifier for this submission. Use this to:

  • Correlate with the original Submit API response
  • Match with messageDeliveryError events during retry attempts
  • Find the job in the Failed tab of Submission Queue under System > Queues

error​

The first line of the stack trace of the first failed attempt, so it starts with the error class name. A message whose attempts failed for different reasons therefore reports the earliest one here; the messageDeliveryError events carry each attempt's own error, and the job's full stack traces are on the message's page in the queue browser (System > Queues > Outgoing messages > Failed).

Common patterns:

Error PatternDescription
Error: Invalid login: 535...SMTP authentication failure
Error: Connection timeoutUnable to connect to the SMTP server within the timeout
Error: Can't send mail - all recipients were rejected: 550 5.1.1 ...Recipient mailbox does not exist
Error: Can't send mail - all recipients were rejected: 550 5.7.1 ...Sender rejected by the recipient server
Error: connect ECONNREFUSED ...SMTP server refused the connection
Error: certificate has expiredTLS certificate validation failed

Handling the Event​

This is the terminal event for the submission, so the handler closes the record the Submit API's queueId opened and tells whoever submitted the message. For an SMTP submission the preceding messageDeliveryError events carry the error code and server reply; here error is a single line, so keep it as the reason rather than parsing it:

async function handleMessageFailed(event) {
const { account, data, date } = event;

const submission = await db.submissions.findUnique({
where: { queueId: data.queueId }
});

if (!submission) {
// Queued outside this application
return;
}

await db.submissions.update({
where: { queueId: data.queueId },
data: { status: 'failed', failedAt: new Date(date), error: data.error }
});

await notifySubmitter(submission, {
account,
messageId: data.messageId,
error: data.error
});
}

Retry Behavior​

Before messageFailed is sent, the queue retries transient failures with an exponential backoff: 5 seconds after the first failure, doubling each time, with a random reduction of up to 20 percent, for up to 10 attempts by default. The schedule, the deliveryAttempts setting and the distinction between permanent and transient failures are described on the Outbox queue page.

The failed job stays in the submit queue for inspection: the last 500 failed jobs are kept for 7 days by default, see Keep Completed and Failed Jobs. The message itself is no longer queued and cannot be resumed; submit it again to retry.

Relationship to Other Events​

The messageFailed event is the terminal failure state in the email delivery lifecycle:

EventDescription
messageSentSuccessful handoff to the provider
messageDeliveryErrorA failed SMTP attempt, retried or not
messageFailedEmailEngine has given up (this event)
messageBounceBounce notification received after a successful handoff

Difference from messageDeliveryError​

AspectmessageDeliveryErrormessageFailed
When triggeredEach failed SMTP attemptOnce, when the job ends without success
Submission typesSMTP onlySMTP, Gmail API, MS Graph
Retries remainingUsuallyNone
Action requiredMonitor, may self-resolveIntervention needed
Payload detailError code, server response, envelope, retry infoMessage and queue IDs, one error line, network routing

See Also​