Sending
This page covers the interface between your application and relay: how to
connect, what relay checks on submission, and what happens to a message after
relay accepts it.
What you need
- An SMTP credential from the dashboard. The username is your organization
slug, and the password is the API key. - A sender domain registered to your organization, whose From-address you
use. The managed sender domain works from the first minute. - A receiver address that is not on your suppression list.
Connect and authenticate
| Port | Security | Use |
|---|---|---|
| 465 | Implicit TLS from the first byte | Preferred submission path |
| 587 | STARTTLS required before AUTH and DATA | Corporate networks that filter 465 |
relay speaks SMTP AUTH PLAIN. The username is the organization slug, and the
password is an SMTP credential key. No session and no TLS means no AUTH: the
server refuses AUTH over plaintext on port 587.
The credentials page shows the same values as a smtps:// URI, with the
organization slug as the user and a <credential key> placeholder for the
password. Clients that accept a URI take it once you put the key in.
The submission exchange:
openssl s_client -starttls smtp -connect smtp.relays.to:587
EHLO your-app.example.com
AUTH PLAIN <base64 of \0org-slug\0api-key>
MAIL FROM: <billing@acme.com>
RCPT TO: <kim@example.net>
DATA
<message with headers and body>
.
The acceptance checks
relay runs its checks inside the SMTP transaction and answers with the
final acceptance code:
flowchart TD
A[DATA received over TLS] --> B{Credential valid and not held?}
B -- No --> C[530 / 535]
B -- Yes --> D{From-domain registered to this org?}
D -- No --> E[550 Sender domain not registered]
D -- Yes --> F{Recipient suppressed?}
F -- Yes --> G[Store as suppressed, answer 250]
F -- No --> H{Billing active, or member recipient?}
H -- No --> I[550 not allowed without active billing]
H -- Yes --> J[Store as pending, enqueue spam scan]
J --> K[250 OK]
Notes on the rules:
- The From-domain must resolve to a domain of your organization, matching by
root domain and organization. Subdomain From-addresses therefore work
without extra configuration. - A suppressed recipient is not an error. relay stores the message with the
suppressed status so the audit trail stays complete. - Without active billing, relay accepts only messages addressed to members
of the organization, so you can always test on yourself.
The delivery pipeline
relay signs each message while the SMTP transaction is still open, so the
stored message is the exact message that leaves. A task worker then takes
over:
sequenceDiagram
participant MSA as MSA
participant Queue as Task worker
participant Scan as Scanner
participant DNS as DNS resolvers
participant Remote as Recipient MX
MSA->>MSA: mint Feedback-ID, sign with all domain keys
MSA-->>Client: 250 OK, signed message stored
Queue->>Scan: score the message
alt score reaches the hold threshold
Queue->>Queue: status held, stop
else clean
Queue->>DNS: MX lookup for the recipient domain
DNS-->>Queue: MX hosts by preference
Queue->>Remote: per host: STARTTLS on 25, then the message
Remote-->>Queue: SMTP answer
end
Important details of the pipeline:
- Signing covers the message as stored. relay signs with the private
keys of the sender domain for RSA-2048 and Ed25519 at once.
All signatures cover the same headers: From, To, Subject, Date,
Message-ID, andFeedback-ID. The message detail page lists every
signature relay applied in its own card, next to the signing domain,
selector, and algorithm. The raw tag list is one click away. - Customers’ messages carry a platform cosign. relay cosigns with the
keys of the platform domain. relay also sets aFeedback-IDheader with
its own token. This token replaces a customer-suppliedFeedback-ID, so
complaint reports echo relay’s key and the complaint maps to one message. - The envelope differs from the From header. The Return-Path becomes
bounce+{message-id}@{sender-subdomain}, so each bounce identifies one
message and the envelope domain aligns with your DKIM. - Sending spreads across an IP pool. Each outgoing connection leaves
through a randomly picked relay sending IP. Every pool IP has matching
forward and reverse DNS, and a blacklisted IP can rotate out without an
outage. Every delivery attempt records the sending IP it used, so a
refusal names the address the receiver blocked. - EHLO identifies the relay sending host, whose name matches its
reverse DNS record. Receivers grade that consistency. - Enforced MTA-STS. For recipient domains with a policy, relay skips
hosts the policy does not permit. See
Encryption. - relay records every SMTP leg. The accepted submission is stored as
the first transmission of a message, followed by one transmission per
delivery attempt, each with the SMTP answer.
Message and attempt statuses
The message carries one status. Every attempt carries its own:
| Message status | Meaning |
|---|---|
| pending | Stored, spam scan or delivery not finished yet |
| held | the scanner held the message for spam or malware, and it never left |
| sent | At least one attempt ended with success |
| bounced | A recipient server rejected the message permanently |
| suppressed | The recipient is on the suppression list |
| failed | relay cannot deliver, the transcript shows why |
The Transmission list per message starts with the submission and shows
every delivery attempt: the MX host context, the SMTP status code, the
answer text, and whether TLS was in use.
Bounce handling
On a permanent 5xx rejection relay records the bounce and adds the
recipient’s address as a suppressions entry with the reason bounce.
Suppressed addresses reject silently on later submissions. There is no
automatic removal. A human can release an address again in the
dashboard. Manual entries can carry reason manual.
A minimal client
Python with the standard library:
import smtplib, ssl
with smtplib.SMTP("smtp.relays.to", 587) as server:
server.starttls(context=ssl.create_default_context())
server.login("acme", "your-smtp-credential-key")
server.sendmail(
"billing@acme.com",
["kim@example.net"],
"From: billing@acme.com\r\n"
"To: kim@example.net\r\n"
"Subject: Invoice\r\n"
"\r\nYour invoice is ready.\r\n",
)
Libraries that speak SMTP natively set the same four things: host, port,
STARTTLS, and the credential pair.
Related pages
- Deliverability.
The DNS records behind this pipeline. - Reliability.
Retries, attempts, and monitoring.