Skip to main content

SMTP Relay

The SMTP Relay is a migration aid for teams that already send mail through an SMTP client or library and are not yet ready to rewrite that integration against the HTTP API. A workspace issues SMTP username/password credentials from Posta, the existing SMTP client points at Posta's SMTP Relay instead of its current outbound provider, and every message it sends is parsed and relayed through the same outbound pipeline used by POST /api/v1/emails/send — the same domain-verification, suppression-list, rate-limit, and delivery handling, just reached over SMTP instead of HTTP. Teams can cut over their SMTP client on day one and migrate call sites to the HTTP API gradually, at their own pace.

This is a separate, purpose-built listener from Inbound Email: Inbound accepts anonymous mail addressed to a verified domain, while the Relay requires SMTP AUTH and exists to accept mail from your applications, independent of whether Inbound is enabled.

Enabling the Relay

The SMTP Relay is off by default. Enable it with configuration:

VariableDefaultDescription
POSTA_SMTP_RELAY_ENABLEDfalseMaster switch. Enables the SMTP relay listener and the /api/v1/workspaces/current/smtp-credentials routes.
POSTA_SMTP_RELAY_HOST0.0.0.0Bind address for the built-in SMTP relay listener.
POSTA_SMTP_RELAY_PORT2526Listener port. Separate from POSTA_INBOUND_SMTP_PORT — the two listeners never share a port or process state.
POSTA_SMTP_RELAY_HOSTNAMEposta.localHostname announced in the SMTP EHLO greeting.
POSTA_SMTP_RELAY_MAX_MESSAGE_SIZE26214400 (25 MiB)Maximum raw message size in bytes. Larger messages are rejected with 552.
POSTA_SMTP_RELAY_RATE_LIMIT60Per-IP maximum SMTP sessions per window; 0 disables the limit.
POSTA_SMTP_RELAY_RATE_WINDOW60Rate-limit window, in seconds.

:::danger No TLS The Relay listener does not support TLS or STARTTLS, by design — it is a minimal, plaintext-AUTH migration aid, not a hardened public MTA. Credentials and message content travel unencrypted. Only expose POSTA_SMTP_RELAY_PORT on a private network, over a VPN, or to localhost — never bind it directly to the public internet. If you need Relay access from outside your private network, put a TLS-terminating TCP proxy (e.g. stunnel, an SMTP-aware load balancer) in front of it; Posta itself will never decrypt or negotiate TLS on this listener. :::

How It Works

your SMTP client POSTA_SMTP_RELAY_PORT (no TLS)
│ │
├── EHLO / AUTH PLAIN ────────────►│ verify SMTPCredential (workspace-scoped)
│ │
└── MAIL FROM / RCPT TO / DATA ───►│ parse message


email.Service.Send() ──► same pipeline as POST /api/v1/emails/send

├─► domain verification
├─► suppression list
├─► rate limits / plan quota
└─► queued for delivery (Email record)
  1. Your SMTP client opens a connection to POSTA_SMTP_RELAY_HOST:POSTA_SMTP_RELAY_PORT and authenticates with AUTH PLAIN, using an SMTP Relay username and password.
  2. Posta looks up the credential, confirms it is not revoked, and ties the rest of the session to the credential's workspace. MAIL FROM, RCPT TO, and DATA are all rejected until AUTH succeeds.
  3. On DATA, Posta reads the raw message (bounded by POSTA_SMTP_RELAY_MAX_MESSAGE_SIZE), parses subject, HTML/text bodies, and attachments from the MIME body, and builds a send request using the SMTP envelope addresses (MAIL FROM / RCPT TO) rather than the parsed header addresses — the same behavior a normal MTA would have.
  4. That request is handed to the same email.Service.Send used by the HTTP API, scoped to the credential's workspace and owning user. It goes through the identical checks: sender domain verification, suppression-list filtering, rate limits, plan quota, and attachment-size validation, and a normal Email record is created and queued for delivery.
  5. The SMTP response code reflects the outcome of that call — see Send Outcomes below.

Issuing a Credential

SMTP credentials are workspace-scoped and always require a workspace — there is no personal/unscoped credential. Create one from the dashboard under Developers → SMTP Relay (/smtp-relay), or directly via the API:

POST /api/v1/workspaces/current/smtp-credentials
curl -X POST http://localhost:9000/api/v1/workspaces/current/smtp-credentials \
-H "Authorization: Bearer <jwt>" \
-H "X-Posta-Workspace-Id: 1" \
-H "Content-Type: application/json" \
-d '{ "name": "Legacy app relay", "allowed_ips": ["203.0.113.0/24"] }'

Response (201):

{
"success": true,
"data": {
"id": 7,
"name": "Legacy app relay",
"username": "smtp_9f3c2a1b7e4d5601",
"password": "8b1c...e02f",
"host": "posta.local",
"port": 2526,
"created_at": "2026-07-20T00:00:00Z",
"message": "Save this password securely. It will not be shown again."
}
}
warning

Save the password immediately. Like an API key, the plaintext password is only returned once, at creation time. Posta stores only a hash of it.

Unlike API keys, an SMTP credential has no expiry or scopes — it is either usable or revoked. It does support an IP allowlist (allowed_ips), same as API keys: an empty list permits any IP.

Listing Credentials

GET /api/v1/workspaces/current/smtp-credentials

Returns a paginated list scoped to the current workspace. Passwords are never returned — only credential metadata (id, name, username, allowed_ips, revoked, created_at, last_used_at).

Revoking a Credential

Instantly disables a credential without deleting it, mirroring API key revocation:

POST /api/v1/workspaces/current/smtp-credentials/{id}/revoke

A revoked credential fails AUTH immediately on its next connection attempt; any already-open session is not forcibly disconnected but subsequent commands on a new session will be rejected.

Deleting a Credential

DELETE /api/v1/workspaces/current/smtp-credentials/{id}

Permanently removes the credential record.

Connecting Your SMTP Client

Point your existing SMTP client at the Relay host and port, with the generated username/password and no encryption:

SettingValue
HostPOSTA_SMTP_RELAY_HOST (or wherever it's reachable from your app)
PortPOSTA_SMTP_RELAY_PORT (default 2526)
EncryptionNone — do not configure TLS or STARTTLS on the client
Auth mechanismPLAIN
Username / PasswordFrom the credential creation response
swaks --server localhost --port 2526 \
--auth PLAIN --auth-user smtp_9f3c2a1b7e4d5601 --auth-password 8b1c...e02f \
--from sender@yourdomain.com --to recipient@example.com \
--header "Subject: Hello from the SMTP Relay" \
--body "This message was relayed through Posta's outbound pipeline."

AUTH PLAIN is the only mechanism the Relay advertises; clients that default to LOGIN or CRAM-MD5 should be configured to use PLAIN explicitly. The listener accepts up to 50 recipients per message.

Send Outcomes

Because the Relay relays into the same email.Service.Send used by POST /api/v1/emails/send, an accepted message ends up in the identical set of email statuses (pending, queued, processing, sent, failed, suppressed) — the Relay does not introduce any new ones. The SMTP response code the client sees just reflects whether Posta accepted the message for that pipeline, not its eventual delivery outcome:

SMTP responseMeaning
250Accepted and handed to the outbound pipeline as an Email record. This includes the case where every recipient was suppressed — Posta still accepts the message, logs it with suppressed status, and does not attempt delivery, exactly as the HTTP API does.
550 5.7.1Sender domain is not verified for this workspace.
452 4.7.0 / 421 4.7.0Rate limit or plan quota exceeded; retry later.
552 5.3.4Message exceeds POSTA_SMTP_RELAY_MAX_MESSAGE_SIZE.
554 5.6.0Message could not be parsed (malformed MIME).
451 4.3.0Temporary failure (e.g. a transient error while queuing).
502 5.7.0A mail command was sent before a successful AUTH.
535 5.7.8AUTH failed — unknown or revoked credential, wrong password, inactive user, or the connecting IP is not on the credential's allowed_ips.

Check the eventual delivery outcome of a message the same way you would for an API-sent email — via GET /api/v1/emails/{id}/status using the id Posta assigned, or by browsing the workspace's email log in the dashboard.

Next Steps

  • Domain Verification — required before a sender address can relay mail.
  • API Keys — the HTTP-API equivalent of a Relay credential, for when you're ready to migrate fully.
  • Rate Limiting — how send limits are enforced across both the HTTP API and the Relay.
  • Email Status — track a relayed message after it's accepted.