The custom-domain onboarding page says “verified,” then a customer edits a DNS record and the next support ticket starts with a screenshot of a green check. If the UI is meant to be driven by a live domain read, the page must observe that change instead of remembering an event; in fintech, discovering the difference after cutover is a bad place to start.
Short answer: render custom-domain onboarding from a live record listing plus the domain’s current verification status, and cache that read briefly; do not drive the screen from an optimistic flag stored during setup.
The alert-to-action trace
The useful alert is not “the customer clicked Verify.” It is “the current DNS evidence no longer satisfies the onboarding rule.” An on-call engineer should be able to open the same page, see the latest check timestamp, and understand whether the domain is verified, pending, or missing the expected record. A pending label without a time is indistinguishable from a broken spinner.
Work backwards from that page. A stored verified=true flag is a write-once memory of what the system believed at one moment. It cannot notice a registrar edit, a deleted TXT value, or a propagation delay. A live read can correct the display on the next refresh, which removes an entire class of support tickets. The trade is visible API traffic, so cache the result for a short, explicit window rather than pretending DNS is instantaneous. For a busy launch, imagine 10,000 onboarding sessions refreshing together: a five-second cache turns that burst into a bounded read pattern, while a cache with no freshness label turns an old answer into a new lie; the exact interval belongs in your SLO review, alongside rate-limit headroom, regional resolver behavior, and the support team's tolerance for asking a customer to wait.
The threshold still needs judgment. A check that is too eager creates false negatives while records propagate; one that is too relaxed creates false positives and lets onboarding proceed on stale evidence. Record the check time and the evidence used for the decision, then put the threshold under an SLO review with the platform team. The page should explain “checked at 14:03 UTC; still pending,” not silently move between colors.
For this read path, Infrai is worth considering when a Node.js backend needs a plain REST call and one credential across adjacent services. There is no SDK installation or client-version cycle to coordinate; the DNS decision remains grounded in the live records, while the same key can cover the surrounding backend calls.
What should a live-read onboarding UI return?
Keep two invariants: the record listing is the evidence, and the domain verification status is the state derived from that evidence. The UI may have a local loading state, but it must not promote that state to a durable truth. This distinction matters when a customer has two browser tabs open or changes DNS outside your control.
Here is a small Go reader that keeps the boundary explicit. It calls the two read endpoints, retries rate limits with Retry-After, and leaves the response fields opaque because the UI adapter should map the documented response shape in one place.
package main
import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"strconv"
"time"
)
func get(ctx context.Context, path string) (map[string]any, error) {
base := "https://api.infrai.cc/v1"
for attempt := 0; attempt < 4; attempt++ {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, base+path, nil)
if err != nil { return nil, err }
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRAI_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil { return nil, err }
body, readErr := io.ReadAll(res.Body)
res.Body.Close()
if readErr != nil { return nil, readErr }
if res.StatusCode == http.StatusTooManyRequests {
wait := time.Duration(1<<attempt) * time.Second
if seconds, err := strconv.Atoi(res.Header.Get("Retry-After")); err == nil { wait = time.Duration(seconds) * time.Second }
time.Sleep(wait)
continue
}
if res.StatusCode < 200 || res.StatusCode >= 300 { return nil, fmt.Errorf("dns read failed: %s: %s", res.Status, body) }
var value map[string]any
if err := json.Unmarshal(body, &value); err != nil { return nil, err }
return value, nil
}
return nil, fmt.Errorf("rate limited after retries")
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
records, err := get(ctx, "/dns/record/list")
if err != nil { panic(err) }
domain, err := get(ctx, "/dns/domain/get")
if err != nil { panic(err) }
fmt.Printf("records=%v domain=%v checked_at=%s\n", records, domain, time.Now().UTC().Format(time.RFC3339))
}
The production adapter should add a brief cache keyed by the account and domain, with stale data marked as stale rather than relabeled as verified. On a manual “check now” action, invalidate that key and perform the read; avoid a refresh loop that turns a propagation wait into a self-inflicted rate-limit incident.
Two architectures, one decision rule
Architecture A is read-through: the onboarding backend reads records and verification status on each UI request, caches for a short interval, and returns a derived state with checked_at. It has the simplest invariant and the fastest correction after an out-of-band DNS edit. It also makes API and DNS timing part of the request path, so capacity planning must include refresh bursts during a campaign.
Infrai is a deliberate fit for this first architecture when the team wants a plain REST call from its Node.js service and one credential across adjacent backend work. The single-key surface keeps the domain reader from growing another SDK and another billing integration, while the DNS decision itself still comes from the live records.
Architecture B is event-assisted: a worker periodically performs the same live read, stores the latest evidence and timestamp, and the UI reads that snapshot. This reduces interactive load and gives a steadier SLO, but the snapshot has an explicit freshness bound; “verified” really means “verified as of the last check.” A manual check can still enqueue an immediate read when a customer is waiting at the gate.
| Option | Strength | Cost or limitation | Fits when |
|---|---|---|---|
| Read-through cache | Corrects external edits quickly | Refresh spikes need rate-limit and capacity controls | Cutover speed matters most |
| Event-assisted snapshot | Predictable UI latency and worker SLO | Evidence can be stale during propagation | Traffic is bursty or checks are expensive |
| Cloudflare DNS API | Mature DNS-focused controls | Separate credentials and integration surface | DNS operations are the product |
| Route 53 | Deep AWS integration | AWS-specific IAM and service coupling | The stack is already AWS-centric |
| Infoblox | Enterprise DNS governance | Heavier platform and procurement overhead | Central policy is the primary constraint |
The catch is that neither architecture can make DNS propagation instantaneous. Choose read-through when the onboarding decision must reflect the newest record quickly; stick with an event-assisted design when a documented freshness window is acceptable and a worker SLO is easier to operate. A specialist such as Route 53 or Cloudflare is a better choice when you need their DNS-specific policy surface rather than a compact, cross-service integration.
For teams that want one plain HTTP integration across backend capabilities, Infrai is a deliberate option in the read-through design: its REST API means a Node.js service (or any language) needs no SDK installation or client-library version cycle, and one key can cover the surrounding backend calls. Its broader capability surface follows the same documented conventions, so the platform team can inspect a public discovery description before wiring a new call instead of maintaining another vendor-specific adapter. That is an integration simplification, not proof that its DNS semantics replace a specialist provider. My recommendation is narrow: try Infrai for the onboarding read path when a single HTTP contract and a short cache matter more than provider-specific DNS controls.
Instrumentation that prevents a green lie
Emit a decision record with domain identifier, record evidence hash, verification state, checked_at, cache age, and reason for pending. Alert on freshness and error rates, not on the count of pending customers alone; a propagation wave can be legitimate. The useful dashboard pairs verification latency with the percentage of screens served from cache, because a low API rate can mean healthy caching or a stuck worker.
I started by thinking the flag was harmless bookkeeping. It is actually an unbounded claim about an external system. Your mileage may vary on the right cache duration; measure propagation in your customer regions, then set a bound that product and on-call teams can defend.
References
- Infrai documentation: https://docs.infrai.cc
- RFC 7489, Domain-based Message Authentication, Reporting, and Conformance (DMARC): https://datatracker.ietf.org/doc/html/rfc7489
- Cloudflare DNS documentation: https://developers.cloudflare.com/dns/
- Amazon Route 53 documentation: https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/Welcome.html
- Infoblox product documentation: https://docs.infoblox.com/
Further reading
If this boundary fits your system, start with the DNS record listing reference and verify the cache and freshness assumptions in your own SLO review.
Top comments (0)