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. A change to what the listener serves (smtpServerTLSCertificate, tlsProvisioning, tlsHostnames, or serviceUrl) swaps the certificate on the running listener without a restart.

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
smtpServerTLSCertificateThe certificate the listener presents by default, as an ID from the TLS Certificates page: manual, self-signed, acme:<hostname>, or env:smtp. Empty or auto lets EmailEngine choose. v2.80.1 and laternot set
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.

Failed logins are budgeted (v2.79.9 and later). After 20 refused logins for one username from one client address within 5 minutes, further attempts for that pair are answered with 454 Too many failed authentication attempts, try again later until the window passes. A second budget of 100 refused logins per client address, whatever the username, covers attempts whose password is not shaped like an access token, so guessing the global password across account IDs runs out at the same point. Accepted logins do not count.

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. If authentication is switched on while a session opened without it is still connected, the next message on that session is refused with 530 Authentication required.

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.

The other EENGINE_SMTP_TLS_ variables (CA, PASSPHRASE, DHPARAM, CIPHERS, ECDH_CURVE, MIN_VERSION, MAX_VERSION, REJECT_UNAUTHORIZED, REQUEST_CERT) map onto the Node.js TLS option of the same name, and a _FILE suffix reads any of them from a file. The suffix table is on Certificates for EmailEngine's Own Listeners.

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. A message the MIME parser cannot read is refused with a 552 Failed to parse message reply as well, so a conforming client does not keep retrying it.

At most 100 clients are connected at a time, because each connection buffers its message in memory up to the size limit. A further connection is answered with 421 Too many connected clients, try again in a moment, which SMTP clients retry. EENGINE_SMTP_MAX_CLIENTS 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, y, on, enabled, or a non-zero number 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 the queue browser under System > Queues > Outgoing messages

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​

  • Basic sending - The REST equivalent, with every option this page cannot express
  • SMTP gateways - Relaying a submission through a named SMTP relay, selected with the X-EE-Gateway header
  • Outbox queue - Where an accepted message goes next
  • Access tokens - Minting the token used as the SMTP password
  • TLS Certificates - Which certificate this listener presents, and how to get one