Skip to content

SMTP Submission

Connect any app or service that speaks SMTP.

Replace example addresses with your enrolled sending address and a real recipient address you control. The relay rejects IANA example domains such as example.com as recipients.

Host smtp.atmos.email
Port 587 (STARTTLS)
Security STARTTLS required
Username Your DID, or any sending domain enrolled under it (e.g. yourdomain.com)
Password Your API key (atmos_…)

The key must be allowed to use the sending domain. See creating and limiting API keys.

The username is not your atproto handle. The relay accepts your DID (did:plc:...) or, as a more SMTP-friendly alternative, any sending domain enrolled under that DID. Most clients use the domain because the : in a DID breaks URL-style auth configs (nodemailer, PDS env vars, etc.).

  1. Your app connects to smtp.atmos.email:587 and upgrades to TLS via STARTTLS.
  2. Your app authenticates with SASL PLAIN using your DID or sending domain as the username, and your API key as the password.
  3. Your app sends the message using standard SMTP commands (MAIL FROM, RCPT TO, DATA).
  4. The relay applies dual DKIM signatures, checks warming limits, and delivers to the recipient’s MX.
const transporter = nodemailer.createTransport({
host: "smtp.atmos.email",
port: 587,
secure: false, // STARTTLS
auth: {
user: "yourdomain.com",
pass: "atmos_your-api-key",
},
});
import smtplib
from email.message import EmailMessage
msg = EmailMessage()
msg["From"] = "you@yourdomain.com"
msg["To"] = "friend@example.com"
msg["Subject"] = "Hello"
msg.set_content("Sent via comail")
with smtplib.SMTP("smtp.atmos.email", 587) as server:
server.starttls()
server.login("yourdomain.com", "atmos_your-api-key")
server.send_message(msg)
auth := smtp.PlainAuth("", "yourdomain.com", "atmos_your-api-key", "smtp.atmos.email")
err := smtp.SendMail("smtp.atmos.email:587", auth, "you@yourdomain.com",
[]string{"friend@example.com"}, []byte(msg))

If you run a self-hosted atproto PDS that sends email (verification, notifications), point its SMTP config at the relay:

PDS_EMAIL_SMTP_URL=smtp://yourdomain.com:atmos_your-api-key@smtp.atmos.email:587

The relay handles DKIM signing. No additional DNS records beyond the ones from enrollment.

  • The MAIL FROM domain must match a domain enrolled under the authenticated DID. See Identity and sending domains.
  • Port 465 (implicit TLS) is unavailable. Use port 587 with STARTTLS.
  • Messages hit warming tier rate limits during the first 14 days.
  • The relay adds List-Unsubscribe and List-Unsubscribe-Post headers to explicitly tagged bulk or broadcast mail per RFC 8058. One-click unsubscribes add the recipient to your suppression list. If you already include your own List-Unsubscribe or List-Unsubscribe-Post header in the message, comail detects it and does not inject its own. You own the pair and are responsible for a valid RFC 8058 combination: List-Unsubscribe must contain an HTTPS URI (a mailto: URI may additionally be present) and List-Unsubscribe-Post must be exactly List-Unsubscribe=One-Click. Suppression-list enforcement stays active either way. See Categories for details.
  • Prefer the HTTP API from serverless environments (Cloudflare Workers, Vercel Edge) where raw TCP sockets are unavailable.
  • Accepted messages (250 OK) are fsynced before the response. The relay retries failed deliveries. See Reliability for the retry schedule and status vocabulary.
  • See Troubleshooting for common errors.

Back to docs