SMTP relay
Some mail code is not worth rewriting. A Rails app with ActionMailer configured, a WordPress install, a monitoring tool with an SMTP box in its settings: all of those want a host, a port, and a credential, not an SDK. The gateway takes the message, parses the MIME, and hands it to exactly the same POST /v1/emails your API traffic goes through.
The credential is one you already have
The username is the fixed string emailssh and the password is your esh_ API key. There is no second credential to mint, rotate, and forget about, and revoking the key revokes the SMTP access in the same moment. Encryption is not optional: port 587 with STARTTLS or port 465 with implicit TLS, AUTH is refused before the connection is secured, and port 25 is not offered at all.
It is a translator, not a second mail server
The gateway queues nothing, signs nothing, and enforces nothing itself. It parses the message and calls the REST API, which means suppression, quota, idempotency, DKIM signing, and the delivery log are the same code as your API sends rather than a parallel implementation that drifts. A message sent over SMTP appears in the same dashboard, with the same id, answering the same GET /v1/emails/:id.
Rejections are the SMTP codes a retrying client understands
A quota, spend, or rate limit comes back as a 4xx so your mailer retries later instead of dropping the message. A suppressed recipient is 550 5.1.1 and an unverified From domain is 550 5.7.1, both permanent, because retrying either would never work. If the verifier is unreachable it is 454 4.7.0, which is temporary, because refusing permanently on our outage would lose your mail.
Bcc works the way it is supposed to
The envelope recipients from RCPT TO are authoritative and the headers only sort those recipients into to, cc, and bcc. A bcc recipient is not leaked into the headers of anybody else's copy, which is a bug plenty of relays have shipped.
The limits, stated
40 MB of raw MIME per message, advertised as SIZE and enforced during DATA. 50 recipients per transaction. 10 concurrent connections per IP. Five authentication failures in fifteen minutes locks that source out for fifteen minutes. Sockets idle for sixty seconds are closed.
The API
Every one of these exists today and answers to the key you already have. Nothing on this list is planned.
- smtp.emails.sh:587
- STARTTLS. AUTH PLAIN or LOGIN, after the connection is secured.
- smtp.emails.sh:465
- Implicit TLS, for clients that want the connection encrypted from the first byte.
- Username
- emailssh, the same for every account.
- Password
- Your esh_ API key. Revoking it closes SMTP access too.
Rails, with nothing else changed
config/environments/production.rb. Read the key from the environment; do not commit it.
Rails.application.configure do
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
address: "smtp.emails.sh",
port: 587,
user_name: "emailssh",
password: ENV.fetch("EMAILSSH_API_KEY"),
authentication: :plain,
enable_starttls_auto: true
}
endWhat this does not do
Named here rather than left for a trial to discover. If something on this list is how your team works, that is a real reason to pick another product, and we would rather you knew now.
No tags over SMTP
The gateway builds from, to, cc, bcc, subject, text, html, reply_to, headers, and attachments. There is no tags field, so the tag breakdown in analytics is only available to mail sent through the REST API.
No stored templates over SMTP
A template is referenced by a field on the JSON request, and SMTP has no way to express it. Render it in your application and send the result.
No scheduling, idempotency key, or topic
send_at, idempotency_key, and topic are request fields on POST /v1/emails and there is no header that carries them here. A duplicate submission over SMTP is a duplicate send.
Custom headers must start with X-
Anything else is dropped, and X-Emailssh- prefixed headers are refused so a client cannot forge our own metadata.
The separate SMTP credential in the dashboard does not work yet
The schema carries a revocable send-only SMTP credential and it is not wired up: it authenticates a workspace without yielding a token the gateway could send with, so it would issue a password that cannot send. The API-key-as-password path is what works and what is documented.
Questions
Which port should I use?
587 with STARTTLS unless your client only speaks implicit TLS, in which case 465. Port 25 is not offered, and most hosting providers block outbound 25 anyway.
Does SMTP mail show up in the same log as API mail?
Yes, because it is the same send. The gateway calls POST /v1/emails, so a message sent over SMTP has an id, a delivery timeline, webhooks, and suppression behaviour identical to one sent from the SDK.
Why does nodemailer with Gmail stop working when I deploy?
Vercel, Netlify, and Cloudflare block outbound connections on port 587 to arbitrary hosts, and Gmail app passwords are rate limited and not meant for application mail. If you are already on a serverless host, use the REST API rather than the relay; the relay is for code that cannot be changed.
Does the relay have its own quota?
No. It draws on the same quota as the REST API, because it is the REST API. One allowance covers both.
Can I still send attachments?
Yes, up to the 40 MB raw message limit. Inline parts referenced with cid: are kept as inline rather than listed as visible attachments.
Send your first email today.
3,000 emails a month on the transactional side and 1,000 contacts on the marketing side, both free, no card. Enough to run real traffic through it before you decide.