Send · SMTP relay

SMTP for everything that can’t call an API.

Point a CMS, an ERP, a billing system or an old app at our relay. It takes a username and an API key as the password, and you get the same logs, webhooks and suppression list as the API.

Connection settings

Host
smtp.refiremail.com
Port
465Implicit TLS. 587 with STARTTLS also works; see the table below.
Username
refiremailThe same for every account: the API key identifies your team.
Password
<your Refiremail key>Any API key. A sending-only key is enough.

01, Ports and login

Pick the port your network allows.

Every port requires TLS before your client can log in. Choose implicit TLS or STARTTLS, whichever your client supports.

The username is refiremail. The password is an API key, checked exactly the way the HTTP API checks it.

  • AUTH PLAIN and AUTH LOGIN are accepted, and only once the connection is encrypted.
  • A sending-only key is enough. A key tied to one domain can send only from that domain.
  • Revoke a key and the relay refuses it straight away, just as the API does.
PortSecurityWhen to use it
465Implicit TLSThe default. TLS starts before the first command.
2465Implicit TLSWhen a network blocks 465.
587STARTTLSThe usual submission port for clients that upgrade the connection.
2587STARTTLSWhen a network blocks 587.

02, Same pipeline

Everything the API gives you.

A message sent over SMTP becomes an ordinary Refiremail email: signed with your domain’s DKIM key, listed with the rest, and reported through the same events and webhooks.

  • A successful submission replies 250 2.0.0 OK <email id>. Log that id and you can look the email up later.
  • Suppressed addresses are skipped here too, and each one fires email.suppressed.
  • Bcc works the way SMTP expects: envelope recipients missing from To and Cc are sent as bcc.
  • Attachments and inline images arrive intact, with their content ids kept.
See the event catalogue
EmailsSample data
SMTP mail sits in the same list as API, broadcast and automation mail, with the same statuses and its source.

03, Reply codes

Replies your code can act on.

Each refusal has its own code, so a script, a queue or a mail server can tell a bad key from a full quota.

ReplyWhat it means
250 2.0.0 OK <email id>Accepted. The id is the same one GET /emails/{id} and your webhooks use.
535 5.7.8Authentication failed: a wrong username, or a key that is unknown, revoked or expired.
550 5.7.1The From domain isn’t verified for your team, or the key is tied to another domain.
554 5.6.0The message failed validation, for example more than 50 recipients.
452 4.5.3Your sending quota is used up. A temporary error: retry later, or move up a plan.
451 4.7.1Rate limited. A temporary error: nothing was sent, so retry after a moment.

04, Idempotency

Safe retries over SMTP too.

Add a Refire-Idempotency-Key header. If a timeout makes your app submit the same message again, it isn’t sent twice. We remove the header before the message is relayed.

  • It follows the same rules as the API’s Idempotency-Key header: kept for 24 hours and scoped to your team.
  • Use something that names the message, such as an order or invoice number, not a random value per attempt.
How idempotency works in the API
Message headersHTTP
From: Nimbu <[email protected]>
To: [email protected]
Subject: Order #1042 is confirmed
Refire-Idempotency-Key: order-1042

05, Code

Three common setups.

Any SMTP client works with the settings above. Here they are in Nodemailer, Python’s standard library and Django.

send.tsTypeScript
import nodemailer from 'nodemailer';

const transport = nodemailer.createTransport({
  host: 'smtp.refiremail.com',
  port: 465,
  secure: true, // implicit TLS. For STARTTLS use port 587 and secure: false
  auth: {
    user: 'refiremail',
    pass: process.env.REFIREMAIL_API_KEY,
  },
});

await transport.sendMail({
  from: 'Nimbu <[email protected]>',
  to: '[email protected]',
  subject: 'Order #1042 is confirmed',
  html: '<p>Thanks, Asha. Your order ships tomorrow.</p>',
  headers: { 'Refire-Idempotency-Key': 'order-1042' },
});
send.pyPython
import os
import smtplib
from email.message import EmailMessage

msg = EmailMessage()
msg["From"] = "Nimbu <[email protected]>"
msg["To"] = "[email protected]"
msg["Subject"] = "Order #1042 is confirmed"
msg["Refire-Idempotency-Key"] = "order-1042"
msg.set_content("Thanks, Asha. Your order ships tomorrow.")

with smtplib.SMTP("smtp.refiremail.com", 587) as smtp:
    smtp.starttls()
    smtp.login("refiremail", os.environ["REFIREMAIL_API_KEY"])
    smtp.send_message(msg)
settings.pyPython
# settings.py
import os

EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = "smtp.refiremail.com"
EMAIL_PORT = 587
EMAIL_USE_TLS = True  # STARTTLS
EMAIL_HOST_USER = "refiremail"
EMAIL_HOST_PASSWORD = os.environ["REFIREMAIL_API_KEY"]
DEFAULT_FROM_EMAIL = "Nimbu <[email protected]>"

06, Limits

What one message can carry.

The relay advertises its size limit in EHLO, so a well-behaved client checks before it sends.

Need a time in the future, or more than 50 recipients? Scheduling and batches are API features. Mail to a whole list belongs in a broadcast, which handles unsubscribes for you.

Broadcasts
LimitValue
Message size40 MB, attachments included
Recipients per message50
SchedulingNot over SMTP. Use scheduled_at in the API

Four settings and a key.

Every API key is also an SMTP password. Request early access, then create a key and paste four settings.