Skip to main content

SMTP

The SMTP submission server is a second way to send mail through Zyphr. It is a standard port-587 submission endpoint that authenticates with your Zyphr API key and hands the message straight to the same send pipeline as the Email API. Anything that can speak SMTP — WordPress, Postfix, a legacy app, a network printer, or a language without a Zyphr SDK — can relay through it without writing any HTTP code.

Because it reuses the HTTP sendEmail pipeline, SMTP submission inherits the same rules: a verified sending domain, the shared daily send cap, and server-side DKIM signing. There is no separate quota and no separate configuration to keep in sync — it is the same account, the same keys, the same limits, reached over a different protocol.

Availability

SMTP submission is live at smtp.zyphr.dev:587 (STARTTLS) on every paid plan (Starter and above). It shares the same send pipeline as the Email API, so you can use either ingress — or both — interchangeably with no other changes.

Who can use it

SMTP submission is available on any paid plan (Starter and above). Only Free accounts are rejected — at the AUTH step with 535 5.7.8 (authentication failure), the same response as a bad credential, so it never reveals whether the key itself was valid. To use SMTP, upgrade to any paid plan, then send over the HTTP API or SMTP as you prefer.

Connection details

SettingValue
Hostsmtp.zyphr.dev
Port587 (submission)
EncryptionSTARTTLS (required)
Usernamezyphr
PasswordYour Zyphr API key (zy_live_...)
Auth methodsPLAIN, LOGIN

A few rules the server enforces:

  • STARTTLS is required before AUTH. The server refuses to accept your credentials over a cleartext connection — your client must upgrade to TLS on port 587 first. Every example below enables STARTTLS.
  • The password is your API key — the username does not matter. Your API key (zy_live_... for production, zy_test_... for test mode) is the only credential; it alone determines which account and project the mail is sent from. The username field is required by the SMTP protocol but is not validated, so set it to any non-empty value — we recommend the literal zyphr. Use a stable value: the per-account rate limit is keyed on the username when one is provided.
  • The From domain must be verified. Just like the HTTP API, the From: address must use a domain you have verified on your account. See Domain Verification.
  • Don't sign your own DKIM. Zyphr signs outbound mail with your verified domain's DKIM key. Any DKIM-Signature header on the message you submit is stripped before sending, and Zyphr re-signs server-side. Let Zyphr handle it.

Shared limits

SMTP sends draw down the same daily and monthly send allowance as the HTTP API — there is no separate SMTP quota. On top of the plan quota, the submission server applies short-window protections:

  • AUTH-attempt throttling — too many AUTH attempts in a short window return a temporary 454 4.7.0 ("too many authentication attempts, try again later"). This is distinct from the 535 credential rejection: a 454 never reveals whether a credential was valid, it only means you are retrying too fast.
  • Per-tier send-rate ceiling — each account has a per-minute relay ceiling that scales with plan tier (Starter < Pro < Scale < Enterprise). Exceeding it returns a temporary 452 4.3.1 ("sending rate exceeded, try again shortly").

A 4xx response is temporary — back off and retry. A 535 is a permanent authentication failure — fix your credentials or plan tier; retrying won't help.

IP allowlists

If the API key you authenticate with has an IP allowlist configured, it is enforced on SMTP exactly as it is on the HTTP API. A connection from an address outside the allowlist is rejected at AUTH. A key that is blocked over HTTP from a given IP cannot relay over SMTP from that IP either.

Client configuration

Every example uses the same four settings: host smtp.zyphr.dev, port 587, STARTTLS, username zyphr, password = your Zyphr API key.

Postfix

Configure Postfix to relay all outbound mail through Zyphr. In main.cf:

relayhost = [smtp.zyphr.dev]:587
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
smtp_tls_security_level = encrypt
smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt

Create /etc/postfix/sasl_passwd with your credentials (username, then your API key as the password):

[smtp.zyphr.dev]:587 zyphr:zy_live_your_api_key

Then secure and load the map and reload Postfix:

sudo postmap /etc/postfix/sasl_passwd
sudo chmod 600 /etc/postfix/sasl_passwd /etc/postfix/sasl_passwd.db
sudo systemctl reload postfix

Node.js (nodemailer)

import nodemailer from 'nodemailer';

const transport = nodemailer.createTransport({
host: 'smtp.zyphr.dev',
port: 587,
secure: false, // STARTTLS is negotiated on 587 (not implicit TLS)
requireTLS: true,
auth: {
user: 'zyphr', // any non-empty value; the key is the credential
pass: process.env.ZYPHR_API_KEY, // zy_live_...
},
});

await transport.sendMail({
from: 'hello@yourapp.com', // must be a verified domain
to: 'user@example.com',
subject: 'Welcome!',
html: '<h1>Welcome to our app!</h1>',
text: 'Welcome to our app!',
});

Python (smtplib)

import os
import smtplib
from email.message import EmailMessage

msg = EmailMessage()
msg["From"] = "hello@yourapp.com" # must be a verified domain
msg["To"] = "user@example.com"
msg["Subject"] = "Welcome!"
msg.set_content("Welcome to our app!")
msg.add_alternative("<h1>Welcome to our app!</h1>", subtype="html")

with smtplib.SMTP("smtp.zyphr.dev", 587) as smtp:
smtp.starttls() # required before AUTH
smtp.login("zyphr", os.environ["ZYPHR_API_KEY"]) # username ignored; key is the credential
smtp.send_message(msg)

PHP (PHPMailer)

use PHPMailer\PHPMailer\PHPMailer;

$mail = new PHPMailer(true);
$mail->isSMTP();
$mail->Host = 'smtp.zyphr.dev';
$mail->Port = 587;
$mail->SMTPAuth = true;
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->Username = 'zyphr'; // any non-empty value
$mail->Password = getenv('ZYPHR_API_KEY'); // zy_live_...

$mail->setFrom('hello@yourapp.com'); // must be a verified domain
$mail->addAddress('user@example.com');
$mail->Subject = 'Welcome!';
$mail->isHTML(true);
$mail->Body = '<h1>Welcome to our app!</h1>';
$mail->AltBody = 'Welcome to our app!';

$mail->send();

WordPress (WP Mail SMTP)

Using the WP Mail SMTP plugin, choose Other SMTP as the mailer and enter these settings:

FieldValue
SMTP Hostsmtp.zyphr.dev
EncryptionTLS (STARTTLS)
SMTP Port587
AuthenticationOn
SMTP Usernamezyphr
SMTP PasswordYour Zyphr API key (zy_live_...)
From EmailAn address on your verified domain

Set the From Email to an address on a domain you have verified in Zyphr, or mail will be rejected.

Migrating from another provider

If you already relay through another provider's SMTP endpoint, migration is a configuration change — point the host at Zyphr, swap the password for your Zyphr API key, and make sure your From domain is verified. Most providers expose the same 587 + STARTTLS settings, so only the values change:

SettingResend / Postmark / SendGrid / MailgunZyphr
Hostsmtp.resend.com / smtp.postmarkapp.com / smtp.sendgrid.net / smtp.mailgun.orgsmtp.zyphr.dev
Port587 (STARTTLS)587 (STARTTLS)
Usernameprovider-specific (e.g. apikey, a server token, or SMTP user)zyphr
Passwordprovider API key / SMTP passwordYour Zyphr API key (zy_live_...)
Plan requirementAny paid plan (Starter and above)
From domainverified on that providerverified on Zyphr

What to check after switching:

  • Verify your sending domain on Zyphr before cutting over, so DKIM and the From-domain check pass. See Domain Verification.
  • Remove any DKIM signing you configured for the old provider. Zyphr signs server-side and strips inbound DKIM-Signature headers.
  • You must be on a paid plan (Starter or above) — only Free is rejected at AUTH.
  • Send a test message and confirm it appears under Messages in the Dashboard, the same place HTTP API sends show up.

Errors

ResponseMeaningWhat to do
535 5.7.8Authentication failed — bad key, unverified email, or a Free account (SMTP needs a paid plan)Check the API key, confirm your email is verified, and upgrade to a paid plan
454 4.7.0Too many AUTH attempts in a short windowBack off and retry — this is temporary
452 4.3.1Per-account send-rate ceiling exceededSlow your send rate — this is temporary
538 5.7.0AUTH attempted before STARTTLS — Must issue a STARTTLS command firstEnable STARTTLS so the connection upgrades to TLS before you authenticate
530 5.7.0A command (e.g. MAIL FROM / DATA) was issued before authenticatingAuthenticate first — the server requires AUTH before accepting mail
From-domain rejectedThe From: domain is not verified on your accountVerify the domain and use an address on it

Because SMTP relays through the same pipeline as the HTTP API, sent messages, delivery events, and tracking all appear in the same place — see Viewing Messages on the Email page.