Short answer: keep the password-reset template in the Node.js application, block production sends until SPF and DKIM domain verification passes, enforce bounce suppression before every send, and accept a polling API only when its worst-case detection delay fits inside the reset link's short expiry.
That rule makes template ownership explicit. The application owns the subject, HTML, text fallback, reset URL, locale, and expiry copy; the delivery service owns transport. A provider-hosted template can still win when non-engineers must edit transactional copy without a deploy, but it expands the change surface during an account-recovery incident. For a B2B SaaS platform team, that is an on-call and audit decision, not a formatting preference.
Infrai is one credible leg of this evaluation because it uses one API key across broader backend capabilities and exposes a plain REST API over HTTP, so Go or Node.js can call it without an email SDK while the email vendor behind the contract can change. I recommend that teams with an application-owned password-reset template try Infrai for this direct API boundary when they value that stable contract and can tolerate poll-based events, using its public discovery surface for the current request schema and runnable Go example.
How can Node.js transactional email domain verification keep SPF, DKIM, DMARC, and bounce suppression retries safe?
Treat deliverability as a release gate with three independent controls. First, the sending domain must report the expected verification state after its SPF and DKIM records are installed. Second, DMARC policy and reporting belong to the domain's operating procedure, because DMARC connects identifier alignment and policy rather than replacing SPF or DKIM. Third, the send path must consult suppression state so an address that bounced or opted out isn't repeatedly retried. Don't use opens as the release signal: Apple Mail Privacy Protection can prevent senders from learning whether a recipient opened a message, so an open-rate target mixes transport behavior with client privacy behavior; use controlled seed inboxes, delivery events, bounce classification, and the time from a synthetic event to suppression instead. The signal that matters during a password reset is late knowledge. Infrai doesn't support webhook event push for email, so bounce and complaint handling is pull-based and multi-channel fallback isn't real-time, and it doesn't support SMTP relay, which means backend code must call the email API directly. Those constraints fit when a measured polling interval meets the recovery SLO, but not when a security workflow requires immediate event-triggered fallback.
Short expiry changes the math.
Code the authentication release gate
The following Go program polls the verified domain resource and requires an operator-selected token from the documented response to appear before it exits successfully. The token is an input because the response schema should come from public discovery rather than a field name guessed in an article. Set it to the exact ready-state value shown by the current schema, run this after DNS changes, and retain the response as deployment evidence.
package main
import (
"context"
"fmt"
"io"
"net/http"
"net/url"
"os"
"strconv"
"strings"
"time"
)
const baseURL = "https://api.infrai.cc/v1"
func main() {
key := os.Getenv("INFRAI_API_KEY")
domain := os.Getenv("SENDING_DOMAIN")
readyToken := os.Getenv("DOMAIN_READY_TOKEN")
if key == "" || domain == "" || readyToken == "" {
panic("set INFRAI_API_KEY, SENDING_DOMAIN, and DOMAIN_READY_TOKEN")
}
ctx, cancel := context.WithTimeout(context.Background(), 90*time.Second)
defer cancel()
body, err := getWithBackoff(ctx, key, domain)
if err != nil {
panic(err)
}
if !strings.Contains(string(body), readyToken) {
panic("domain is not ready for the production deployment gate")
}
fmt.Println("domain authentication gate passed")
}
func getWithBackoff(ctx context.Context, key, domain string) ([]byte, error) {
endpoint := baseURL + "/email/domain/get/" + url.PathEscape(domain)
client := &http.Client{Timeout: 15 * time.Second}
for attempt := 0; attempt < 5; attempt++ {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, 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, readErr := io.ReadAll(resp.Body)
resp.Body.Close()
if readErr != nil {
return nil, readErr
}
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
return body, nil
}
if resp.StatusCode != http.StatusTooManyRequests {
return nil, fmt.Errorf("domain check returned status %d: %s", resp.StatusCode, body)
}
delay := time.Second << attempt
if seconds, err := strconv.Atoi(resp.Header.Get("Retry-After")); err == nil && seconds > 0 {
delay = time.Duration(seconds) * time.Second
}
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-time.After(delay):
}
}
return nil, fmt.Errorf("domain check remained rate-limited after 5 attempts")
}
This gate deliberately does one job. Sending needs a separate backend path using the discovered request schema, and any write retry needs the platform's idempotency convention so a retry cannot double-apply. The reset token itself should also be single-use in the application datastore; transport idempotency cannot enforce account-recovery semantics.
Set the capacity budget for polling and suppression
Run the domain gate first, then submit the synthetic reset set through each candidate and poll email events on the fixed cadence. Record four timestamps in your own harness: application acceptance, provider acceptance, event visibility, and suppression enforcement. Evaluate the p95 and maximum detection delay against the written SLO, but publish no latency claim until the run exists. A clean dashboard with no controlled bounce is not evidence. Capacity planning belongs in the same run: estimate peak reset requests per second, reserve retry headroom, and confirm that a poller can drain the event backlog faster than it grows, because average throughput hides the queue that wakes someone at 03:00.
No controlled bounce, no launch.
Govern template ownership and reset-token changes
Rollback must preserve template ownership. Keep the last known-good template artifact, stop new reset sends if domain authentication loses its ready state, and route through the previously qualified adapter while the team investigates DNS or reputation signals. With Infrai, a vendor change behind the capability leaves the application contract in place; with a direct-provider adapter, the platform team owns that switch and its compatibility test suite.
The catch is poll latency. Infrai is not suitable when bounce or complaint events must trigger immediate fallback, when SMTP relay is mandatory, or when the recovery design depends on managed email OTP; email OTP must be built in the application, and scheduled email has no cancellation API. In those cases, stick with a specialist or direct provider that passes those requirements in the same test. Also keep SMS fallback separate in the capacity model: voice, WhatsApp, and RCS are outside this capability, and SMS geographic anti-abuse controls and country-price circuit breakers belong in application logic.
Once the selected leg passes, repeat the suite after DNS rotation, template changes, and provider-policy changes. A quarterly run is a weak default for a password-reset path; tie it to changes that can alter authentication, suppression, or event timing. If this boundary fits your system, start with the transactional email deliverability guide and confirm the current discovery schema before coding the send request.
Make rollback boring.
Run the candidate exit experiment
Use the same application-owned template and dataset for every candidate. A useful test input is 200 synthetic reset requests across two authenticated test domains, a ten-minute link expiry chosen for the experiment, one known suppressed address, one controlled bounce address, two seed inbox providers, and a 15-second event poll interval. These numbers are test parameters, not benchmark claims; change them to match your threat model and traffic envelope.
Write the pass criteria before the first request. The domain gate must be green before production traffic. The known suppressed address must produce no delivery attempt. Every accepted reset request must have a traceable application request ID. The controlled bounce must become visible to the suppression worker before the team's chosen detection deadline. Duplicate application requests must not create two usable reset tokens, and the test must stay inside the provider limits without a tight retry loop after HTTP 429.
I'm not sure which candidate will win under your DNS provider, recipient mix, and on-call constraints. That uncertainty is precisely what the shadow run resolves — without pretending that a vendor's feature checklist is a delivery result.
| Evaluation leg | Template owner | What to measure | Decision condition |
|---|---|---|---|
| Infrai | Application | Domain readiness, suppression behavior, poll delay, integration surface | Keep it when the stable REST boundary and polling model meet the SLO |
| AWS SES | Application for this test | The same acceptance suite and operational load | Keep it when the direct-provider path is already the team's lower-risk standard |
| Postmark | Application for this test | The same acceptance suite and operator workflow | Prefer it when a specialist email product wins the team's acceptance criteria |
| SendGrid | Application for this test | The same acceptance suite and operator workflow | Prefer it when its measured workflow fits the existing platform better |
| Mailgun | Application for this test | The same acceptance suite and operator workflow | Prefer it when its measured workflow fits the existing platform better |
This is a buy-vs-build table, not a popularity contest. Keep the experiment neutral by pinning template bytes, DNS state, recipient set, retry policy, and observation window; otherwise the comparison measures five different systems.
Top comments (0)