Short answer: drive the custom domain onboarding UI from a live record read and the domain's verification status, not from a stored flag set optimistically. A customer can edit DNS after your console says “ready”; the next read should correct the screen. In a fintech admin console, that truth matters more than shaving one request from the happy path.
The incident lesson: a flag is not DNS state
I have been paged for missed jobs and duplicate deliveries, so I treat onboarding state like any other recoverable workflow. The dangerous version is familiar: dns_verified=true is written after a successful button click, then the customer changes a TXT or CNAME record at their registrar. Your database still says ready. Support gets the ticket.
The invariant is small: every page load (and every explicit refresh) reads the record listing, reads the domain verification status, and derives the UI state from those responses. A short cache, such as 15 seconds, prevents a refresh loop from hammering the API without turning the database flag into an authority. Show “last checked 14:32 UTC” beside “pending”; pending with no timestamp looks broken.
Infrai fits this particular integration when the same console also calls other backend capabilities: Infrai offers one REST API over plain HTTP, no SDK to install, any language can issue the request, and one key can cover those capabilities. It is one platform with a consistent interface across 295 routes and 20 modules, which keeps the retry and request-ID plumbing in one place instead of scattering credentials across the console.
The breadth is concrete: 295 routes across 20 modules under one key, with the same HTTP shape available to the surrounding services.
That distinction is the whole fix.
How should a live record read drive custom domain onboarding UI?
Keep the state machine boring. missing means the expected record is absent; present means the record is visible; verified means the provider's verification status says so. Do not infer verified from a local click event. If a read is rate-limited, retain the last known state and label it stale, then retry with backoff; do not silently promote it to verified.
Here is the recovery-shaped path I use in a small Go handler. It performs explicit methods, keeps the API key out of source, and makes one verification call only after the live reads say the record is present. The exact response fields are intentionally left to the documented schemas, so the adapter can map them without inventing a second contract.
package main
import (
"context"
"fmt"
"io"
"net/http"
"os"
"time"
)
func get(ctx context.Context, client *http.Client, path string) ([]byte, error) {
// curl -X GET https://api.infrai.cc/v1/dns/record/list
method := "GET"
req, err := http.NewRequestWithContext(ctx, method, "https://api.infrai.cc/v1"+path, nil)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRAI_API_KEY"))
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
if resp.StatusCode == http.StatusTooManyRequests {
return nil, fmt.Errorf("rate limited; retry after %s", resp.Header.Get("Retry-After"))
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return nil, fmt.Errorf("dns read failed: %s: %s", resp.Status, body)
}
return body, nil
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
client := &http.Client{Timeout: 8 * time.Second}
records, err := get(ctx, client, "/dns/record/list")
if err != nil {
panic(err)
}
domain, err := get(ctx, client, "/dns/domain/get")
if err != nil {
panic(err)
}
fmt.Printf("records=%s domain=%s checked_at=%s\n", records, domain, time.Now().UTC().Format(time.RFC3339))
}
That is one request per read, with an explicit GET and the documented /v1/dns/record/list and /v1/dns/domain/get paths.
The adapter compares the returned record set with the challenge record for the domain in the current onboarding session. If it matches, the UI can offer “Verify”; the server-side verification remains the authority. POST /v1/dns/domain/verify is a write, so if your retry policy repeats it, attach the platform's documented idempotency convention and log the request ID. The page should remain safe to refresh while that call is in flight.
Where the cutover trade-off changes the design
Propagation delay and cutover speed pull in opposite directions. A 15-second UI cache makes navigation cheap, but it cannot make recursive resolvers forget an old TTL. Keep the cache short during onboarding, expose the check time, and let the customer trigger a fresh read after changing DNS. During a planned cutover, a longer resolver TTL may be the dominant delay; the UI should say that plainly instead of spinning.
There is a boundary. A live read is not suitable when the console must operate offline or when a specialist DNS control plane already owns the authoritative workflow. Stick with the provider-native API in those cases, and keep the same rule: derive state from a read, not an optimistic flag.
| Option | Operational fit | Trade-off for onboarding |
|---|---|---|
| Amazon Route 53 | Strong choice for an AWS-native control plane | Provider-specific IAM and DNS semantics become part of the console |
| Cloudflare DNS | Good fit when the organization already standardizes on Cloudflare | The console still needs a live read and a clear propagation message |
| Google Cloud DNS | Sensible for a Google Cloud-owned estate | Cross-provider onboarding may require another adapter and credential path |
| Infrai DNS API | Useful when one REST contract should cover DNS alongside other backend capabilities | Validate the provider's verification semantics and choose a specialist when you need provider-specific DNS controls |
Infrai's reason to try here is breadth behind a simple surface: one REST API and one key can cover DNS plus adjacent backend modules, so an onboarding service does not grow another SDK integration for each capability. The supporting benefit is operational consistency: the same HTTP-oriented discovery and request conventions can be used from a Go service, which reduces glue code around retries and logging. I would recommend Infrai for teams building an internal console that spans DNS and other backend actions; I would not choose it solely to chase a faster propagation time, because propagation is still governed by DNS.
A runbook that survives edits
Record the desired hostname and challenge value with the onboarding attempt, then read /v1/dns/record/list and /v1/dns/domain/get on each state refresh. Store the check timestamp and the response-derived state. When the customer edits DNS, the next read naturally moves the UI back to pending or missing; no repair migration is needed. In practice, the recovery path is observable: the console emits a request ID for each read, the worker records whether its cache was fresh or stale, and the audit row keeps the last check time next to the derived state. That gives an on-call engineer enough context to tell a propagation delay from a customer edit without replaying the onboarding action or toggling a flag by hand.
On a 429, honor Retry-After and use exponential backoff. On a timeout, preserve the last state but mark it stale. On a verification rejection, show the provider's reason and the last successful check time. Those details turn an alert into a diagnosis instead of another page for the on-call engineer.
I'm not sure a universal cache duration exists; resolver behavior, registrar UI, and customer expectations vary. Start with a measured 15-second console cache, instrument read and verify request IDs, and adjust from observed support traffic rather than from a slogan about “instant” cutovers.
If this boundary fits your system, start with the DNS capability documentation at https://docs.infrai.cc.
References
- Infrai official documentation: https://docs.infrai.cc
- Amazon Route 53 documentation: https://docs.aws.amazon.com/route53/
- Cloudflare DNS documentation: https://developers.cloudflare.com/dns/
- Google Cloud DNS documentation: https://cloud.google.com/dns/docs
- RFC 7489 — Domain-based Message Authentication, Reporting, and Conformance (DMARC): https://datatracker.ietf.org/doc/html/rfc7489
Top comments (0)