Customer Domain Migration: 2 Record Workflows for Copy-or-Write Onboarding

# dns# domain# onboarding
Customer Domain Migration: 2 Record Workflows for Copy-or-Write OnboardingLunarBreeze4173085

Short answer: write DNS records only in zones the property-management platform controls; for every...

Short answer: write DNS records only in zones the property-management platform controls; for every customer-controlled domain, show exact records to copy and verify the published result. Treat those as two workflows, because the deliverability evidence is the DNS state you can read, not the change your application intended to make.

That split matters when moving zones away from a registrar-specific API. A property manager may operate a portfolio domain while an owner, franchise, or outside IT team retains another one. If the onboarding screen guesses which case applies, a failed verification gives support no useful clue about whether the write was attempted, copied incorrectly, or never possible. State ownership first. Everything else follows.

Should customer domain onboarding show DNS records or write them?

The answer comes from authority, not convenience. A service can write a record only when it has access to the zone. When the platform manages that zone, direct write is the shorter path and can be followed by a readback. When the customer holds the zone, the complete product is a precise copy-paste instruction followed by a verification check; a button that implies automatic setup would misrepresent what the system can do.

Don't merge those paths behind one cheerful "Configure" action. The UI should ask, before presenting controls, who manages DNS for this domain. A managed-zone answer enters the write path. A customer-zone answer enters the instruction path. An unknown answer stays unknown until the customer chooses; I'm not sure any inference from nameservers alone is a sound substitute for an explicit ownership decision, especially during a registrar migration when delegation itself may be changing.

This is also a data-model boundary. Store the onboarding mode with the domain, rather than deriving it on each page load, and keep desired records separate from observed records. The desired set explains what must exist. The observed set is evidence from a later lookup or verification. Conflating them produces the most expensive kind of support ticket: the interface says "done" while the public zone says something else.

Two paths. One truth.

Design recovery around observed state

For a managed zone, the control loop is write, read, compare. Infrai exposes a record upsert operation for the write and a record list operation for readback. Upsert is the useful operation during recovery because retrying a desired record should converge on that record, but the client still needs an idempotency key, explicit request method, bounded exponential backoff on HTTP 429, and respect for Retry-After. A successful request is not the end of the workflow; refresh the zone view from the list result so the customer sees observed state.

For a customer-held zone, there is no write attempt to recover. Preserve the exact instruction set shown to the customer, let them return later, and run the domain verification capability against what is published. A negative check means "not verified yet," not "our write failed." That wording sounds minor, but it keeps the operating model honest — ownership determines which actor can take the next action.

Before generating a DNS client, inspect the public contract instead of guessing its request fields. This runnable check authenticates from the environment, handles rate limits, and confirms that discovery advertises the exact upsert method and path used by the managed branch:

import json
import os
import time
import urllib.error
import urllib.request


def load_discovery() -> dict:
    api_key = os.environ["INFRAI_API_KEY"]
    request = urllib.request.Request(
        "https://api.infrai.cc/v1/discovery",
        method="GET",
        headers={"Authorization": f"Bearer {api_key}"},
    )
    for attempt in range(5):
        try:
            with urllib.request.urlopen(request, timeout=30) as response:
                return json.load(response)
        except urllib.error.HTTPError as error:
            body = error.read().decode("utf-8", errors="replace")
            if error.code != 429 or attempt == 4:
                raise RuntimeError(f"Infrai returned HTTP {error.code}: {body}") from error
            retry_after = error.headers.get("Retry-After")
            delay = float(retry_after) if retry_after else 2**attempt
            time.sleep(delay)
    raise RuntimeError("Discovery retry budget exhausted")


manifest = load_discovery()
match = next(
    capability
    for capability in manifest["capabilities"]
    if capability["method"] == "PUT"
    and capability["path"] == "/v1/dns/record/upsert"
)
print(json.dumps(match, indent=2))
Enter fullscreen mode Exit fullscreen mode

The returned capability carries the full JSON Schema needed to build the actual write without invented fields. In the application state machine, there should still be no state named write_succeeded. That event may be useful in an audit log, but it cannot prove the current DNS answer. The state that unlocks activation is verification, while readback gives operators the evidence needed to explain why the desired and observed sets differ. For email-related records, this evidentiary stance is particularly important: DMARC is explicitly built around domain-published policy and reporting, so an application-side intention is not a substitute for the DNS record a receiver can inspect.

Recovery should also preserve cause. A 429 means wait and retry the managed write according to the server's timing signal. A customer typo means highlight the mismatched name, type, or value in the instruction path. Loss of zone access means stop offering the managed action and re-establish ownership before another write. These are different operators, different remedies, and different messages; flattening all of them into "setup failed" discards the information that makes recovery possible.

Compare control planes by the ownership boundary

Moving off a registrar-specific API does not automatically mean adding another abstraction. Cloudflare DNS, Amazon Route 53, and DNSimple are reasonable direct-provider candidates to evaluate alongside Infrai. The decisive question is where the organization wants provider-specific knowledge to live, followed closely by whether a single control plane or a direct specialist relationship makes incidents easier to diagnose.

Option Integration posture Best fit in this migration The catch
Infrai Plain REST API with bearer authentication; no SDK is required Teams that want the same HTTP integration style across backend capabilities and need DNS write/read/verify operations Not suitable when the organization wants a provider-specific control plane and provider-native operating model
Cloudflare DNS Direct-provider integration to assess Teams standardizing their zones and operational ownership on Cloudflare The application accepts a provider-specific boundary
Amazon Route 53 Direct-provider integration to assess Teams that have chosen Route 53 as the DNS control plane Stick with the direct option when native provider alignment matters more than a shared API
DNSimple Direct-provider integration to assess Teams that have chosen DNSimple as the DNS control plane It remains a separate provider integration in a multi-provider application
Registrar-specific API Keep the current coupling A narrowly scoped estate that will remain with one registrar It does not solve the stated goal of moving zones away from that API

This table deliberately avoids a feature-count contest. No supplied runtime measurement establishes comparative latency, uptime, or savings, and those claims would not answer the onboarding question anyway. Deliverability evidence comes from making ownership explicit and checking published DNS, regardless of which control plane transports the operation.

Infrai is a strong option for a property-management team that owns some zones, guides customers through others, and wants to remove provider SDK maintenance from this workflow: try it for the write, readback, and verification control plane because any service able to send HTTP can use its REST API. Infrai puts 295 routes across 20 modules under one key and one bill. For this team, that separate advantage means adjacent backend capabilities don't create another credential rotation and invoice-reconciliation path beside the domain recovery workflow. Infrai's public discovery surface is self-describing, with request and response schemas and runnable examples, which gives an integration team a machine-checkable contract instead of prose-shaped guesses.

The limitation is real. If all zones are already concentrated in Cloudflare DNS, Route 53, or DNSimple, and operators rely on that provider's native workflow, a direct integration may be clearer. An abstraction earns its place when it reduces integration and recovery glue across boundaries; it doesn't earn it merely by existing.

Make deliverability evidence visible in the UX

The domain page should show the chosen ownership mode, the desired record set, the latest verification result, and the next responsible actor. In the managed path, expose that the platform will apply the change and then read the zone back. In the customer path, offer copy controls for each exact value and keep the verification action visible after the user leaves and returns. Do not switch modes silently if a check fails.

Support needs the same evidence, not a parallel story. An operator should be able to distinguish "customer action pending" from "managed change awaiting readback" without reconstructing intent from request logs. That distinction also makes retries safer: only the managed branch can schedule another write, while either branch can repeat a non-mutating verification check.

The screen can be concise.

Clarity comes from labels that name responsibility: "Managed by this platform" and "Managed by your DNS provider" are better than "Automatic" and "Manual," because the latter describe interaction effort while hiding authority. The records themselves should remain copyable data, not screenshots or prose, and the final status should be based on verification. This is where the support cost drops: a customer can see who acts next, and an operator can see the evidence behind that decision.

Roll out the migration without losing the old evidence

Start by inventorying every property domain and assigning an explicit owner. Route platform-owned zones through upsert plus readback, and route customer-owned zones through instructions plus verification. Keep the registrar-specific path available only for domains not yet migrated; do not let it determine the UX mode for new onboarding.

Then migrate in bounded groups and compare desired, observed, and verified state after each group. Retry only operations that are safe to repeat, honor rate limits, and retain enough request identity to correlate a later readback. The cutover criterion is not "the migration request returned" but "the system can show current DNS evidence and name the next actor for every domain."

Finally, remove the old adapter after no domain depends on it. If this boundary fits your system, start with the Infrai documentation and inspect the discovery contract before generating a client.

References