Skip to content
Sendtier Docs
Dashboard

SMTP relay

Submit supported transactional email over TLS with your Sendtier API key.

SMTP submission is implemented; the public host rollout and staging checks are still pending. The connection settings below are for that rollout.

Connection settings

SettingValue
Hostsmtp.sendtier.com
STARTTLS ports587 or 2587
Implicit TLS ports465 or 2465
Usernamesendtier
PasswordAPI key with sending or full scope
AuthenticationPLAIN or LOGIN, after TLS 1.2 or newer

Add your From domain first. Live keys (st_live_…) require verified DNS. Test keys (st_test_…) accept mail from an added domain before verification, but the worker discards test mail. Keys are checked again for every message, so revoking one invalidates an already authenticated connection's next submission.

Supported messages

Send plain text, HTML or multipart/alternative, up to 10 MiB including MIME encoding and headers. Decoded content also has a 10 MiB limit. Use one From and 1–50 To recipients. The header To set must equal the SMTP envelope recipient set. Attachments, inline images, Cc, Bcc, Reply-To, Sender, Resent headers and read receipt requests are rejected. SMTPUTF8 and BINARYMIME are not advertised. DATA and BDAT use the same validation. Other ordinary headers are not preserved for delivery; MAIL FROM is syntax-checked, while the header From owns domain verification. The outbound MTA chooses the Return-Path.

Use these optional headers once each:

Idempotency-Key: welcome-user-123
X-Sendtier-Tags: {"category":"welcome"}

The idempotency key is 1–255 printable ASCII bytes without spaces. For 24 hours, repeating the exact message bytes, envelope and key mode with the same tenant key returns the stored reply and email ID. Changing the request returns 554 5.7.0; an in-progress claim returns 451 4.3.0. HTTP and SMTP share the tenant namespace but use different request hashes, so use separate keys for each transport. Client-generated Date, Message-ID or MIME boundaries can change between retries; preserve the serialized message for a replay. Tags must be a JSON object with string values. Both control headers are stored as controls, never delivered.

Reply codes and retries

ReplyMeaning
250 2.0.0 queued as em_…Committed acceptance, not proof of delivery
421 / 454Connection or AUTH admission unavailable/exhausted; reconnect with backoff
451 4.7.0Per-key request limit (10/second, burst 20)
451 4.3.0Temporary dependency failure or idempotent request in progress
452 4.3.1 / 452 4.5.3Sending usage cap / too many recipients
530 / 535AUTH required / invalid credentials
550 5.7.1Sender domain, suppression or authorization policy refused
552 5.3.4Message exceeds a size limit
554 5.6.0 / 554 5.7.0Unsupported/invalid content / idempotency conflict

Retry 4xx with backoff and stable message bytes/idempotency key. Resolve 5xx before retrying. If the connection closes before the final reply, acceptance is uncertain; replay with the same key. Delivery status is available through the API and webhooks.

Nodemailer

Require STARTTLS on 587; for 465 use secure: true. See Nodemailer SMTP settings.

import nodemailer from "nodemailer";
const transport = nodemailer.createTransport({
  host: "smtp.sendtier.com", port: 587, secure: false, requireTLS: true,
  auth: { user: "sendtier", pass: process.env.SENDTIER_API_KEY },
});
await transport.sendMail({
  from: "hello@mail.example.com", to: "user@example.com",
  subject: "Welcome", text: "Your account is ready.",
  headers: { "Idempotency-Key": "welcome-user-123", "X-Sendtier-Tags": '{"category":"welcome"}' },
});

Laravel and Symfony Mailer

These DSNs use implicit TLS. Substitute your API key through protected environment configuration, URL-encoding credentials. Symfony Mailer uses MAILER_DSN; Laravel uses the SMTP mailer's url setting (env('MAIL_URL') in config/mail.php).

# Symfony
MAILER_DSN=smtps://sendtier:YOUR_API_KEY@smtp.sendtier.com:465
# Laravel: select the SMTP mailer and set its URL
MAIL_MAILER=smtp
MAIL_URL=smtp://sendtier:YOUR_API_KEY@smtp.sendtier.com:465?scheme=smtps

Send text/HTML mail without attachments, Cc, Bcc or Reply-To. Laravel's SMTP mailer configuration must read MAIL_URL; older application config files may need that url entry. Symfony's STARTTLS alternative is smtp://sendtier:YOUR_API_KEY@smtp.sendtier.com:587?require_tls=true.

Django

Django SMTP settings:

import os
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = "smtp.sendtier.com"
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_USE_SSL = False
EMAIL_HOST_USER = "sendtier"
EMAIL_HOST_PASSWORD = os.environ["SENDTIER_API_KEY"]
DEFAULT_FROM_EMAIL = "hello@mail.example.com"

Use send_mail for text or EmailMultiAlternatives for text plus HTML. For port 465, set EMAIL_USE_SSL=True and EMAIL_USE_TLS=False; do not enable both.

Rails

Action Mailer SMTP settings:

config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
  address: "smtp.sendtier.com", port: 587, user_name: "sendtier",
  password: ENV.fetch("SENDTIER_API_KEY"), authentication: :plain,
  enable_starttls: true
}

Use text/HTML templates without attachments, Cc, Bcc or Reply-To. This example requires Mail 2.7.1 or newer and mandatory STARTTLS with enable_starttls: true.

Go

Go's standard client supports DATA. This function requires STARTTLS before AUTH:

import (
    "crypto/tls"
    "io"
    "net/smtp"
    "os"
)

func send() error {
    c, err := smtp.Dial("smtp.sendtier.com:587")
    if err != nil { return err }
    defer c.Close()
    if err := c.StartTLS(&tls.Config{ServerName: "smtp.sendtier.com", MinVersion: tls.VersionTLS12}); err != nil { return err }
    if err := c.Auth(smtp.PlainAuth("", "sendtier", os.Getenv("SENDTIER_API_KEY"), "smtp.sendtier.com")); err != nil { return err }
    if err := c.Mail("hello@mail.example.com"); err != nil { return err }
    if err := c.Rcpt("user@example.com"); err != nil { return err }
    w, err := c.Data()
    if err != nil { return err }
    message := "From: hello@mail.example.com\r\nTo: user@example.com\r\nSubject: Welcome\r\nContent-Type: text/plain; charset=utf-8\r\n\r\nYour account is ready.\r\n"
    if _, err := io.WriteString(w, message); err != nil { return err }
    if err := w.Close(); err != nil { return err }
    return c.Quit()
}

On this page