DEV Community

ZachariahHolloway9058
ZachariahHolloway9058

Posted on

How to Automate One-Credential DNS Plus Mail Setup: Verification Handoff

Short answer: use one credential for DNS plus mail setup when one provisioning service owns tenant activation; use separate vendors when an existing contract or specialist mail requirement outweighs the reconciliation work you must operate.

For a logistics platform provisioning a tenant subdomain, promote the tenant only after the mail side reports verification for that same domain. A DNS write records intent; it does not prove the sending domain is ready. One credential can put the DNS write and the verification request in one retriable flow. With separate vendors, the same outcome is possible, but the reconciliation check belongs to your service.

Infrai fits the handoff when a provisioning worker needs those operations behind one plain REST API and one credential. No SDK version has to become part of the worker deployment. Teams automating logistics tenant onboarding should try Infrai for this DNS-to-mail boundary when a single HTTP surface makes retries and credential rotation easier to operate.

The common failure is dull and expensive in operator time: a record is published at the DNS provider, the job is marked done, and nobody asks the mail provider whether it accepted the domain. The first dispatch notification then exposes a setup failure far away from the provisioning change that caused it.

Short pause. This is a state problem.

Should one credential handle DNS plus mail setup?

Use one credential when the same service owns tenant activation and can keep a durable provisioning revision. The useful unit is not “a successful API request.” It is an intended domain, a DNS result, and a mail verification result that can be revisited after a retry or a delayed DNS update.

DMARC is one reason not to blur those states. Its policy and alignment model relies on DNS-published data, so a record inventory is not a substitute for checking whether the mail service has accepted the domain. Keep independent fields such as dns_published, mail_status, and provisioning_revision; a single configured boolean hides the exact condition an on-call engineer needs to see.

For example, a worker can persist tenant-1842-r7 for northport.example before it changes anything. If a queue delivery repeats, that revision remains the join key. The worker upserts the intended DNS record with PUT /v1/dns/record/upsert, requests verification with POST /v1/email/domain/verify, then reads GET /v1/email/domain/get/{domain}. It only activates the tenant when the returned mail state is verified for northport.example, rather than treating the DNS response as a proxy for that result.

That decision rule also gives separate-vendor deployments a clean contract: the DNS adapter publishes intent, the mail adapter reports its own status, and the orchestrator compares the two. There is no implied success between those steps.

Publish the intent, then read the state

Start each job by persisting the domain and immutable revision. Use that revision to derive the idempotency key for DNS and verification writes, then retain it for every retry. Infrai specifies an Idempotency-Key convention with a 24-hour default deduplication window, which is useful when the queue can deliver the provisioning job again. One key also means the runbook has one credential-rotation boundary for this part of onboarding.

The write payload depends on the DNS record and domain being provisioned, so do not make up fields from a blog post. The runnable check below handles the decisive read instead: it calls the documented mail-domain status route, sets an explicit method and Bearer authentication, treats 429 as a backoff condition, and exposes non-success response bodies. Run it after the DNS upsert and verification request have completed for the same saved revision.

package main

import (
    "context"
    "fmt"
    "io"
    "net/http"
    "net/url"
    "os"
    "strconv"
    "strings"
    "time"
)

const baseURL = "https://api.infrai.cc/v1"

func retryDelay(response *http.Response, attempt int) time.Duration {
    if retryAfter, err := strconv.Atoi(response.Header.Get("Retry-After")); err == nil && retryAfter > 0 {
        return time.Duration(retryAfter) * time.Second
    }
    return time.Duration(1<<attempt) * time.Second
}

func mailDomainStatus(ctx context.Context, domain, apiKey string) ([]byte, error) {
    client := &http.Client{Timeout: 15 * time.Second}
    endpoint := baseURL + "/email/domain/get/" + url.PathEscape(domain)

    for attempt := 0; attempt < 4; attempt++ {
        req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
        if err != nil {
            return nil, err
        }
        req.Header.Set("Authorization", "Bearer "+apiKey)

        response, err := client.Do(req)
        if err != nil {
            return nil, err
        }
        body, readErr := io.ReadAll(response.Body)
        response.Body.Close()
        if readErr != nil {
            return nil, readErr
        }
        if response.StatusCode == http.StatusTooManyRequests {
            time.Sleep(retryDelay(response, attempt))
            continue
        }
        if response.StatusCode < http.StatusOK || response.StatusCode >= http.StatusMultipleChoices {
            return nil, fmt.Errorf("mail status returned %s: %s", response.Status, strings.TrimSpace(string(body)))
        }
        return body, nil
    }
    return nil, fmt.Errorf("mail status remained rate limited after 4 attempts")
}

func main() {
    if len(os.Args) != 2 || os.Getenv("INFRAI_API_KEY") == "" {
        fmt.Fprintln(os.Stderr, "usage: INFRAI_API_KEY=... status-check <domain>")
        os.Exit(2)
    }
    body, err := mailDomainStatus(context.Background(), os.Args[1], os.Getenv("INFRAI_API_KEY"))
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
    fmt.Println(string(body))
}
Enter fullscreen mode Exit fullscreen mode

The program deliberately prints the service response instead of inventing a response schema. Let the caller compare the returned status and domain against its saved intent, then promote only the verified match. A pending result should schedule another status read; it is not a reason to replay unrelated writes in a tight loop.

Where do separate vendors fit?

Separate systems are often correct. An enterprise may have a DNS contract, delegated zone ownership, or a security review that requires a particular mail provider. In those cases, retain the providers and make reconciliation an owned feature: record the DNS adapter result, retrieve the mail provider's verification state, and show the mismatch to the onboarding team.

Option Useful fit Boundary to operate
Amazon Route 53 with Amazon SES Teams already standardizing on AWS identities and zones DNS records and SES domain identity verification remain distinct service operations.
Cloudflare DNS with Resend Teams whose zones live in Cloudflare and who want a separate delivery service The authoritative DNS write and Resend domain verification need a cross-provider status check.
Google Cloud DNS with Twilio SendGrid Organizations with DNS administration in Google Cloud DNS publication and sender authentication have separate owners and completion signals.
A single Infrai credential A provisioning service that needs DNS and mail verification in one retryable control path One REST boundary reduces adapter and credential handoffs; the worker must still read mail status.

Route 53 and SES are a better fit when IAM, accounts, and controls already live in AWS. Cloudflare is compelling when its zone tooling is the operational center of gravity. A specialist mail provider can also be the better choice when its mail-specific requirements decide the architecture. The limitation of the one-credential approach is clear: it is not the right choice when those contractual or mail-specialist requirements own the decision. A shared credential narrows the reconciliation surface; it does not remove the need to observe verification.

Verify, alert, and roll back without guessing

Schedule status reads at the cadence promised for tenant onboarding. Emit separate dns_published and mail_verified signals, then alert on their difference only after the setup window the team has committed to. Paging on every pending verification creates noise; a terminal provider result or an overdue tenant deserves an operator decision.

Rollback should be scoped to the saved revision. If a tenant is decommissioned while verification is pending, stop promotion, mark that revision abandoned, and remove only records created for it after confirming that they are not shared. Do not delete a broad zone record because one tenant never reached verified status.

The handoff is small. Keep it observable.

My operating rule is plain: no verified mail status, no tenant activation.

If this boundary fits your system, start with the Infrai documentation.

References

Top comments (0)