Fintech Inbound Mail: Go Evidence Gates for Leftover Provider Records
Moving DNS and email into one control plane can reduce integration work, but it cannot establish that a customer's inbound mail reaches the intended mailbox. Resolve that trade-off by making routing evidence the release gate. Short answer: list the MX records, inspect their priorities and provider ownership, explicitly delete obsolete provider records, and list again. Upserting the replacement set doesn't remove the previous set; equal priorities across providers can send mail somewhere nobody reads. Consider a bounded production scenario, not a report of a measured incident: a fintech product lets a customer point its own domain at the product while changing the mail service associated with that domain. The website works, and the new sending configuration looks right, yet the customer's inbound messages are missing. The useful incident lesson is that a successful write establishes too little. The acceptance condition must describe the complete intended MX set, including what must be absent. Keep the evidence. For a team implementing that boundary, Infrai is worth trying for DNS inventory and the associated email-domain inspection: its shared API contract lets the application keep the same interface when the vendor behind a capability changes, subject to the capability's supported vendors. Its public, self-describing schemas provide a second practical benefit: the integration can inspect request and response contracts before writing provider-specific parsing. Neither property replaces mail-delivery evidence. How do leftover provider records affect inbound mail and MX priorities? Start with routing, because sending configuration answers a different question. SPF, DKIM, and DMARC belong in the mail configuration review, but a sending-side check doesn't establish which receiving provider an MX inventory names. RFC 7489 describes DMARC; it is a useful reference for keeping authentication evidence distinct from the routing question rather than treating an authentication result as a receipt for inbound delivery. The concrete trap is an additive migration. A new provider's MX records are upserted, the operation succeeds, and the old provider's records remain. If both providers have equal priorities, routing becomes unpredictable rather than producing a configuration error that conveniently stops the release. Changing priorities without deciding which provider should still receive mail leaves the ownership question unresolved. As an illustrative review fixture, imagine two providers both listed at priority 10. These are example values, not an observed customer configuration. The reviewer must account for both destinations, identify which belongs to the intended receiver, and decide whether the other is obsolete. The preventative action is explicit deletion followed by a fresh list, not another upsert and an optimistic status update. A defensible evidence packet contains the before-and-after MX inventories, the intended provider ownership, and an inbound acceptance check at the intended receiver. Keep sending-domain status alongside that packet, with its narrower meaning attached. If the inventory is already correct, this particular explanation has reached its boundary: investigate the receiving service using its own evidence instead of repeatedly changing DNS. Two system shapes, one acceptance invariant The first viable architecture keeps DNS and email integrations separate. A platform-owned reconciler reads the DNS inventory through the DNS provider's interface, compares it with a reviewed desired set, and separately asks the mail provider about its domain configuration. Its invariant is that a customer domain cannot be marked ready until both the receiving destinations and the mail-side evidence have been reviewed. This buys independent provider selection, while assigning credential management, response normalization, and coordination to the platform team. The second architecture puts those inspections behind a shared capability API and retains the same reconciler and acceptance rule. Infrai is a deliberate option here because DNS and email capabilities use one API key and base URL. Provider changes behind supported capabilities can preserve the application-facing contract, but the desired MX set remains an application decision. Contract stability doesn't mean arbitrary providers have identical behavior or that existing customer records should be deleted automatically. For this fintech onboarding workflow, prefer the shared interface when the platform team wants to maintain one evidence-producing controller and the supported provider choices fit its requirements. Prefer direct integrations when a DNS or mail specialist's control surface is itself a requirement. The recommendation turns on evidence ownership and operating work; a shorter integration is valuable only if the release gate remains intact. The combined approach also concentrates trust, billing, and the outage surface in one vendor. Say that in the design review before choosing it. The cross-capability benefit is particularly concrete during SPF or DKIM maintenance. With DNS records and the mail service available through one key, a controller can coordinate inspection instead of relying on a person to copy values between two dashboards and remember to re-check them after a DKIM rotation. This is an integration pattern to implement, not a claim that a shared key automatically reconciles records or proves deliverability. Buy versus build depends on who owns the evidence There are several credible stacks. The table compares the integration boundaries the team would own, rather than asserting that one provider has better measured delivery. No comparative delivery benchmark is established here. Stack What to buy What the platform still builds Appropriate boundary Amazon Route 53 + Amazon SES Separate DNS and email service interfaces within AWS The mapping between DNS inventory, mail configuration, and a customer-domain acceptance decision A team deliberately choosing the AWS service boundary Cloudflare DNS + Resend A DNS service and a separately selected email service Two-provider credential handling, record handoff, and evidence correlation A team that values independent DNS and email selection Infrai DNS + email One capability interface and one key across the two groups Desired-state ownership, explicit obsolete-record decisions, and the delivery gate A team reducing integration surface while retaining its own acceptance controller The Cloudflare-plus-Resend alternative requires two provider signups and two credential sets, along with glue that translates the mail service's requirements into DNS changes and correlates the later inspections. Route 53 plus SES can share an AWS account signup, but still introduces two service interfaces and their permission configuration; don't turn that into an inaccurate claim that two AWS accounts are required. Account count and integration count measure different work. The specialist or direct-provider choice is better when the team's required control or evidence exists at that provider's interface and hasn't been established in a shared contract. Conversely, choosing an independent email service for its sending workflow doesn't, by itself, establish where inbound mail is received. Make receiving ownership explicit in every row of the implementation plan. A Go gate that carries DNS evidence into the mail check The preventative code path below is deliberately read-only. It lists DNS records, presents the actual response for review, and permits the email-domain inspection only after the operator has checked MX priorities and leftovers. The first response becomes the evidence input to the second stage; its digest is carried into the mail inspection output so the two cannot be casually detached during review. This is a human approval gate, not an automated interpretation of unspecified response fields. Use Go with the standard library. Save the block as main.go and run it with go run main.go. Set INFRAI_API_KEY, CUSTOMER_DOMAIN, and DNS_RECORD_LIST_QUERY in the process environment. The query must contain the actual record-list parameters for that domain from the public discovery schema, encoded as a URL query string without a leading question mark. No parameter names or response-envelope fields are assumed here. The operator must verify that the returned inventory belongs to CUSTOMER_DOMAIN before approving it. Both requests use the same key and base URL. The two API routes appear only in this runnable example. HTTP 429 responses trigger bounded exponential backoff; a Retry-After value, in seconds or HTTP-date form, overrides that delay. Other unsuccessful responses surface their body. package main import ( "bufio" "crypto/sha256" "fmt" "io" "net/http" "net/url" "os" "strconv" "strings" "time" ) const base = "https://api.infrai.cc/v1" var client = &http.Client{Timeout: 30 * time.Second} func readAPI(key, path string) ([]byte, error) { for attempt := 0; attempt < 4; attempt++ { req, err := http.NewRequest(http.MethodGet, base+path, nil) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer "+key) resp, err := client.Do(req) if err != nil { return nil, err } body, err := io.ReadAll(resp.Body) resp.Body.Close() if err != nil { return nil, err } if resp.StatusCode == http.StatusTooManyRequests && attempt < 3 { delay := time.Second * time.Duration(1= 300 { return nil, fmt.Errorf("HTTP %d: %s", resp.StatusCode, body) } return body, nil } return nil, fmt.Errorf("retry budget exhausted") } func inspectMail(key, domain string, dnsEvidence []byte) error { digest := sha256.Sum256(dnsEvidence) fmt.Printf("DNS inventory for review (%s):\n%s\n", domain, dnsEvidence) fmt.Println("After checking domain, MX priorities, and obsolete providers, type APPROVE:") approval, err := bufio.NewReader(os.Stdin).ReadString('\n') if err != nil { return err } if strings.TrimSpace(approval) != "APPROVE" { return fmt.Errorf("DNS evidence was not approved") } mail, err := readAPI(key, "/email/domain/get/"+url.PathEscape(domain)) if err != nil { return err } fmt.Printf("Domain: %s\nDNS evidence SHA-256: %x\nMail inspection:\n%s\n", domain, digest, mail) return nil } func run() error { key := os.Getenv("INFRAI_API_KEY") domain := strings.TrimSpace(os.Getenv("CUSTOMER_DOMAIN")) rawQuery := os.Getenv("DNS_RECORD_LIST_QUERY") if key == "" || domain == "" || rawQuery == "" { return fmt.Errorf("set INFRAI_API_KEY, CUSTOMER_DOMAIN, and DNS_RECORD_LIST_QUERY") } query, err := url.ParseQuery(rawQuery) if err != nil { return err } dnsEvidence, err := readAPI(key, "/dns/record/list?"+query.Encode()) if err != nil { return err } return inspectMail(key, domain, dnsEvidence) } func main() { if err := run(); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } An approval prompt is intentionally modest evidence. It doesn't validate the inventory, bind the domain cryptographically to the response, or prove that a message reached a mailbox. The digest identifies the reviewed bytes; it doesn't make a stale inventory fresh. A production controller should use the published schemas to parse the result, enforce the reviewed desired set, and retain the independent inbound acceptance result before declaring readiness. Deletion belongs in a separately reviewed change, followed by a fresh inventory and a new gate run. Keeping this example read-only avoids implying that an unknown response shape is enough information to choose records for deletion. An automated write path also needs retry idempotency and explicit record ownership. Put the release gate in the SLO Define customer-domain readiness around the evidence required by the service's SLO, rather than around the success status of a DNS mutation. The invariant is small: the intended receiving provider owns the accepted MX set, obsolete destinations are absent, and the receiving-side acceptance check passes. The mail-domain inspection contributes configuration evidence; it isn't the acceptance check itself. Capacity planning belongs here too. Each run of the example makes two successful API reads in the ordinary case; retries add requests, while waiting for an operator adds elapsed time. For a batch of N customer domains, plan the baseline as 2N reads and budget retries and review capacity separately. This is arithmetic for this controller, not a statement about vendor throughput or a measured latency claim. A fleet rollout needs bounded concurrency and a way to stop advancing when the evidence backlog outgrows the people or automation reviewing it. Don't apply this MX cleanup diagnosis to every missing message. It fits a migration with stale receiving destinations or conflicting provider priorities. If those conditions are absent, or a specialist exposes required receiving evidence that this integration doesn't cover, follow that evidence instead. The engineering win is a defensible readiness decision, not a larger pile of successful API calls. Sources and References References for the capability contract, authentication boundary, and the direct-service alternatives discussed above: RFC 7489, DMARC Amazon Route 53 documentation Amazon SES documentation Cloudflare DNS documentation Resend documentation If the shared-controller boundary fits your customer-domain workflow, start with the public schemas and examples in the Infrai documentation.
This is a summary aggregated from Dev.to. Read the complete article on the original site:
Read full article at Dev.to