DocsComing soon

SMTP

Send through smtp.letr.dev from tools that only speak SMTP, with an API key as the password.

For a framework, an auth provider or an app that can only send over SMTP. An email sent over SMTP is an email like any other: it shows up in the API and the dashboard with the same events and webhooks.

Connection

Setting Value
Host smtp.letr.dev
Port 465 with implicit TLS, or 587 with STARTTLS (required)
Username letr
Password An API key with sending_access or full_access
Authentication PLAIN or LOGIN
Sending over SMTPYour app connects to smtp.letr.dev on port 465 with TLS from the start, or on port 587 and then starts TLS with STARTTLS. Over TLS it sends EHLO, logs in with AUTH PLAIN or LOGIN, the username letr and an API key as the password, and sends the message with MAIL FROM, RCPT TO and DATA. letr.dev answers 250 with the email id and from there handles the email like one from POST /v1/emails.smtp.letr.devYour appalt[port 465][port 587]then the same path as POST /v1/emails,with the same events and webhooksconnect, TLS from the startconnect, EHLOSTARTTLSEHLO over TLSAUTH PLAIN or LOGINusername letr, password: an API keyMAIL FROM, RCPT TO, DATA250 2.0.0 OK: queued as <id>

The key's domain limits apply: a key limited to some domains sends only from those. Use a separate key per tool, so you can roll or revoke one without touching the others. Revoking a key stops open sessions that use it at their next message.

Same rules as the API

A message sent over SMTP goes through the same path as POST /v1/emails: a verified from-domain, the suppression list, 50 recipients and 15 MB per email as sent, measured the same way as on the API, your monthly ceiling, the daily limit and test recipients all apply, and letr.dev adds Feedback-ID and signs with DKIM.

letr.dev keeps your MIME structure and your Message-ID, and turns line endings into CRLF. Before signing, it removes Bcc, Feedback-ID and any other X-Letr-* header than the two below.

Limits

Each key may submit 600 messages a minute over SMTP, a budget of its own next to the account's 600 API requests a minute; above it a MAIL FROM answers 451 4.7.1. Each client network may attempt as many logins a minute as the API's per-minute limit allows, successful ones included (454 4.7.0 above that). A client address holds at most 5 connections at once, each port 200; a session has 30 seconds to authenticate and ends after 10 minutes. Keep a session open and send several messages through it rather than reconnecting per message.

letr.dev headers

  • X-Letr-Tag: key=value sets a tag, as tags does in the API. Repeat the header for more tags, at most 10.
  • X-Letr-Idempotency-Key has the same meaning as the Idempotency-Key HTTP header.
X-Letr-Tag: type=password-reset
X-Letr-Tag: app=web
X-Letr-Idempotency-Key: reset-7f3a2c

Reply codes

When letr.dev refuses a command or a message, the SMTP reply says why:

  • 530: AUTH or MAIL before STARTTLS on port 587, or MAIL without AUTH.
  • 535 5.7.8: unknown, revoked or read_only key.
  • 454 4.7.0: too many logins a minute from your network, successful ones included; see Limits and try again later.
  • 550 5.7.1: the from-domain is not verified, or outside the key's domain limit.
  • 550 5.1.1: the recipient address is malformed, or the recipient is on the suppression list; only that recipient is refused.
  • 452 4.5.3: more than 50 recipients.
  • 552 5.3.4: the message is larger than 15 MB as sent, with the headers and DKIM signature letr.dev adds.
  • 554 5.6.0: the message is malformed, From is missing or not exactly one address, an X-Letr-Tag is invalid, or the X-Letr-Idempotency-Key was already used with a different message.
  • 451 4.7.1: the monthly ceiling or the daily limit is reached, or too many requests; try again later.
  • 451 4.3.0: a temporary internal error; try again later.
  • 554 5.7.0: the account is not active (payment pending or disabled) or paused.

Codes starting with 4 are temporary, and most tools retry them on their own.

Setup

Most examples use port 587 with STARTTLS. Put the API key in a secret store or an environment variable, never in the code.

Supabase Auth

In the Supabase dashboard, open the Auth SMTP settings (/dashboard/project/_/auth/smtp), enable custom SMTP and enter the host, port, username and password from above. The same with the Supabase Management API:

curl -X PATCH "https://api.supabase.com/v1/projects/$PROJECT_REF/config/auth" \
  -H "Authorization: Bearer $SUPABASE_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "smtp_admin_email": "[email protected]",
    "smtp_sender_name": "Acme",
    "smtp_host": "smtp.letr.dev",
    "smtp_port": "587",
    "smtp_user": "letr",
    "smtp_pass": "'"$LETR_API_KEY"'"
  }'

Django

# settings.py
import os

EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = "smtp.letr.dev"
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = "letr"
EMAIL_HOST_PASSWORD = os.environ["LETR_API_KEY"]
DEFAULT_FROM_EMAIL = "Acme <[email protected]>"

For port 465 set EMAIL_USE_SSL = True instead of EMAIL_USE_TLS; Django allows only one of the two.

Rails

# config/environments/production.rb
config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
  address: "smtp.letr.dev",
  port: 587,
  user_name: "letr",
  password: ENV.fetch("LETR_API_KEY"),
  authentication: :plain,
  enable_starttls: true
}

Spring Boot

# application.properties
spring.mail.host=smtp.letr.dev
spring.mail.port=587
spring.mail.username=letr
spring.mail.password=${LETR_API_KEY}
spring.mail.properties[mail.smtp.auth]=true
spring.mail.properties[mail.smtp.starttls.enable]=true
spring.mail.properties[mail.smtp.starttls.required]=true

Host, port, username and password are Spring Boot's own spring.mail.* properties. The mail.smtp.* keys for login and STARTTLS are Jakarta Mail properties, which Spring Boot passes to the mail session through spring.mail.properties.

WordPress

WordPress needs an SMTP plugin. In WP Mail SMTP, choose the mailer Other SMTP and set:

  • SMTP Host: smtp.letr.dev
  • Encryption: TLS
  • SMTP Port: 587
  • Authentication: on
  • SMTP Username: letr
  • SMTP Password: your API key

For port 465 choose the encryption SSL.

Nextcloud

Nextcloud, here on port 465 with implicit TLS:

// config/config.php
'mail_smtpmode' => 'smtp',
'mail_smtphost' => 'smtp.letr.dev',
'mail_smtpport' => 465,
'mail_smtpsecure' => 'ssl',
'mail_smtpauth' => true,
'mail_smtpname' => 'letr',
'mail_smtppassword' => 'letr_your_api_key',
'mail_from_address' => 'no-reply',
'mail_domain' => 'mail.acme.dev',