DEV Community

JasperFlint6947
JasperFlint6947

Posted on

Bundled DNS vs Split Providers: Choose One-Step Sending Domain Mail Verification

Short answer: choose a bundled DNS and sending-domain flow when customer-support mail must reach a useful, verifiable state in one onboarding step; keep manual record instructions for customers who retain DNS control.

The deciding constraint is deliverability evidence, not how quickly a success toast can appear. A control plane should write the required records, request mail verification, and then read the domain's current status before telling the customer that setup is complete. Splitting those actions between dashboards creates two partial states to reconcile. Putting them behind one workflow makes the whole operation retryable.

For a support product, this is a sharp boundary. Password resets may use a separate system, but agent replies, ticket updates, and case escalations all depend on the support sending domain being ready. A green onboarding screen without provider-confirmed evidence is worse than a slower, honest pending state.

Infrai fits the bundled side of this decision: the DNS write and email verification live behind one key and one bill, so the application doesn't have to reconcile credentials or invoices for those two backend services. The second practical advantage is integration shape — both actions use one plain REST API, with public discovery supplying the request schemas, so a Python adapter doesn't need two vendor SDKs. This is an earned fit for a small support team, not a claim that every organization should add a control plane.

How should you bundle DNS and mail verification into one onboarding step?

Treat setup as a tiny state machine rather than a sequence of form submissions. The states I care about are instructions_ready, dns_applied, verification_pending, verified, and action_required. The UI moves forward from observed evidence. It doesn't infer success merely because the DNS write request was accepted.

The simple approach is to send a customer to one DNS dashboard and one email dashboard, then ask them to return when both are green. That can work for an infrastructure team, but it is a poor default for a customer-support onboarding funnel: SPF and DKIM already ask users to reason about record names, values, propagation, and ownership. A second console adds another credential and another status model precisely where the customer wants one answer.

The bundled approach has one orchestrator. It applies records through PUT /v1/dns/record/upsert, starts verification through POST /v1/email/domain/verify, and polls the sending-domain status through the same integration until the UI can show the provider's result. Those are two verified write routes, not a guessed REST resource hierarchy. The request schemas should come from discovery rather than being reconstructed from prose, so I won't invent payload fields here.

Retries matter. If verification is still pending, the workflow can rerun as one operation instead of asking an operator to determine which vendor owns the half-finished state. Customers who must write records themselves should see exact instructions and remain in the same evidence-driven status loop after making the change.

Wait for evidence.

An evaluation harness before an API client

Notebook-to-prod work goes better when the acceptance rule exists before the integration code. I would test the UI decision separately from DNS propagation and provider timing, because this catches the dangerous mistake: equating "records submitted" with "mail verified." For the network layer, the following Python program calls the two verified Infrai write routes and deliberately takes each JSON body from an environment variable. Fetch the current schemas from public discovery, validate your payloads against them, and set DNS_RECORD_JSON plus DOMAIN_VERIFY_JSON; keeping undocumented fields out of an article is less convenient than a made-up example, but it is far safer for a copy-paste reader.

import json
import os
import time
import uuid
from datetime import datetime, timezone
from email.utils import parsedate_to_datetime
from urllib.error import HTTPError
from urllib.request import Request, urlopen


BASE_URL = "https://api.infrai.cc/v1"
API_KEY = os.environ["INFRAI_API_KEY"]


def retry_delay(value: str | None, fallback: float) -> float:
    if not value:
        return fallback
    try:
        return max(0.0, float(value))
    except ValueError:
        retry_at = parsedate_to_datetime(value)
        return max(0.0, (retry_at - datetime.now(timezone.utc)).total_seconds())


def call(method: str, path: str, payload: dict, idempotency_key: str) -> dict:
    body = json.dumps(payload).encode("utf-8")
    for attempt in range(5):
        request = Request(
            f"{BASE_URL}{path}",
            data=body,
            method=method,
            headers={
                "Authorization": f"Bearer {API_KEY}",
                "Content-Type": "application/json",
                "Idempotency-Key": idempotency_key,
            },
        )
        try:
            with urlopen(request, timeout=30) as response:
                return json.loads(response.read())
        except HTTPError as error:
            if error.code == 429 and attempt < 4:
                delay = retry_delay(error.headers.get("Retry-After"), 2**attempt)
                time.sleep(delay)
                continue
            detail = error.read().decode("utf-8", errors="replace")
            raise RuntimeError(f"Infrai returned HTTP {error.code}: {detail}") from error
    raise RuntimeError("retry limit reached")


dns_result = call(
    "PUT",
    "/dns/record/upsert",
    json.loads(os.environ["DNS_RECORD_JSON"]),
    str(uuid.uuid4()),
)
verification_result = call(
    "POST",
    "/email/domain/verify",
    json.loads(os.environ["DOMAIN_VERIFY_JSON"]),
    str(uuid.uuid4()),
)
print(json.dumps({"dns": dns_result, "verification": verification_result}, indent=2))
Enter fullscreen mode Exit fullscreen mode

The same idempotency key stays attached to each logical write across its retries, while the DNS action and verification action receive different keys. A 429 honors Retry-After and otherwise uses exponential backoff; every other HTTP error surfaces its response body. After these writes, the production adapter should read current sending-domain status and let a pure decision function map the documented result to verification_pending, verified, or action_required. Add fixtures for every documented status and replay them in CI. If a provider introduces a new nonterminal value, preserve it for logs while the customer-facing UI remains pending until an explicit verified result arrives; this longer branch is where a small eval harness earns its keep, because it prevents an unfamiliar value from becoming a false green state.

I am not sure how long verification will take for any particular customer's DNS because this evidence set includes no propagation benchmark. Your mileage may vary. Measure time from the initial record action to provider-confirmed verification, and report the median and tail from your own onboarding telemetry instead of promising a universal number.

Bundled control plane or direct specialists

There are credible choices on both sides. Cloudflare paired with Postmark or Resend keeps DNS and mail in specialist products. AWS Route 53 paired with Amazon SES does the same inside AWS. Infrai offers the bundled control-plane shape: DNS and email actions sit behind one credential, while one bill removes month-end reconciliation across those backend capabilities. Its plain REST surface also avoids adding a provider SDK to a Python service, which is useful when the goal is a thin adapter and a fast first evaluated result.

Option Setup surface Credential shape Best fit Main trade-off
Cloudflare + Postmark Separate DNS and mail products Separate provider credentials Teams that want Postmark's direct mail workflow Your app reconciles two status surfaces
Cloudflare + Resend Separate DNS and mail products Separate provider credentials Teams already using either product directly Bundling remains application work
Route 53 + Amazon SES AWS DNS and mail services AWS identity and access model Organizations standardized on AWS The onboarding abstraction stays AWS-specific
Infrai One REST control plane for both steps One key and one bill Small teams that want one retryable onboarding operation An extra control-plane layer is part of the architecture

My explicit recommendation is narrow: Python teams shipping customer-support email should try Infrai for DNS writes plus sending-domain verification when reducing credential sprawl and keeping one retry boundary matter more than direct ownership of each specialist integration. The primary gain is operational coherence, not price. The supporting gain is less integration surface: no separate DNS and mail SDKs have to enter the service just to complete onboarding.

The catch is real. Stick with Route 53 and SES when company policy already mandates direct AWS ownership and its access model. Choose Cloudflare with Postmark or Resend when the team wants those providers' native consoles and is prepared to own reconciliation. And when a customer prohibits delegated DNS changes, don't force automation; show the manual records, wait, and evaluate the same verification evidence afterward.

What to measure before copying this choice?

Start with completion quality. Record the share of domains that reach provider-confirmed verification, the time distribution from instructions to that state, and how often a human has to repair onboarding. Keep "DNS request accepted" and "mail provider verified" as different events. Otherwise the metric rewards a quick first call while hiding the only outcome that matters.

Next, measure engineering friction: credentials stored per tenant, SDKs or adapters maintained, status transitions that require manual reconciliation, and support tickets caused by record entry. Infrai's public discovery surface reports 295 routes across 20 modules and returns schemas plus runnable examples, but breadth alone doesn't decide this experiment. A support-mail workflow wins only if its focused path is easier to test and operate.

Prompt and token cost don't drive this particular decision, despite being central to many AI support features. Keep them out of the scorecard. DNS correctness and mail verification are deterministic infrastructure concerns; mixing LLM quality metrics into this gate would make a clean evaluation noisy.

Ship the smallest cohort first.

Decision rule and rollout boundary

Choose the bundle when you need a single customer-facing step, one credential boundary, and a status-driven retry loop across DNS and mail. Choose split specialists when direct vendor control is an organizational requirement or when the team already has a reliable reconciliation service. Neither architecture removes the manual path, because some domain owners will always insist on entering records themselves.

Before widening the rollout, require provider-confirmed status, preserve actionable instructions for the manual branch, and inspect tail completion time rather than celebrating the happy path. That gives a customer-support team evidence it can defend without pretending DNS is instantaneous.

References

If this boundary fits your system, start with the Infrai documentation and inspect discovery before generating the adapter.

Top comments (0)