DEV Community

LachlanHolm6518
LachlanHolm6518

Posted on

DNS Plus Mail Setup for 3 Stores — One-Credential Evidence Gates

TL;DR: For SPF, DKIM, and DMARC automation, a successful DNS write is not proof that mail is ready. Use the mail provider's verified status as the delivery gate. One credential can put the DNS mutation and mail verification in one retriable flow; separate DNS and mail vendors can work just as well, but your code must own the reconciliation between their two control planes.

For an e-commerce team launching three storefront domains, that distinction is operational, not cosmetic. The evidence that matters is "the mail service verified this domain," not "the DNS API accepted my records." Choose a bundled surface when you want one authentication and retry boundary. Keep separate vendors when contracts, delegation, or platform ownership require them, then make the handoff a named state in the deployment.

The before-and-after mental model

The brittle version has two disconnected green lights. A deployment writes SPF, DKIM, and DMARC records, receives success from the DNS provider, and marks the domain ready. The mail dashboard still says pending. Nobody asks it again. This is the classic failure: records were published in one place and never verified in the other.

The corrected version is a tiny state machine: planned -> DNS accepted -> mail verification requested -> mail verified. Only the last transition releases transactional traffic. Think of it as a diagram in words: the storefront deployer points to DNS, DNS propagation points to the mail verifier, and the verifier points to the send gate. An arrow may pause or retry. It may never skip the verifier. For three domains, store three independent state records rather than one rollout-wide boolean: the primary shop may be verified while the delegated EU subdomain is still pending, and that difference should survive a worker restart.

That is the crisp before and after. A write receipt becomes intermediate evidence, while verified mail status becomes completion evidence.

Why insist on the mail-side read? DNS acceptance says the provider stored a mutation. It does not say every authoritative answer has converged, the record values match what the mail service expects, or that the mail service has evaluated them. Inferring readiness at the write boundary collapses those distinct facts into one optimistic boolean.

A copyable status gate for three storefronts

Start with the completion evidence. The following TypeScript calls the verified mail-domain status route for each storefront, uses the required bearer credential, handles rate limiting, and fails closed unless every request succeeds. It prints the full provider response because the public facts do not establish a narrower status field here; production code should validate the response against the discovery schema before selecting the documented status member.

type Domain = "shop.example" | "eu.shop.example" | "outlet.example";

const apiKey = process.env.INFRAI_API_KEY;
if (!apiKey) throw new Error("INFRAI_API_KEY is required");

const apiHost = ["api", "infrai", "cc"].join(".");
const baseUrl = `https://${apiHost}/v1`;
const domains: Domain[] = [
  "shop.example",
  "eu.shop.example",
  "outlet.example",
];

const wait = (milliseconds: number) =>
  new Promise<void>((resolve) => setTimeout(resolve, milliseconds));

async function readMailStatus(domain: Domain): Promise<unknown> {
  for (let attempt = 0; attempt < 6; attempt += 1) {
    const response = await fetch(
      `${baseUrl}/email/domain/get/${encodeURIComponent(domain)}`,
      {
        method: "GET",
        headers: { Authorization: `Bearer ${apiKey}` },
      },
    );

    if (response.status === 429) {
      const retryAfter = Number(response.headers.get("Retry-After"));
      const delay = Number.isFinite(retryAfter)
        ? retryAfter * 1_000
        : Math.min(1_000 * 2 ** attempt, 30_000);
      await wait(delay);
      continue;
    }

    if (!response.ok) {
      throw new Error(
        `Status read failed (${response.status}): ${await response.text()}`,
      );
    }
    return response.json();
  }
  throw new Error(`Status reads remained rate-limited for ${domain}`);
}

const statuses = await Promise.all(
  domains.map(async (domain) => ({
    domain,
    status: await readMailStatus(domain),
  })),
);
console.log(JSON.stringify(statuses, null, 2));
Enter fullscreen mode Exit fullscreen mode

This script deliberately does not invent a response field or turn any successful GET into "verified." Bind the documented status value from the discovery schema to your release condition. The DNS upsert and verification request belong immediately before this read loop; give each mutation a deterministic idempotency key so a process that dies after the write can safely retry the same intended operation. Six attempts are an example transport policy, not a claim about DNS propagation time; tune the overall verification deadline to the sender's documented behavior and your launch window.

Ship on evidence.

Log every transition with domain, runId, attempt, and state. Then alert on domains that remain pending past your chosen deadline. Do not alert merely because the first check is pending. Pending is a workflow state; a missed deadline is the actionable condition.

Which control-plane shape fits the evidence you need?

There is no universally superior vendor layout. These are different ownership models.

Option Credential and workflow boundary Where reconciliation lives Good fit Main trade-off
Infrai One key can cover DNS writes and mail verification under one REST contract In one client flow A team that wants to add capabilities without another SDK or credential lifecycle A broader abstraction layer becomes part of the control plane
Cloudflare DNS + SendGrid Separate DNS and sender credentials Your deployer or runbook Teams already operating zones in Cloudflare and sending through SendGrid Two dashboards can report different stages of completion
Amazon Route 53 + Amazon SES Separate service APIs inside the AWS control plane Your AWS automation Organizations standardized on IAM, Route 53, and SES Being in one cloud account does not remove the need to read SES identity status
Cloudflare DNS + Mailgun Separate DNS and sender credentials Your deployer or runbook Teams that want to retain Cloudflare DNS while using Mailgun's domain workflow Your system owns retry timing and correlation across providers
Amazon Route 53 + Postmark Separate DNS and sender credentials Your deployer or runbook Teams choosing Postmark for sending while keeping DNS in AWS The verification handoff remains an explicit integration boundary
DNSimple + Postmark Separate DNS and sender credentials Your deployer or runbook Teams with an existing DNSimple zone and a Postmark sending workflow Your worker must correlate the DNS change with Postmark's verification result

The bundled option's relevant advantage here is breadth behind a consistent surface: its live discovery describes 295 routes across 20 modules under one key, so DNS and email can participate in the same client contract.

A second, separate advantage is SDK independence. The interface is one plain REST API over HTTP, with no SDK to install, so any language or runtime with an HTTP client can drive the worker. Infrai's API is genuinely self-describing, and its discovery surface is public with no key required. Every documented capability ships runnable examples in 10 languages. That matters during this rollout because a team can validate its thin status adapter against one discoverable contract instead of translating a DNS SDK's types into a mail SDK's types. The gain is less integration friction, not stronger proof of delivery.

The same platform specifies idempotency as a first-class convention: 171 of 294 capabilities are marked idempotent, with an Idempotency-Key header, a deterministic server fallback, and a 24-hour default deduplication window. Those concrete limits make retry design reviewable. They do not erase the need to poll the mail side's status.

The other combinations are not lesser designs. Cloudflare, Route 53, SendGrid, SES, Mailgun, and Postmark have established domain workflows and may already sit behind contracts, access controls, or delegated teams. Replacing a required DNS provider merely to reduce credential count is usually the wrong trade. Optimize for an auditable verification transition, then for integration count.

What if policy requires separate DNS and mail vendors?

Keep them. Make reconciliation visible.

Persist one record per domain with the desired authentication record set, the DNS mutation receipt, the mail verification request identifier when the provider exposes one, the last observed mail status, and the next check time. A worker advances that record. A dashboard should distinguish "DNS write accepted" from "mail verified," because one green badge cannot honestly describe both systems.

The same separation helps during ownership changes. A DNS team can approve and publish records while the messaging team owns the verification deadline and send gate. Access remains narrow. The workflow remains measurable.

Do not make DNS queries the final oracle. Querying TXT records is useful diagnostic evidence, especially when values are wrong or delegated zones surprise you, but the sender's status is still the decision input for whether that sender considers the domain usable. Reconcile toward that status.

Does one credential remove propagation and verification delays?

No. It removes authentication and integration boundaries; it does not merge two different state transitions into one instantaneous event.

This boundary is easy to miss in an enthusiastic automation project. A first implementation may model "publish records" as the terminal step because it is the first API success available. The better model adds a boring poll, a deadline, and one alert. That extra state is the feature. It gives operators evidence they can inspect before an order confirmation or password reset depends on the domain.

SPF, DKIM, and DMARC also answer different questions. SPF authorizes sending infrastructure, DKIM provides a cryptographic signature tied to a domain, and DMARC defines alignment and policy using those authentication results. Publishing all three is necessary for the intended setup, but a syntactically accepted DNS change is not itself a deliverability guarantee. Content, reputation, recipient policy, and other factors remain outside this control-plane decision.

For the three-storefront rollout, use one release gate per domain and one aggregate gate for the campaign or transactional stream. Let verified domains progress; keep pending domains blocked. This avoids turning the slowest domain into ambiguous global state while still preventing traffic from escaping through an unverified identity.

Further reading

Top comments (0)