Docs/Sending email

Sending over SMTP

Anypost accepts mail over SMTP as well as over the HTTP API. If your application, or off-the-shelf software you run, already sends through an SMTP server, point it at Anypost and you get the same authentication, delivery, and event reporting without writing code.

When to use SMTP

The HTTP API is the richer interface and what most of these docs describe. Reach for SMTP when:

  • An existing app already speaks SMTP and you would rather configure it than rewrite its mail layer.
  • You run software whose mail transport you do not control.
  • A library or framework you already use makes SMTP the path of least resistance.

Both transports send from the same verified domains, authenticate with the same API keys, and produce the same events. They do not sustain the same submission rate, though: see Throughput before you commit a high-volume stream to SMTP.

Connection settings

SettingValue
Hostsmtp.anypost.com
Ports587, 2587, or 25
EncryptionSTARTTLS (required)
Usernameanypost
Passwordyour API key secret (ap_...)

All three ports behave identically: pick whichever your network allows. 587 is the standard submission port and the one to use by default. 2587 is an alternative for networks that block 587. 25 is also accepted, but many hosting providers and ISPs block outbound traffic on it, so treat it as a last resort rather than a default.

STARTTLS is mandatory on every port. The connection opens in plaintext and must upgrade to TLS before authenticating. There is no implicit-TLS port and no unencrypted submission path: Anypost refuses to authenticate, and refuses mail, on a session that has not upgraded to TLS.

Authentication

The username is always the literal string anypost. The password is an API key secret, the same ap_... value used as the bearer token on the HTTP API. A single key authenticates both transports. See Authentication for creating, scoping, and rotating keys.

Use a send_only key for an application that only sends. SMTP submission needs no other permission.

Authentication failures are reported in the SMTP exchange: a bad key is rejected at the AUTH step, and a session that never authenticates is refused at MAIL FROM. Repeated failures from one address are temporarily blocked.

Sender addresses

An SMTP submission carries two sender addresses, and Anypost checks both:

  • The envelope sender, set by the SMTP MAIL FROM command. It is where delivery failures are returned and is not shown to the recipient. Most SMTP clients set it automatically to match the From header.
  • The From header, the address the recipient sees in their mail client.

The domain of each must be both verified for your team and permitted for the API key:

  • The domain must be verified, exactly as on the HTTP API. See Domains & DNS setup.
  • The key must be allowed to use it. A key with an allowed_domains restriction can only send from the domains it lists; an unrestricted key may use any of the team's verified domains. See Authentication.

The envelope sender and the From header do not have to share a domain, but each is checked on its own. A submission is rejected if either one is on a domain that is unverified or not permitted for the key. Checking the From header separately is what stops an authenticated sender from passing the envelope check with one domain and then showing the recipient a From address on another.

Message size

A submitted message may be up to 5 MB, the same ceiling the HTTP API enforces. This is the size of the full MIME message, including encoded attachments. Anypost advertises the limit over the SIZE extension, so a well-behaved client can refuse an oversized message before sending it.

5 MB is ample for transactional and marketing email. For a send that would strain it, host large files elsewhere and link to them rather than attaching them.

A test submission

Any SMTP client works. This example uses swaks, a command-line SMTP tester:

swaks \
  --server smtp.anypost.com --port 587 --tls \
  --auth-user anypost --auth-password "$ANYPOST_API_KEY" \
  --from [email protected] \
  --to [email protected] \
  --header "Subject: Hello from Anypost" \
  --body "Hello, inbox!"

--tls performs the required STARTTLS upgrade. A 250 OK on the DATA command means the message was accepted.

Anypost headers

Standard MIME covers most of a message: From, To, Cc, Bcc, Subject, Reply-To, the text and HTML parts, attachments, and inline images are all ordinary parts of the message you submit.

The fields that are neither MIME headers nor message content — Anypost's send-time correlation dimensions, and the IP pool to send from — have no MIME equivalent, so over SMTP you set them with headers. Anypost reads and then strips these headers, so recipients never see them:

HeaderSetsValue
X-Anypost-TagsTagsA JSON array of strings, for example ["welcome","onboarding"].
X-Anypost-TagOne tagA single tag value. Repeat the header for several. Cannot be combined with X-Anypost-Tags.
X-Anypost-TopicTopicA single topic string.
X-CampaignCampaignA single campaign string.
X-Anypost-Ip-PoolIP poolThe name of one of your named IP pools. Only for accounts with dedicated IPs.

Each header may appear at most once per message (apart from repeated X-Anypost-Tag). A malformed value is rejected in the SMTP exchange rather than sent. See Tags, topics & campaigns for what the first four dimensions do, and ip_pool for pool selection.

The message ID

The 250 OK reply to the DATA command carries an ids= field: one email_ ID per recipient, space-separated.

250 OK ids=email_018f4f3e-7b2c-7c80-8e21-1a3a4f5b6c7d

Each ID identifies one recipient's copy of the message, and every event for that copy references it. Capture the ids= field from the DATA response if you want to correlate events back to a submission.

Throughput

SMTP submission is validated one message at a time. Every message pays its own sender authorization, suppression-list lookup, and quota accounting before Anypost accepts it. The HTTP API does that work inside the request that already carried the message, and POST /v1/email/batch amortizes it across up to 100 messages at once.

This is invisible on transactional traffic and on campaigns in the low millions of messages a month. It becomes the limiting factor on a sustained stream well above that: at tens of millions a month, the per-message overhead, not the network or the delivery capacity, sets how fast you can hand mail off.

So:

  • Under a few million a month: use whichever transport suits your stack. SMTP is fine.
  • Tens of millions a month or more: submit over the HTTP API, and use the batch endpoint wherever the messages are independent of each other. Keep SMTP for the systems whose transport you genuinely cannot change.

Nothing about deliverability, IP assignment, or event reporting changes with the transport. This is about submission rate only.

What differs from the HTTP API

SMTP reaches the same delivery and reporting, with a few interface differences:

  • Idempotency keys are an HTTP-only feature. SMTP has no equivalent; a resubmitted message is a new send.
  • Batch sending is HTTP-only. Over SMTP, submit each message separately on the same connection.
  • Templates are an HTTP-API feature. An SMTP submission is an already-rendered message: it cannot reference a stored template or carry per-recipient variables.
  • The remaining correlation dimensions (tags, topic, campaign) and ip_pool are set through headers, as above, rather than as JSON fields.
  • Submission rate is lower per message, which matters only at very high volume. See Throughput.

For anything beyond a straightforward send, the HTTP API is the fuller interface. See Send a single email.

Where to go next