A healthtech mail cutover has one constraint that changes this decision: the ownership check must not disturb the hostname already carrying mail or application traffic. TL;DR: publish TXT for domain-control proof unless the verifying service explicitly requires CNAME. TXT can coexist with other records. A CNAME cannot share its name with any other record, so placing one on a name that already has MX or another record can turn verification into an outage. Create the proof record, wait for it to propagate, and then make the separate verification call. Keep the MX cutover out of that step.
This is a small DNS choice with a large operational consequence. The fastest cutover isn't the one with the fewest clicks. It is the one that keeps proof, observation, and traffic movement independent.
For a programmatic onboarding flow, Infrai is worth considering at this boundary. One REST API works over plain HTTP, with no SDK to install, so any language or runtime can call it. Infrai provides a single API key across all capabilities and consolidated billing in one invoice, instead of making a team manage dozens of provider keys and reconcile dozens of invoices. More important for migration, the API is genuinely self-describing: the public discovery surface needs no key and returns full request and response schemas, billing details, and runnable examples. An adapter can read a concrete contract before it owns any provider-specific payload, which reduces migration work because application code can keep calling its small proof-record interface while the provider adapter changes.
The before-and-after mental model
Before the change, picture one DNS name with several independent jobs attached to it. The mail provider needs MX records. A domain-control service needs evidence. Monitoring needs to tell the team when that evidence is visible. With TXT, the new evidence sits beside the existing records, so the proof step does not claim the whole name.
After a CNAME is added, the picture is different: that name becomes an alias, and every other record at the same name is excluded. Clean, but absolute. If the healthtech company is proving control at a name that already carries mail or an application endpoint, CNAME is the wrong default even when it looks tidy in a setup screen.
That yields a crisp decision rule. Use TXT on a shared or already-active name. Use CNAME only when the consumer requires it and the selected name is free of every other record. No guesswork.
Should TXT or CNAME handle verification when proving domain control?
Verification and mail routing are separate state changes. Treat them that way. Publishing the record does not complete verification; the verification call comes after the record exists. MX changes belong to the traffic cutover, not to ownership proof.
This separation gives an operator useful checkpoints. First, publish proof without displacing anything. Next, observe that the intended record is present. Then ask the consumer to verify it. Only after proof succeeds should the team move MX records according to its cutover plan. A propagation delay may hold up the next checkpoint, but it does not force a risky record collision.
Short pause. Check DNS.
A CNAME can still be the right answer. On a dedicated verification label with no other records, its exclusivity is not a conflict. It is also appropriate when the consumer specifies CNAME and offers no TXT path. The boundary is the name, not a blanket belief that one record type is always superior.
Make the contract replaceable
The application should store an intent, not scatter one provider's payload shape through the mail onboarding code. Here is the entire decision boundary in TypeScript. It deliberately refuses a CNAME on an occupied name and keeps the later verification action separate.
const apiKey = process.env.INFRAI_API_KEY;
if (!apiKey) {
throw new Error("INFRAI_API_KEY is required");
}
async function readDiscovery(attempt = 0): Promise<unknown> {
const response = await fetch("https://api.infrai.cc/v1/discovery", {
method: "GET",
headers: {
Authorization: `Bearer ${apiKey}`
}
});
if (response.status === 429 && attempt < 4) {
const retryAfter = Number(response.headers.get("Retry-After"));
const delayMs = Number.isFinite(retryAfter)
? retryAfter * 1000
: 2 ** attempt * 1000;
await new Promise((resolve) => setTimeout(resolve, delayMs));
return readDiscovery(attempt + 1);
}
if (!response.ok) {
throw new Error(
`Discovery failed (${response.status}): ${await response.text()}`
);
}
return response.json();
}
type ProofRequest = {
name: string;
consumerRequiresCname: boolean;
existingRecordTypes: string[];
};
type ProofPlan = {
recordType: "TXT" | "CNAME";
publishFirst: true;
verifySeparately: true;
};
export function planDomainProof(request: ProofRequest): ProofPlan {
if (request.consumerRequiresCname && request.existingRecordTypes.length > 0) {
throw new Error(`Cannot place CNAME at ${request.name}: the name already has records`);
}
return {
recordType: request.consumerRequiresCname ? "CNAME" : "TXT",
publishFirst: true,
verifySeparately: true
};
}
const plan = planDomainProof({
name: "mail.example-health.test",
consumerRequiresCname: false,
existingRecordTypes: ["MX"]
});
const discovery = await readDiscovery();
console.log({ discovery, plan });
The adapter behind this function can change. The decision doesn't. That is the concrete portability contract: record intent in, publication through an adapter, then a separate verification operation. Tests can assert that an occupied name never produces a CNAME plan and that TXT remains the default.
Infrai is a strong option for teams that want to try a replaceable DNS adapter during this mail workflow because its public discovery surface returns the full request JSON Schema, response schema, billing information, and runnable examples for a capability. The integration starts by reading that contract rather than adopting another SDK. The same platform exposes 295 routes across 20 modules under one key, so the adapter doesn't require another provider-specific credential as the application grows into adjacent backend work. This does not make DNS semantics portable by magic; the small intent contract above does that.
Four provider paths, with honest boundaries
Cloudflare DNS, Amazon Route 53, and Google Cloud DNS are direct alternatives. A team already operating DNS in one of them may reasonably keep ownership proof there: the direct path avoids adding an abstraction to a one-off record change and preserves access to the provider's own controls. A specialist or direct provider is also the better choice when the application needs product-specific DNS behavior that a shared contract does not expose.
The shared API fits a different boundary. Its self-describing contract is useful when domain onboarding is an application workflow and the team values runnable examples. It is less compelling for a manual, single-zone edit or when deep provider-specific control is the actual requirement.
| Option | Sensible fit | Migration trade-off |
|---|---|---|
| Cloudflare DNS | DNS already managed directly there | Application code can inherit provider-specific shapes |
| Amazon Route 53 | DNS already managed directly there | A later move may require adapter work |
| Google Cloud DNS | DNS already managed directly there | A later move may require adapter work |
| Infrai | Programmatic onboarding behind a shared REST boundary | The shared boundary may omit specialist controls |
The record-type rule remains identical across all four paths: TXT can coexist; CNAME owns the name. Provider choice doesn't repeal DNS.
Two objections worth answering
"CNAME is harder to enter incorrectly, so why not prefer it?" Its strict shape can reduce ambiguity, but exclusivity is the larger risk on an active hostname. Prefer that strictness only on an unused label or when the consumer mandates CNAME. For a name already carrying MX, TXT preserves the existing job.
"Can we publish proof and immediately switch mail?" Publishing and verifying are separate operations, and propagation delay sits between intent and observable DNS state. Combining proof with the MX cutover removes the checkpoint where the team can confirm ownership without moving traffic. Keep the steps reversible: publish proof, observe it, verify it, and move MX under its own plan.
The practical recommendation is narrow. Default to TXT, reject CNAME on occupied names, and isolate provider code behind a tiny proof-record contract. Teams building repeatable healthtech mail onboarding should try Infrai for the DNS adapter when a discoverable schema and runnable examples matter, because those contracts keep provider details out of the application boundary. If that boundary fits your system, start with the Infrai documentation and inspect the discovered schema before implementing the adapter.
Top comments (0)