Skip to main content

SMTP Server

EmailEngine includes an SMTP submission server. A client connects to it with the standard SMTP protocol, authenticates as one of the registered accounts, and hands over a message; EmailEngine queues that message and delivers it through the account's own SMTP server (or an SMTP gateway), exactly as if it had arrived through the submit API. The server announces itself as EmailEngine MSA.

This is the way in for tools that speak SMTP and nothing else: desktop mail clients, legacy applications, and libraries that take an SMTP host and port.

How It Works

  1. EmailEngine listens on the configured port (default 2525, on 127.0.0.1 unless changed)
  2. The client authenticates with an account ID and either the global SMTP password or an access token
  3. The client submits the message
  4. EmailEngine strips its own X-EE-* control headers, derives the SMTP envelope, and adds the message to the outbox queue
  5. The submit worker delivers it through the account's SMTP server, with the same retries and webhooks as an API submission

The SMTP reply to a successful DATA command names the queue entry, for example 250 Message queued for delivery as 1869c5692565f756b33 (2026-08-26T09:12:23.000Z). The queue ID is the one GET /v1/outbox/{queueId} takes.

Enabling the SMTP Server

Via Web Interface

Open Configuration > SMTP Server in the admin interface, tick Enable SMTP Server, and save. Saving from this page restarts the SMTP worker when the enabled flag, port, listen address, or TLS setting changed, so the new values apply without restarting EmailEngine.

SMTP Server configuration page Enable the SMTP server and set the listen address, port and authentication options

Via Settings API

The same keys are settable through the Settings API:

curl -XPOST "https://emailengine.example.com/v1/settings" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"smtpServerEnabled": true,
"smtpServerPort": 2525,
"smtpServerHost": "0.0.0.0",
"smtpServerAuthEnabled": true,
"smtpServerPassword": "optional-global-password"
}'

The Settings API stores the values but does not restart the SMTP worker. After changing smtpServerEnabled, smtpServerPort, smtpServerHost, or smtpServerTLSEnabled through the API, restart EmailEngine (or save the admin page once). The authentication settings and the PROXY protocol flag are read on every new connection, so those take effect immediately either way.

Configuration Options

SettingDescriptionDefault
smtpServerEnabledStart the SMTP serverfalse
smtpServerPortTCP port to listen on2525
smtpServerHostIP address to bind to. 0.0.0.0 or empty accepts connections on every interface127.0.0.1
smtpServerAuthEnabledRequire AUTH before accepting a messagetrue
smtpServerPasswordGlobal password accepted for any account ID. Leave unset to accept access tokens onlynot set
smtpServerTLSEnabledServe implicit TLS instead of plaintextfalse
smtpServerProxyExpect the PROXY protocol header, for HAProxy send-proxy and similarfalse

On the first start, EENGINE_SMTP_ENABLED, EENGINE_SMTP_PORT, EENGINE_SMTP_HOST, EENGINE_SMTP_SECRET, and EENGINE_SMTP_PROXY seed these settings when they have no stored value yet. They are documented under environment variables; once a value is stored, the setting wins.

Authentication

Username and Password

The SMTP username is always the account ID, the identifier given when the account was registered, not the mailbox address. An email address works as the username only when the account ID happens to be that string.

The password is one of:

  • The global password set in smtpServerPassword, which unlocks any account ID
  • An access token that carries the smtp scope. A token bound to an account is accepted for that account only; a token with restricted permissions must include the SMTP surface

A token without the smtp scope is refused with Access denied, invalid scope.

SMTP settingValue
HostYour EmailEngine host
Port2525 by default, set by smtpServerPort
EncryptionNone, unless you enable TLS
UsernameThe account ID
PasswordThe global SMTP password, or an access token with the smtp scope

PLAIN and LOGIN are the supported AUTH mechanisms. Without TLS the server still allows them over the plaintext connection, so put a TLS-terminating proxy in front of it or enable TLS when the port is reachable from outside the host.

Without Authentication

When smtpServerAuthEnabled is off, the server does not offer AUTH, and the message itself has to say which account sends it: set an X-EE-Account header to the account ID. A message without that header is refused with 451 Sender account ID not provided, can not send mail. The header is removed before delivery.

Configuration Examples

Desktop Email Clients

  1. Go to Account Settings > Outgoing Server (SMTP)
  2. Click Add
  3. Configure:
    • Server Name: emailengine.example.com
    • Port: 2525
    • Connection security: None (or SSL/TLS if TLS is enabled on the server)
    • Authentication method: Normal password
    • Username: Account ID (the identifier assigned when registering the account)
    • Password: Access token with the smtp scope, or the global SMTP password

Programming Languages

Point any SMTP client at the server. The credentials are the same in every case: the account ID as the user name and an access token with the smtp scope as the password.

Using Nodemailer:

const nodemailer = require('nodemailer');

const transporter = nodemailer.createTransport({
host: 'emailengine.example.com',
port: 2525,
secure: false,
auth: {
user: 'example-account',
pass: process.env.EMAILENGINE_TOKEN
}
});

async function sendEmail() {
const info = await transporter.sendMail({
from: '"Sender Name" <sender@example.com>',
to: 'recipient@example.com',
subject: 'Test Email',
text: 'Plain text content',
html: '<p>HTML content</p>',
attachments: [
{
filename: 'document.pdf',
path: './files/document.pdf'
}
]
});

console.log('Message ID:', info.messageId);
}

sendEmail().catch(console.error);

TLS Support

Enabling TLS

Set smtpServerTLSEnabled on the admin page or through the Settings API:

curl -XPOST "https://emailengine.example.com/v1/settings" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"smtpServerTLSEnabled": true
}'

TLS is implicit: the client opens a TLS connection from the first byte, the way port 465 works. STARTTLS is not offered in either mode.

Certificate

With TLS enabled, the certificate the listener presents is decided on the TLS Certificates page (/admin/config/tls): a certificate uploaded there, one ordered from Let's Encrypt, or a self-signed one so the listener always starts. Certificates are held per hostname and selected by SNI, so if your clients connect to a name other than the serviceUrl hostname - smtp.example.com, typically - add it under Additional hostnames and it gets a certificate of its own. TLS Certificates covers the whole flow.

To supply your own certificate instead, set the PEM content (not a file path) in the environment before starting EmailEngine:

EENGINE_SMTP_TLS_KEY="$(cat /path/to/private.key)"
EENGINE_SMTP_TLS_CERT="$(cat /path/to/certificate.crt)"

Since v2.80.0 material supplied this way takes precedence over any certificate EmailEngine manages, for every hostname it covers. Before that, it was read first and then overwritten by the provisioned certificate for the serviceUrl hostname.

Every variable with the EENGINE_SMTP_TLS_ prefix maps onto the Node.js TLS option of the same name:

VariableTLS option
EENGINE_SMTP_TLS_KEYkey, the private key in PEM
EENGINE_SMTP_TLS_CERTcert, the certificate chain in PEM
EENGINE_SMTP_TLS_CAca
EENGINE_SMTP_TLS_DHPARAMdhparam
EENGINE_SMTP_TLS_PASSPHRASEpassphrase for an encrypted key
EENGINE_SMTP_TLS_CIPHERSciphers
EENGINE_SMTP_TLS_ECDH_CURVEecdhCurve
EENGINE_SMTP_TLS_MIN_VERSIONminVersion, for example TLSv1.2
EENGINE_SMTP_TLS_MAX_VERSIONmaxVersion
EENGINE_SMTP_TLS_REJECT_UNAUTHORIZEDrejectUnauthorized, a boolean
EENGINE_SMTP_TLS_REQUEST_CERTrequestCert, a boolean

Features and Limitations

What Works

  • Standard SMTP submission, with PLAIN and LOGIN authentication
  • Implicit TLS when the server is configured for it
  • Multiple recipients across To, Cc, and Bcc
  • Attachments, custom headers, HTML and plain text
  • The same outbox queue, retries, and webhooks as an API submission

Messages larger than 25 MB are refused with 552 Message exceeds fixed maximum message size. EENGINE_MAX_SMTP_MESSAGE_SIZE changes the limit.

EmailEngine Options as Headers

Some submit-API options have header equivalents, because SMTP has no other way to pass them. EmailEngine reads each one and removes it from the message before delivery, so none of them reaches the recipient:

HeaderEquivalentValue
X-EE-AccountThe account path parameterThe account ID. Read only when authentication is disabled; with authentication on, the account is the one that logged in
X-EE-Idempotency-KeyThe Idempotency-Key request headerAny string of up to 1024 characters. A repeated key returns the earlier queue entry instead of queueing a second copy
X-EE-Send-AtsendAtAn ISO 8601 timestamp or a millisecond epoch. For a time in the future, the Date header is rewritten to match
X-EE-Delivery-AttemptsdeliveryAttemptsA number
X-EE-GatewaygatewayA gateway ID
X-EE-Tracking-EnabledtrackOpens and trackClicks togethertrue, yes, or 1 to enable; anything else disables

X-EE-Idempotency-Key was added in v2.52.0.

What Is Not Available

Everything the API expresses as a structured payload rather than a header:

  • Mail merge, which needs a recipient list with per-recipient parameters
  • Templates referenced by ID
  • Reply and forward mode, which needs a reference to a stored message

For those, use the REST API.

Monitoring and Webhooks

Messages sent via the SMTP interface are treated the same as messages sent via the REST API:

  • Queued in the outbox queue, with source set to smtp in the outbox entry
  • Automatic retry logic
  • Webhook notifications (messageSent, messageDeliveryError, messageFailed)
  • Visible in Bull Board under System > Queues

Query queue status (the outbox is a single global queue, not scoped per account):

curl "https://emailengine.example.com/v1/outbox" \
-H "Authorization: Bearer <token>"

When to Use the SMTP Server vs REST API

Use the SMTP Server When:

  • Integrating with legacy systems
  • Using desktop email clients
  • Tools only support SMTP
  • Minimal code changes desired
  • Standard SMTP features sufficient

Use REST API When:

  • Building new applications
  • Need advanced features (mail merge, templates, replies and forwards)
  • Need programmatic control
  • Want detailed delivery tracking

See Also