Skip to main content

messageDeliveryError

The messageDeliveryError webhook event is triggered when EmailEngine encounters an SMTP error while attempting to deliver an email. Unlike messageFailed, this event indicates a temporary failure that may be retried automatically.

When This Event is Triggered

The messageDeliveryError event fires when:

  • The SMTP server returns an error response during message submission
  • A connection timeout occurs while communicating with the SMTP server
  • TLS negotiation fails with the mail server
  • DNS resolution fails for the SMTP hostname
  • Authentication with the SMTP server fails
  • The TCP connection to the SMTP server cannot be established

This event is triggered for each failed delivery attempt. If retries are configured, the message will be re-queued for another attempt. Monitor this event to track delivery issues and diagnose SMTP connectivity problems.

Common Use Cases

  • Delivery monitoring - Track SMTP failures in real-time to identify connectivity issues
  • Alerting - Trigger alerts when delivery errors exceed a threshold
  • Retry tracking - Monitor how many attempts have been made for problematic messages
  • Diagnostics - Log detailed error information for troubleshooting SMTP configuration
  • Failover logic - Switch to backup SMTP servers when primary server errors occur
  • Rate limiting detection - Identify when SMTP servers are rejecting messages due to sending limits

Payload Schema

Top-Level Fields

FieldTypeRequiredDescription
serviceUrlstringNoThe configured EmailEngine service URL
accountstringYesAccount ID that attempted to send the message
datestringYesISO 8601 timestamp when the webhook was generated
eventstringYesEvent type, always "messageDeliveryError" for this event
dataobjectYesEvent data object (see below)

Data Object Fields

FieldTypeRequiredDescription
queueIdstringYesEmailEngine's internal queue ID for this submission
envelopeobjectYesSMTP envelope with sender and recipients
messageIdstringNoMessage-ID header of the email being sent
errorstringYesHuman-readable error message
errorCodestringNoError code (e.g., "ETIMEDOUT", "EAUTH", "ECONNECTION")
smtpResponsestringNoRaw SMTP server response (if available)
smtpResponseCodenumberNoSMTP response code (e.g., 421, 450, 550)
smtpCommandstringNoSMTP command that triggered the error (e.g., "RCPT TO", "DATA")
networkRoutingobjectNoNetwork routing information (if local address or proxy was used)
jobobjectYesJob queue information including retry details

Envelope Object Structure

FieldTypeDescription
fromstringEnvelope sender (MAIL FROM address)
toarrayArray of envelope recipient addresses (RCPT TO)

Network Routing Object Structure

Present only when a custom local address or proxy is configured:

FieldTypeDescription
localAddressstringLocal IP address used for the SMTP connection
proxystringSOCKS proxy URL used for the connection

Job Object Structure

FieldTypeDescription
idstringInternal job ID in the queue system
attemptsMadenumberNumber of delivery attempts made so far
attemptsnumberMaximum number of attempts configured
nextAttemptstringISO 8601 timestamp of the next scheduled retry (or false if no more retries)

Example Payload

Connection Timeout

{
"serviceUrl": "https://emailengine.example.com",
"account": "user123",
"date": "2025-10-17T08:15:32.456Z",
"event": "messageDeliveryError",
"data": {
"queueId": "183e4b18f0ffe977476",
"envelope": {
"from": "sender@example.com",
"to": ["recipient@destination.com"]
},
"messageId": "<305eabf4-9538-2747-acec-dc32cb651a0e@example.com>",
"error": "Request timed out. Possibly a firewall issue or a wrong hostname/port (smtp.destination.com:587).",
"errorCode": "ETIMEDOUT",
"job": {
"id": "42",
"attemptsMade": 1,
"attempts": 10,
"nextAttempt": "2025-10-17T08:20:32.456Z"
}
}
}

Authentication Failure

{
"serviceUrl": "https://emailengine.example.com",
"account": "user123",
"date": "2025-10-17T09:30:15.123Z",
"event": "messageDeliveryError",
"data": {
"queueId": "184a5c29e1aaf988567",
"envelope": {
"from": "sender@example.com",
"to": ["recipient@destination.com"]
},
"messageId": "<abc123@example.com>",
"error": "Authentication failed",
"errorCode": "EAUTH",
"smtpResponse": "535 5.7.8 Authentication credentials invalid",
"smtpResponseCode": 535,
"smtpCommand": "AUTH",
"job": {
"id": "43",
"attemptsMade": 2,
"attempts": 10,
"nextAttempt": "2025-10-17T09:40:15.123Z"
}
}
}

TLS Certificate Error

{
"serviceUrl": "https://emailengine.example.com",
"account": "user123",
"date": "2025-10-17T10:45:00.000Z",
"event": "messageDeliveryError",
"data": {
"queueId": "184b6d30f2bbg099678",
"envelope": {
"from": "sender@company.com",
"to": ["customer@external.com"]
},
"messageId": "<msg-456@company.com>",
"error": "Certificate check for smtp.external.com:465 failed. CERT_HAS_EXPIRED",
"errorCode": "ESOCKET",
"job": {
"id": "44",
"attemptsMade": 1,
"attempts": 10,
"nextAttempt": "2025-10-17T10:50:00.000Z"
}
}
}

DNS Resolution Failure

{
"serviceUrl": "https://emailengine.example.com",
"account": "user123",
"date": "2025-10-17T11:22:33.789Z",
"event": "messageDeliveryError",
"data": {
"queueId": "184c7e41g3cch110789",
"envelope": {
"from": "sender@example.com",
"to": ["user@typo-domain.com"]
},
"messageId": "<dns-err-789@example.com>",
"error": "EmailEngine failed to resolve DNS record for smtp.typo-domain.com",
"errorCode": "EDNS",
"job": {
"id": "45",
"attemptsMade": 3,
"attempts": 10,
"nextAttempt": "2025-10-17T11:42:33.789Z"
}
}
}

SMTP Rejection with Response Code

{
"serviceUrl": "https://emailengine.example.com",
"account": "user123",
"date": "2025-10-17T12:00:00.000Z",
"event": "messageDeliveryError",
"data": {
"queueId": "184d8f52h4ddi221890",
"envelope": {
"from": "sender@example.com",
"to": ["blocked@strict-server.com"]
},
"messageId": "<rejected-msg@example.com>",
"error": "Message rejected by server",
"errorCode": "EMESSAGE",
"smtpResponse": "450 4.7.1 Client host rejected: cannot find your hostname",
"smtpResponseCode": 450,
"smtpCommand": "RCPT TO",
"job": {
"id": "46",
"attemptsMade": 1,
"attempts": 10,
"nextAttempt": "2025-10-17T12:05:00.000Z"
}
}
}

With Network Routing Information

When EmailEngine uses a custom local address or proxy for the SMTP connection:

{
"serviceUrl": "https://emailengine.example.com",
"account": "user123",
"date": "2025-10-17T13:15:45.678Z",
"event": "messageDeliveryError",
"data": {
"queueId": "184e9g63i5eej332901",
"envelope": {
"from": "marketing@company.com",
"to": ["customer@email.com"]
},
"messageId": "<campaign-123@company.com>",
"error": "EmailEngine failed to establish TCP connection against smtp.email.com",
"errorCode": "ECONNECTION",
"networkRouting": {
"localAddress": "192.168.1.100",
"proxy": "socks5://proxy.company.com:1080"
},
"job": {
"id": "47",
"attemptsMade": 2,
"attempts": 10,
"nextAttempt": "2025-10-17T13:25:45.678Z"
}
}
}

Error Codes Reference

Error CodeDescription
ETIMEDOUTConnection timed out - firewall issue or wrong hostname/port
EAUTHSMTP authentication failed
ETLSTLS/SSL negotiation failed
EDNSDNS resolution failed for the SMTP hostname
ECONNECTIONFailed to establish TCP connection to SMTP server
EPROTOCOLUnexpected response from SMTP server
ESOCKETSocket-level error (often TLS certificate issues)
EMESSAGEMessage-related error from SMTP server
ESTREAMStream error during message transmission
EENVELOPEInvalid envelope (sender/recipient)

Handling the Event

Basic Handler

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

console.log(`Delivery error for account ${account}`);
console.log(` Queue ID: ${data.queueId}`);
console.log(` Error: ${data.error}`);
console.log(` Error Code: ${data.errorCode}`);
console.log(` Attempt: ${data.job.attemptsMade}/${data.job.attempts}`);

if (data.job.nextAttempt) {
console.log(` Next retry: ${data.job.nextAttempt}`);
} else {
console.log(` No more retries scheduled`);
}

// Log to your monitoring system
await monitoring.logDeliveryError({
account,
queueId: data.queueId,
error: data.error,
errorCode: data.errorCode,
attempt: data.job.attemptsMade,
timestamp: event.date
});
}

Alert on Repeated Failures

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

// Alert if message has failed multiple times
if (data.job.attemptsMade >= 5) {
await alerting.send({
severity: 'warning',
title: 'Email delivery struggling',
message: `Message ${data.queueId} has failed ${data.job.attemptsMade} times`,
details: {
account,
error: data.error,
errorCode: data.errorCode,
recipients: data.envelope.to
}
});
}

// Track error patterns
await analytics.trackError({
type: 'smtp_delivery_error',
account,
errorCode: data.errorCode,
smtpCode: data.smtpResponseCode
});
}

Detect Authentication Issues

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

// Authentication errors require immediate attention
if (data.errorCode === 'EAUTH') {
await notifyAdmin({
title: 'SMTP Authentication Failed',
message: `Account ${account} failed to authenticate with SMTP server`,
action: 'Check SMTP credentials in account configuration'
});

// Optionally disable the account to prevent further failures
await disableAccountSending(account);
}
}

Retry Behavior

EmailEngine uses exponential backoff for delivery retries:

  1. Default configuration: 10 retry attempts with exponential delay
  2. Backoff formula: 2^attemptsMade * baseDelay (base delay is typically 5 seconds)
  3. Example delays: 10s, 20s, 40s, 80s, 160s, 320s, 640s, 1280s, 2560s, 5120s

The job.nextAttempt field shows when the next retry is scheduled. If nextAttempt is false, no more retries will be attempted.

Permanent vs Temporary Failures

  • 5xx SMTP codes (500-599): Considered permanent failures - no more retries
  • 4xx SMTP codes (400-499): Considered temporary - will be retried
  • Connection errors: Will be retried (may be transient network issues)

When all retries are exhausted or a permanent failure occurs, a messageFailed webhook is triggered instead.

Relationship to Other Events

The messageDeliveryError event is part of the email delivery lifecycle:

  1. Submit API call - Email is queued for sending
  2. messageDeliveryError - Temporary delivery failure (this event, may occur multiple times)
  3. messageSent - Email accepted by SMTP server (success path)
  4. messageFailed - Permanent delivery failure (failure path, no more retries)
Submit API


┌─────────────────────────────────────────┐
│ Delivery Attempt │
└─────────────────────────────────────────┘
│ │
│ Success │ Error
▼ ▼
messageSent messageDeliveryError

│ Retry?
┌─────────┴─────────┐
│ Yes │ No
▼ ▼
[Retry loop] messageFailed

Best Practices

  1. Monitor error patterns - Track error codes to identify systemic issues (DNS, TLS, auth)
  2. Set up alerts - Notify operators when error rates spike or specific errors occur
  3. Log for debugging - Store full webhook payloads to diagnose delivery problems
  4. Handle auth errors specially - Authentication failures often indicate configuration problems
  5. Track retry counts - Know which messages are struggling to deliver
  6. Consider circuit breakers - Temporarily pause sending to problematic SMTP servers
  7. Process quickly - Return 2xx status within 5 seconds

See Also