Multi-record and email records
Sessions that need more than one DNS record — CNAME plus ownership proof, apex A/AAAA, and SPF/DKIM/DMARC — what each connect path can do with them, and how verification treats email policies.
POST /api/v1/sessions has always taken records as an array, and the guided-manual and Cloudflare one-click paths have always written and verified every record in it. What changed in September 2026 is the Domain Connect one-click path: it used to express exactly one record, and now applies whole record sets — provided the set matches one of the templates DoDomain publishes. This page is the honest map of what each path does with a multi-record session, and how email records are verified.
The shapes that get one-click on Domain Connect
Domain Connect providers apply pre-approved templates, never arbitrary record lists. DoDomain publishes five (the JSON lives in docs/domain-connect/templates); a session gets the one-click button when its records — in any order, every one of them — fill exactly one template:
| Template | Records the session sends | Notes |
|---|---|---|
custom-subdomain-cname | one CNAME at a non-apex host | live on Domain Chief, Glauca HexDNS and Cloudflare |
domain-verification | one TXT at _dodomain-challenge whose value starts with dodomain-verify= | live on the same three |
custom-subdomain-cname-with-verification | the CNAME above plus the ownership TXT at _dodomain-challenge under the same host (_dodomain-challenge.app.acme.com for app.acme.com) | the "A/CNAME + TXT" pattern most custom-domain products use; prepared, awaiting provider onboarding |
apex-a-with-verification | one A at @, optionally one AAAA at @, plus the ownership TXT at _dodomain-challenge | the apex path — never a CNAME (see Apex domains); prepared, awaiting provider onboarding |
email-authentication | any subset of: an SPF TXT at @ with exactly one include:, one DKIM record at <selector>._domainkey (CNAME or TXT), a DMARC TXT at _dmarc | applied group by group; never MX; prepared, awaiting provider onboarding |
A session that matches none of them still works — it takes the guided-manual path (every provider) or Cloudflare one-click (Cloudflare zones), both of which handle any combination of the five record types.
Prepared is not live
The three multi-record templates are written, tested against the compiler, and ready to submit to the Domain Connect template registry — but a provider only applies a template it has onboarded, and none has onboarded these yet. Until a provider does, a multi-record session on a Domain Connect zone shows the manual records and verifies them exactly like tier 3. The dashboard's Domain preflight tool probes each template against a zone's provider and reports onboarded / not onboarded / not probed per template, so you never have to guess.
What each connect path does with several records
| Path | Multi-record sessions |
|---|---|
| Cloudflare one-click (tier 1) | Writes every record through the user's OAuth grant, reads each back from Cloudflare, and finalizes only when every one matches. Unchanged. |
| Domain Connect one-click (tier 2) | Compiles the whole set into one template and one provider approval; the provider writes every record; DoDomain then re-verifies each record against the zone's authoritative nameservers before the session is verified. A redirect back is never proof on its own. |
| Guided manual (tier 3) | Lists every record with copy buttons, checks each one independently, and shows found / not found yet per record. A session is verified only when all records are found. Unchanged. |
Partial progress is reported honestly: after a check, the hosted page shows 2/3 found and marks exactly which record is still missing, with what is actually published at that name when something is there but does not match.
Email records: how verification reads them
Ownership tokens are compared byte for byte, case-sensitively — the whole point of a token. Email policies are not tokens, and comparing them byte for byte would fail the healthy outcome. Verification therefore reads the three email record types by their meaning:
- SPF (
TXTat@,v=spf1 …). A zone may hold only one SPF record — two of them is a permanent error that fails every sender (RFC 7208 §3.2). So the correct result of "addinclude:_spf.yourproduct.com" is the zone's existing SPF record now containing that mechanism, which is exactly what Domain Connect'sSPFMmerge does and what a user does by hand. Verification passes when every mechanism your session asked for is present in the publishedv=spf1record; the terminal~all/-allis the zone owner's choice and is not compared. When an SPF record exists but lacks your mechanism, the hosted page says so and tells the user to edit that record rather than add a second one. The page also warns when the published record makes more than 10 DNS lookups on its own terms (the RFC 7208 evaluation limit) — a warning beside a found record, never a verification failure. - DKIM (
CNAMEorTXTat<selector>._domainkey). A CNAME is compared like any hostname. A keyTXTis compared tag by tag:vandkfall back to their RFC 6376 defaults when the provider omits them, and the key inp=is compared with whitespace removed, because long keys come back split across strings. - DMARC (
TXTat_dmarc). Compared tag by tag: the published record must bev=DMARC1and carry every tag your session asked for with the same value (p=,rua=, and so on); report addresses are compared as sets. Extra tags the zone owner adds are not a mismatch. A_dmarcrecord with a different policy is reported as a different value — waiting will not change it.
Request them the way you would document them for a customer:
{
"domain": "acme.com",
"records": [
{ "type": "TXT", "host": "@", "value": "v=spf1 include:_spf.yourproduct.com ~all" },
{ "type": "CNAME", "host": "yp1._domainkey", "value": "yp1.dkim.yourproduct.com" },
{
"type": "TXT",
"host": "_dmarc",
"value": "v=DMARC1; p=none; rua=mailto:[email protected]"
}
]
}MX is deliberately manual
Sessions may include MX records and every path verifies them (exchange and priority), but the
Domain Connect one-click path never writes one: a wrong MX write black-holes a domain's mail the
moment it propagates, and a connect session knows nothing about the domain's existing mail
routing. If your product receives mail on the customer's domain, the MX stays a manual record —
verified against authoritative DNS like every other, and watched by the monitor afterwards.
Constraints worth designing around
- One
include:per SPF session. The Domain Connect template merges exactly one mechanism, so a session whose SPF value carries more (ip4:,a, a secondinclude:) is entered by hand. Keep your SPF footprint to a single include host you control. - One DKIM selector per session. Providers that rotate between two or three selectors (Microsoft 365, Amazon SES) need one session per selector for one-click, or the manual path for all of them at once.
- Byte-exact prefixes. For one-click, a DKIM
TXTmust start withv=DKIM1;and a DMARCTXTwithv=DMARC1;(with the space) — the template fixes that prefix and appends the rest. Other spellings still work on the manual and Cloudflare paths. - The ownership TXT sits under the host for
custom-subdomain-cname-with-verification:host: "_dodomain-challenge"on a session forapp.acme.comverifies at_dodomain-challenge.app.acme.com. An ownership TXT at the zone apex pairs withdomain-verificationon its own. - Apex sessions never get a CNAME.
apex-a-with-verificationwritesA/AAAA; Cloudflare's Domain Connect implementation rejects theAPEXCNAMErecord type outright, and the Cloudflare one-click (OAuth) path remains the more capable option on Cloudflare zones.
Nothing about the API shape changed for any of this: records[] is the same array it always was, and the SDKs are untouched.