Start here

DNS setup

Four records per sending domain. Two are required, two are recommended. Everything on this page is generated per domain — nothing is shared between customers, and no key material is hard-coded.


The records

Given example.com, the dashboard shows exactly this (with your real key):

@                     3600  IN  TXT    "v=spf1 include:mail.yourservice.com ~all"
selector1._domainkey  3600  IN  TXT    "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQ…"
_dmarc                3600  IN  TXT    "v=DMARC1; p=none;"
bounce                3600  IN  CNAME  bounce.yourservice.com
RecordRequiredWhat it does
SPFyesTells receivers our servers may send for your domain.
DKIMyesLets receivers verify our cryptographic signature on your mail.
DMARCrecommendedTells receivers what to do when SPF and DKIM fail, and sends you reports.
Return pathoptionalBranded bounce hostname. Delivery works without it.

A domain becomes verified once SPF and DKIM both pass. Those two are what actually authenticate the mail.


SPF

Type:  TXT
Host:  @            (the apex — some registrars want the domain name itself)
Value: v=spf1 include:mail.yourservice.com ~all

One record, always

A domain may publish exactly one SPF record. Two is not "extra authorisation" — it is a permanent error, and receivers treat the whole thing as broken. This is the single most common way domains break their own mail.

If you already send through another provider, merge:

# Before
v=spf1 include:_spf.google.com ~all

# After — one record, both providers
v=spf1 include:_spf.google.com include:mail.yourservice.com ~all

The dashboard detects a second record and says so explicitly rather than just failing verification.

~all vs -all

  • ~all (softfail) — mail from unlisted servers is accepted but marked. Start here.
  • -all (hardfail) — mail from unlisted servers is rejected. Move here once you are certain every legitimate sender is listed.
  • +all — authorises the entire internet to send as you. Never use it. The dashboard warns if it sees one.

The ten-lookup limit

SPF permits at most 10 DNS lookups while evaluating a record (RFC 7208). Each include:, a, mx, exists and redirect counts. Exceed it and evaluation returns permerror, which most receivers treat as a failure. The dashboard counts them for you.


DKIM

Type:  TXT
Host:  selector1._domainkey
Value: v=DKIM1; k=rsa; p=<your public key>

Every domain gets its own 2048-bit RSA key pair, generated the moment you add the domain. The public half goes in DNS; the private half is encrypted with AES-256-GCM and only ever decrypted inside the signing service. It is never returned by the API and never written to disk.

Some DNS providers reject the long p= value because a single TXT string is capped at 255 bytes. Providers handle this differently:

  • Most (Cloudflare, Route 53, Google Cloud DNS) split it automatically.
  • Some require you to enter it as multiple quoted strings: "v=DKIM1; k=rsa; p=MIIBIjANBg…" "…rest of the key". The two strings are concatenated with no separator — do not add a space.

Verification compares the published key against the one we issued, ignoring whitespace and PEM armour, so either form works.


DMARC

Type:  TXT
Host:  _dmarc
Value: v=DMARC1; p=none;

DMARC ties SPF and DKIM to the visible From: address and tells receivers what to do when neither aligns. Gmail and Yahoo require a DMARC record from bulk senders, and it is the single best signal that you are a deliberate sender.

Roll it out in stages

v=DMARC1; p=none; rua=mailto:dmarc@example.com;        ← observe
v=DMARC1; p=quarantine; pct=25; rua=mailto:…           ← tighten gradually
v=DMARC1; p=reject; rua=mailto:…                       ← enforce

Stay at p=none until the reports show every legitimate stream passing. Jumping straight to p=reject will silently destroy mail from systems you forgot about — the marketing tool, the CRM, the invoicing service.

Add rua=mailto:… or you will get no reports at all, which makes the whole exercise blind.

We never modify records you already publish. If you already have a DMARC policy, keep it — verification reports what it found and moves on.


Return path (optional)

Type:  CNAME
Host:  bounce
Value: bounce.yourservice.com

Bounce reports are addressed to bounce+<emailId>@bounce.yourservice.com, so each report identifies exactly which message it belongs to. This CNAME gives that a hostname under your own domain.

It is genuinely optional. DMARC alignment is satisfied by the DKIM signature, which carries d=example.com, so delivery and alignment work with or without this record.


Verifying

Dashboard → your domain → Verify DNS, or:

curl -X POST https://api.yourservice.com/v1/domains/dom_abc123/verify \
  -H "Authorization: Bearer $MAIL_API_KEY"

DNS changes are not instant. Propagation usually takes minutes and can take up to the TTL of the record you replaced. If verification fails immediately after you publish, wait and retry before assuming something is wrong.

Checking it yourself

dig +short TXT example.com
dig +short TXT selector1._domainkey.example.com
dig +short TXT _dmarc.example.com
dig +short CNAME bounce.example.com

On Windows:

Resolve-DnsName -Type TXT example.com
Resolve-DnsName -Type TXT selector1._domainkey.example.com

After it verifies

Send yourself a test message and inspect the headers. In Gmail: Show original. You want all three:

SPF:   PASS with IP …
DKIM:  PASS with domain example.com
DMARC: PASS

DKIM: PASS with domain example.com — not yourservice.com — is the one that matters. It means the signature is aligned with the visible From: address, which is what DMARC checks.


Common failures

SymptomCauseFix
SPF MULTIPLE_SPF_RECORDSTwo TXT records starting v=spf1.Merge into one.
SPF INCLUDE_MISSINGRecord exists but does not list us.Add include:mail.yourservice.com to the existing record.
SPF passes locally, fails hereAdded at www or a subdomain instead of the apex.Host must be @.
DKIM MISSING_RECORDProvider appended the domain twice — selector1._domainkey.example.com.example.com.Enter the host as selector1._domainkey only.
DKIM KEY_MISMATCHAn old key from a previous provider, or the value was truncated on paste.Replace with the exact value from the dashboard.
DKIM fails on long valuesProvider will not accept a >255-byte string.Split into two quoted strings with no separator.
LOOKUP_FAILEDTransient resolver problem, or the domain does not resolve.Retry in a minute.
Verified, then failed laterA required record was removed or edited.Re-publish it; verification runs on a schedule.

Platform-side DNS

These are records you publish for the platform itself, not per customer. See deployment.md.

mail.yourservice.com.     A     203.0.113.10
mail.yourservice.com.     TXT   "v=spf1 ip4:203.0.113.10 -all"
bounce.yourservice.com.   A     203.0.113.10
bounce.yourservice.com.   MX    10 mail.yourservice.com.
api.yourservice.com.      A     203.0.113.10
dashboard.yourservice.com. A    203.0.113.10

; And at your hosting provider, the reverse record:
10.113.0.203.in-addr.arpa. PTR  mail.yourservice.com.

The SPF record on mail.yourservice.com is what customers' include: resolves to. When you add a sending IP, add it there — every customer picks up the change without touching their own DNS. That is the entire point of the include: indirection.