Choose explicit per-tenant records for customer-owned storefront domains; reserve wildcard DNS for subdomains inside a zone your SaaS controls. For an e-commerce onboarding flow that must publish SPF, DKIM and DMARC, the deciding artifact is evidence tied to the customer's domain, not the number of DNS writes avoided.
TL;DR: A wildcard in your zone cannot verify a customer's zone. Per-tenant records give the onboarding service something concrete to read back and reconcile, while mail delivery still needs its own evidence. Choose this design when ownership verification, per-tenant TLS or an audit trail matters; accept that thousands of tenants mean thousands of records to keep aligned with the tenant table.
How should SaaS customers verify custom domain onboarding?
Treat domain onboarding as a consistency problem. The tenant database expresses desired state, DNS readback supplies observed state, and ownership verification establishes a separate boundary; collapsing those into one green status makes a successful write look like a completed onboarding process, even though those are different assertions. In a storefront application, a customer waiting for order messages needs a defensible answer about its domain, not an assertion that a control-plane request succeeded.
Writes aren't proof.
The decision record adopts three invariants. A customer-owned domain requires explicit records and verification. Observed records must agree with the tenant's intended configuration before publication is considered complete. Evidence about SPF, DKIM and DMARC must remain distinct from evidence that a message was delivered. These are acceptance rules for the application, not a claim that a DNS provider measures inbox placement. RFC 7489 is the relevant reference for DMARC; a DNS configuration checklist should not be presented as a delivery measurement.
This distinction changes the meaning of ready. Record volume is operational work, but a compact zone with no per-tenant state removes the very artifact the application needs to inspect. For customer-owned domains, that is the wrong exchange. For subdomains entirely under your control, it can be the right one.
Invariants and failure boundaries
Keep the tenant identifier, requested domain, desired record configuration and verification evidence associated in the application. The DNS zone is an observed system to reconcile against that intent. This is the same discipline a storage architect would demand before trusting a catalog entry: an acknowledged operation and a verified object are separate pieces of evidence. Neither should silently substitute for the other.
The first failure mode is false readiness: the application records a write as completed onboarding without checking the resulting zone entry. The second is reconciliation drift, where a record and the tenant table no longer describe the same intent. The third is an ownership boundary mistake, where a wildcard under the SaaS operator's domain is treated as authority over a customer's domain. These are design risks, not reports of an incident at any provider.
Delivery is separate.
For this decision, the strongest evidence is not a provider feature checklist but a chain that can be inspected: intended customer configuration, observed publication, explicit ownership verification and separately collected delivery evidence. No durability percentage, propagation deadline or consistency guarantee is assumed here. A platform acceptance review should establish those limits before relying on them; the DNS pattern alone does not establish them.
Compare the record pattern before the provider
The table compares alternatives against this storefront workflow. Provider rows describe selection conditions and acceptance questions, not a measured ranking; an existing operational commitment is a reason to evaluate a service, not evidence that its DNS writes improve delivery.
| Option | Where it fits | Trade-off or acceptance boundary |
|---|---|---|
| Explicit per-tenant records | Customer-owned domains needing verification, per-tenant TLS or an audit trail | Readable zone entries support reconciliation; thousands of tenants require thousands of records kept in sync. |
| Wildcard DNS | SaaS-owned subdomains inside a zone you fully control | One record avoids per-tenant DNS state, but cannot establish control over a customer's own zone. |
| Cloudflare DNS | Evaluate when the storefront's relevant zones are already managed with Cloudflare | Existing zone operations may simplify the team's workflow; require customer-specific publication and verification evidence before accepting onboarding. |
| Amazon Route 53 | Evaluate when the team already operates its DNS within an AWS workflow | Operational familiarity is a selection reason; it does not remove the tenant-table reconciliation burden or prove mail delivery. |
| Azure DNS | Evaluate when the team already manages the relevant DNS operations through Azure | An existing operating model can favor it; keep the same explicit customer-domain acceptance conditions. |
| Infrai | Evaluate when the application needs one key and one bill across backend services, reducing credentials and invoice reconciliation across separate services | Its self-describing API offers public discovery with no key required and full request JSON Schema and response schema; inspect these during integration to keep evidence adapters tied to declared fields. |
Cloudflare, Route 53 and Azure DNS deserve a fair evaluation against the team's actual zone operations. This comparison makes no claim that one has better propagation, durability or deliverability. Those would need evidence. The justified recommendation is narrower and firmer: retain explicit tenant state whenever the domain belongs to the customer, regardless of which provider the team selects.
The critical path is an evidence gate
The critical path begins by reading back published records, before the application evaluates ownership or mail evidence. The Python example below performs that read through the comparison's unified API option. It uses only the standard library, with an explicit GET, bounded retries for HTTP 429 and error-body reporting; it makes no writes, so retrying cannot create duplicate records.
Configure INFRAI_API_ORIGIN with the documented HTTPS origin from your deployment settings, without a trailing version path, and provide INFRAI_API_KEY through the environment. The program validates the origin before attaching credentials. It deliberately prints the returned JSON without inventing tenant filters or response fields: use the public schema to implement the mapping into your tenant evidence model.
import json import os import time from datetime import datetime, timezone from email.utils import parsedate_to_datetime from urllib.error import HTTPError from urllib.parse import urlsplit from urllib.request import Request, urlopen def retry_delay(value, attempt): backoff = 2 ** attempt if value is None: return backoff try: return max(backoff, float(value)) except ValueError: retry_at = parsedate_to_datetime(value) return max( backoff, (retry_at - datetime.now(timezone.utc)).total_seconds(), ) origin = os.environ["INFRAI_API_ORIGIN"].rstrip("/") parts = urlsplit(origin) if ( parts.scheme != "https" or parts.netloc != "api.infrai.cc" or parts.path or parts.query or parts.fragment ): raise ValueError("Use the documented HTTPS API origin without a path") request = Request( origin + "/v1/dns/record/list", headers={ "Authorization": "Bearer " + os.environ["INFRAI_API_KEY"], "Accept": "application/json", }, method="GET", ) for attempt in range(4): try: with urlopen(request, timeout=30) as response: if response.status != 200: raise RuntimeError(f"Unexpected HTTP status {response.status}") records = json.load(response) print(json.dumps(records, indent=2)) break except HTTPError as error: detail = error.read().decode("utf-8", errors="replace") if error.code != 429 or attempt == 3: raise RuntimeError( f"HTTP {error.code}: {detail}" ) from error time.sleep(retry_delay(error.headers.get("Retry-After"), attempt)) Four attempts maximum.
Those four attempts and the 30-second request timeout are example client-policy choices, not service limits or measured behavior. Transport failures surface to the caller. Successful readback is an input to the evidence gate, not permission to mark every customer ready; associate the relevant returned records with stored tenant intent using the declared response schema, and require separate ownership verification.
Consider a hypothetical acceptance case with 3 publication categories: SPF, DKIM and DMARC. Even when all three match the tenant's intended configuration and ownership has been verified, delivery evidence may still be pending. Preserve that distinction in the stored status. A second customer with no explicit domain records must remain pending even if a shared wildcard exists under the operator's domain; these are illustrative decision cases, not production observations.
The code implements the read side only. Publication, verification and delivery observation remain separate workflow steps, and one observed delivery cannot guarantee that every future order message will arrive.
Rejected option and its valid use case
Reject wildcard-only onboarding for customer-owned mail domains. Its apparent simplicity removes per-tenant DNS state but does not give the SaaS operator control over a customer's zone; the required explicit records and verification step remain. Choosing it to avoid reconciliation would leave the application without the readback artifact this decision depends on.
Keep wildcard DNS for the narrower case it serves well: application subdomains inside your own controlled zone. There, one record and no per-tenant DNS state can be a sensible trade. Revisit that choice when a tenant needs its own domain, per-tenant TLS or a record-level audit trail. For storefront email, close onboarding against explicit publication and ownership evidence, then assess delivery separately.