CustomDomain Docs

Email DNS & SPF merge

Add MX, SPF, DKIM and DMARC records with a domain connection. SPFM merges your SPF mechanisms into the published record, so the domain keeps one SPF record.

Connecting a domain points web traffic at your product. Many customers also want email on that domain (Google Workspace, Microsoft 365, Zoho). Email records (MX, TXT for SPF/DKIM/DMARC) can ride the same apply rail as the web records: you include them in the records array of POST /v1/connections/{id}/apply, or the customer adds them by hand in the manual flow.

The one email-specific hazard the engine handles for you is SPF: a domain must have exactly one v=spf1 record, so blindly writing a second one silently breaks mail. That's what the SPFM meta-record is for.

SPF merge (SPFM)

Instead of a raw TXT, apply an SPFM record with the mechanisms you need. At write time the engine reads any SPF TXT already published at that host and merges your mechanisms into it, deduped, with a single terminal all, and the existing record's qualifier preserved (a merge never loosens the policy):

existing:  v=spf1 include:mailgun.org ~all      (already published)
SPFM add:  include:_spf.google.com
→ written: v=spf1 include:mailgun.org include:_spf.google.com ~all

If nothing is published yet, the SPFM record is materialized into a fresh v=spf1 … ~all TXT. The merge is refused (error spf_merge_failed) when the result would exceed 10 DNS-lookup mechanisms or 255 characters. This append-never-clobber behavior is applied for both Rail B (API-token apply) and the automatic rails.

Provider records (reference)

ProviderTypical records
Google WorkspaceMX smtp.google.com, SPF include:_spf.google.com, DKIM google._domainkey TXT, DMARC
Microsoft 365MX <domain>.mail.protection.outlook.com, autodiscover CNAME, SPF include:spf.protection.outlook.com, DKIM selector1/2 CNAMEs, DMARC
Zoho (US data center)MX mx/mx2/mx3.zoho.com, SPF include:zoho.com, DKIM TXT, DMARC
Zoho (EU data center)MX mx.zoho.eu (Zoho's zoho.eu mail hosts), SPF include:spf.zoho.eu, DKIM TXT, DMARC

Zoho's SPF include depends on the data center that holds the mailbox. When CustomDomain™ fills in a domain's mail provider SPF include itself, it reads the domain's MX records: hosts under zoho.com or zohomail.com get include:zoho.com, hosts under zoho.eu get include:spf.zoho.eu, and Zoho's other data centers (such as mx.zoho.in) get no include rather than a guessed one.

MX records

MX records route inbound mail to the provider's servers and carry a numeric priority (lower = preferred). Set it with the record's priority field; it is honored for MX and SRV and ignored for other types. Most providers publish a single MX (e.g. Google's smtp.google.com at priority 1); a few publish a ranked set (Zoho's mx.zoho.com / mx2 / mx3 at 10 / 20 / 50).

Mark the MX records essential: true to flag them as required for the connection, since mail is down without them. The flag is stored on the record; the poller does not treat it differently from other records today:

{ "type": "MX", "host": "@", "value": "smtp.google.com", "priority": 1, "essential": true }

DKIM & DMARC

  • DKIM keys are generated in the provider's admin console (unique per domain), so they can't be derived; add the provider's selector._domainkey record as given. The widget's DKIM step groups records flagged purpose: "dkim" onto a dedicated screen (enableDkim).
  • DMARC should start at p=none (monitor only). After confirming legitimate mail passes DKIM/SPF, tighten to quarantine, then reject.

Full email setup in one apply

MX, SPF (as SPFM), DKIM, and DMARC can all ride a single POST /v1/connections/{id}/apply records array alongside the web records. The engine merges the SPFM mechanism into any existing SPF and writes the rest as-is:

{
  "credential": { "token": "<scoped-provider-token>" },
  "records": [
    { "type": "MX",   "host": "@",                 "value": "smtp.google.com", "priority": 1, "essential": true },
    { "type": "SPFM", "host": "@",                 "value": "include:_spf.google.com" },
    { "type": "TXT",  "host": "google._domainkey", "value": "v=DKIM1; k=rsa; p=MIGf..." },
    { "type": "TXT",  "host": "_dmarc",            "value": "v=DMARC1; p=none; rua=mailto:[email protected]" }
  ]
}

The same set can also be applied by the automatic rails (OAuth / one-click setup), which carry an email-full template that emits these records grouped for the widget's email screen (MX, an SPFM merge, a DKIM CNAME and a DMARC TXT). The one-click catalog also ships the records split across single-purpose templates (email-mx, email-spf, email-dkim, and email-dmarc) so a provider can apply just one piece. The email-dkim template writes a CNAME (<selector>._domainkey pointing at the target your mail provider hosts), not a TXT. Either way the SPFM merge and MX priority behave identically.

These templates take your mail provider's settings as variables, never records, and each is checked before a link is signed: mxHost, spfInclude and dkimTarget are public hostnames (not IP addresses or internal names), dkimSelector is one DNS label, and dmarcRua is one email address, with a mailto: prefix accepted and no other DMARC tags. A value of another shape is refused with 400 invalid_request (-32602 from the MCP server's add-email). See Template variables.

Pre-flight: POST /v1/domains:check reports spf_override_support and returns any record_conflicts (including spf-merge clashes) it observes in public DNS, so you can warn the customer before writing anything.

On this page