For developers

Your mailer already speaks the protocol.

Standard SMTP on seven ports, or a small HTTP API. Keep the library you have; keep the providers you have.

Integration is a settings change in the mailer you already run: Nodemailer, Laravel, Rails, smtplib, or anything else that sends mail today connects with a new host, port, and SMTP credential (the quickstart). There is no Mailway SDK to install, pin, or upgrade.

For development, smtp.mailway.dev captures mail without delivering it: the same credentials, a different hostname. The HTTP API covers stacks where SMTP is awkward, and the SMTP reference documents the relay's protocol behavior, its ports, and exactly what it does to a message.

Developers

No SDK.
That's a feature.

It's standard SMTP — Nodemailer, smtplib, Laravel's mailer, ActionMailer and net/smtp already speak it fluently. Nothing to install today, nothing to rip out later. Prefer HTTP? The HTTP API comes with bearer auth, idempotency keys, and batch send.

  • Change one config block. Ship. That was the migration.
  • TLS on every connection — STARTTLS and implicit TLS.
  • Staging that can't email your customers — point it at smtp.mailway.dev and everything is captured, nothing is delivered until you explicitly release it.
All 7 SMTP ports open — more than any provider in the matrix below
:25:587:465:2525:2587:2465:588

GCP blocks port 25 outright. DigitalOcean blocks 25, 465 and 587 outbound. The alternate ports mean your app sends from wherever it happens to run.

Read the SMTP relay docs →
// Nodemailer — zero changes to your send logic
const transporter = nodemailer.createTransport({
host: "smtp.mailway.net",
port: 587,
auth: {
user: process.env.MAILWAY_USER,
pass: process.env.MAILWAY_PASS,
},
});
SMTP session — smtp.mailway.net:587
S: 220 mailway.net ESMTP
C: EHLO app.yourcompany.com
S: 250-mailway.net Nice to meet you, [203.0.113.7]
S: 250-STARTTLS
C: STARTTLS
S: 220 Ready to start TLS
--- TLS negotiated (TLSv1.3) ---
C: EHLO app.yourcompany.com
S: 250-AUTH PLAIN LOGIN
C: AUTH PLAIN ••••••••
S: 235 Authentication successful
C: MAIL FROM:<billing@yourapp.com>
S: 250 Accepted
C: RCPT TO:<customer@gmail.com>
S: 250 Accepted
C: DATA
S: 354 End data with <CR><LF>.<CR><LF>
C: … .
S: 250 message queued as 0k3j9f2a8c1d4e7b.1
--- Mailway routing (background) ---
→ SendGrid… 535 credentials rejected
↻ retry on Amazon SES in 5 s…
→ SES eu-central-1… accepted (142 ms)
Under the hood

This is the whole
integration

Your app speaks the SMTP it already knows and gets 250 the moment Mailway takes the message. Provider selection, failover, and the delivery timeline all happen after the handoff, and every line of this session is kept with the mail as its SMTP transcript (minus your credentials, which are redacted before anything is written).

S:
Server responses

Standard ESMTP. Every client, library, and framework already knows how to talk to it.

C:
Client commands

Your app sends exactly what it sends today. AUTH, MAIL FROM, DATA — nothing proprietary.

↻
Background routing

After the 250 your app is done. Failover and logging are invisible to the sender — and fully visible to you.

HTTP API

Or just POST it

The same pipeline — failover, timeline, archive — behind one JSON endpoint, for stacks where SMTP is awkward. Send with a fetch(), get back the same mail ID you'll see in the console, the public viewer, and the webhooks.

Keys that say what they are

mw_test_… drives the sandbox, mw_live_… is production. No guessing which environment you just emailed.

Retries that can't double-send

Put an Idempotency-Key on the request and retry as hard as you like — a repeated key returns the original send instead of a duplicate.

Batch without the mystery

Up to 100 messages per request, answered with a 207 and a per-message status — you know exactly which ones made it.

Limits that negotiate

Hit the rate limiter and the 429 carries Retry-After; hit the daily cap and its 429 says it resets at 00:00 UTC; hit a quota and the 402 names which one in a machine-readable error code.

Read the HTTP API docs →

live on api.mailway.net · sandbox on api.mailway.dev

POST /v1/send
curl https://api.mailway.net/v1/send \
-H "Authorization: Bearer mw_live_…" \
-H "Idempotency-Key: invoice-2042-receipt" \
-d '{
"from": "billing@yourapp.com",
"to": "customer@gmail.com",
"subject": "Invoice #2042",
"html": "<h1>Paid…",
"routing": { "strategy": "fallback" }
}'
HTTP/2 202 · accepted
{
"id": "m.01k8w9x3qf7r2tn5vbhc4jm6pd",
"status": "queued",
"paused": false,
"routing": { "provider": null, "strategy": "fallback" }
…
}
Compatibility

Every port. Every cloud.

GCP blocks port 25. DigitalOcean blocks 25, 465 and 587 outbound. Corporate firewalls do whatever they want. Mailway listens on all 7 SMTP ports, so your app connects from wherever it runs — no ticket to the network team required.

STARTTLS
SSL/TLS
Mailway:587:25:2525:2587:588:465:24657/7
Amazon SES:587:25:2587:465:24655/7
SendGrid:587:25:2525:4654/7
Postmark:587:25:25253/7
Mailgun:587:25:2525:4654/7
Mandrill:587:25:2525:4654/7
port open not offered

The alternate ports (2525, 2587, 2465, 588) are the escape hatch when the standard ones are blocked. TLS on every single one.

Your mailer config has
one block to change

Mailway is in private beta, and we onboard teams one at a time. Leave your email and we'll send your invite when it's your turn.

read the docs meanwhile →

join the waitlist — mailway.net

Your invite, one email away.

Mailway is in private beta, and we onboard teams one at a time. Leave your email and your invite arrives when it's your turn.