SMTP Servers
Configure SMTP servers for email delivery
An SMTP server is a route out. A project can hold several, arranged into groups that decide which one a given message tries and what it falls back to. The server’s IP is what receivers see, so its reverse DNS and TLS are two of the bulk sender requirements that are met here rather than in Mailyard.
Adding one
POST /api/v1/smtp-servers
name is required and is how you refer to the server everywhere else. host and port are required for an ordinary
SMTP relay — a provider
reached over an API has neither.
curl -X POST http://localhost:3000/api/v1/smtp-servers \
-H "Authorization: Bearer myk_..." \
-H "Content-Type: application/json" \
-d '{
"name": "primary-relay",
"host": "smtp.relay.example.net",
"port": 587,
"username": "acme-industrial",
"password": "'"$SMTP_PASSWORD"'",
"encryption": "starttls",
"allowed_emails": ["noreply@yourdomain.com", "alerts@yourdomain.com"]
}'
username and password are both optional — a relay authenticated by IP address, or an internal one that requires
nothing, is a legitimate configuration. The password is sealed at rest and never returned by any read.
On PATCH /api/v1/smtp-servers/{id} an empty password leaves the stored one in place, so a form can send the field
blank without erasing it. An empty username clears the login.
Encryption Options
| Value | Port | Description |
|---|---|---|
none | 25 | No encryption (not recommended) |
starttls | 587 | Upgrade to TLS after connecting |
ssl | 465 | TLS from the start |
Testing Connections
Verify SMTP credentials and connectivity before sending via POST /api/v1/smtp-servers/{id}/test. This validates the
hostname, port, credentials, and encryption.
A passing test puts an invalid server back to enabled, a failing one marks an enabled server invalid. A
disabled server stays disabled either way - the result is recorded, and only Enable puts it back.
Taken out of rotation
A server that refuses its login (SMTP 535, 534 or 538, or SES rejecting the access key) or presents a certificate
that does not verify is marked invalid by the delivery worker. The message moves on to the next server in the group
and the project gets a notification. An invalid server is not tried again until a test succeeds, which puts it back to
enabled. A temporary refusal such as 454, a timeout or a refused connection does not take a server out.
Editing a server never changes its status. When the edit touches what the dial uses - host, port, encryption, username, password or a provider option - an invalid server’s reason becomes “settings changed”, and it stays out of rotation until the next test passes.
The shared pool and relay nodes are never marked this way - they are not the project’s to fix.
Private network targets are refused
A project’s server cannot point at loopback, RFC 1918 or other reserved address space, and neither can a provider’s
endpoint override. The host is a project member’s choice and the connection test reports what the peer answered, so
without that guard the button is a scanner of the network Mailyard runs in. The check happens when the row is written
and again at every dial, after the name has resolved.
A relay node enrolled into a project is a project server like any other: a private hostname is refused at enrolment
and at every dial. sending.allow_private_smtp_targets lifts it, for a relay that genuinely is on the same network.
The shared pool, its relay nodes included, is never subject to it - an operator placed those.
Sender Restrictions
Use allowed_emails to restrict which sender addresses can use a specific SMTP server. This is useful when different
servers are configured for different brands or departments. Each entry is an exact address or *@domain, and
allowed_domains takes bare domain names. Anything else is refused when the server is saved, since an entry that
matches no sender would only show up as a send with no server to carry it.
Note
Passwords are never returned in API responses.
Common SMTP Providers
| Provider | Host | Port | Encryption |
|---|---|---|---|
| Gmail | smtp.gmail.com | 587 | starttls |
| Outlook | smtp.office365.com | 587 | starttls |
| Amazon SES | email-smtp.us-east-1.amazonaws.com | 587 | starttls |
| Mailgun | smtp.mailgun.org | 587 | starttls |
| Postfix (local) | localhost | 25 | none |
Grouping and order
Servers belong to groups , which is how a send picks one and how failover decides what to try next. A project that never creates a group has exactly one, the default, holding every server - which is the behaviour that existed before groups.
If a project has no server
A project that has configured no SMTP server delivers through the platform’s shared pool , if an administrator has set one up. Adding a server here takes delivery over completely: from that point the pool is not consulted, even if the server you added is disabled or refuses the sender.
Providers
A server row says how it is reached, not only where. Two providers today:
| Provider | How | Fields it asks for |
|---|---|---|
smtp | A dial | host, port, encryption, optional username and password |
ses | The Amazon SES API | region, optional configuration set, optional access key |
Everything else about a row is the same whichever it is: it sits in a group , takes a priority, honours its allowed senders, and takes part in failover. A group can hold both, and failover walks it in the same order - which is what lets an SES row be the primary and a plain relay the fallback.
provider is set when the server is created and cannot be patched afterwards. The credentials mean something different
to each one - an SMTP login on one, an access key on the other - so switching in place would leave the wrong ones in the
fields a PATCH does not touch. Delete and recreate instead.
A row reached over an API shows no host and no encryption, and the console says so rather than leaving the cells blank: “none” under encryption would read as cleartext when the call is HTTPS.