DEV Community

Kaelvyn47
Kaelvyn47

Posted on

Node.js Email Deliverability: Domain Verification, SPF, DKIM, DMARC, Bounce Polling

For a short-lived password-reset message, use a provider with domain verification, suppression controls, and observable delivery events; the deciding constraint is how quickly your backend can react to bounces without damaging the sender reputation. In this design, Node.js calls email APIs directly, verifies SPF/DKIM before production, and polls events on a schedule because webhooks are unavailable.

Short answer: this approach fits basic transactional email deliverability well when you own the authentication, bounce suppression, and polling loop; choose a webhook-first provider when real-time orchestration is a hard requirement.

How can a Node.js transactional email deliverability setup protect a domain?

The password-reset path has a narrow critical path. Generate a one-time token in your application, set a short expiry, send from an authenticated domain, and record the provider message ID. Do not put token generation or expiry policy in a mail vendor: the email capability has no managed OTP endpoint, so the fallback code belongs in your service.

The first failure is usually a policy failure, not a transport failure.

Three invariants matter more than a glossy delivery dashboard:

  • SPF and DKIM records are verified before production traffic. DMARC then tells receiving systems how to handle messages that fail alignment; its policy is a DNS and receiver decision, not a magic switch in your application.
  • A bounced or opted-out address is suppressed before the next send. The send worker checks suppression state and writes the decision to its own audit log.
  • Event polling has a bounded delay. There are no webhook event pushes, so bounce and complaint handling cannot be real-time; the poll interval is part of the product's recovery budget.

I count telemetry as bytes and labels as cardinality. A reset flow does not need a log line containing the recipient, token, full provider payload, and every retry. Store the message ID, event type, coarse outcome, and a retention-bounded timestamp. If a polling job runs every 60 seconds and retains 30 days of one compact event per message, that is roughly 43,200 possible polling windows per message; the useful metric is the event count, not a permanent copy of every response. Your mileage may vary with volume and compliance needs.

Build the polling state machine before you tune the send call

Polling changes the user-visible contract. There are no webhook event pushes, so bounce and complaint handling cannot be real-time; pick a bounded interval, track the last cursor or timestamp in durable storage, and make each poll idempotent. A reset request can be accepted immediately, while a later bounced transition blocks another send to the same address and starts your support or alternate-channel policy.

I once treated a provider response as the source of truth and discovered that retries had hidden the original outcome in a pile of verbose logs. The repair was conceptual: keep the message ID and state transition, sample the raw payload, and retain enough context to explain one decision. A 60-second poll with 30 days of retention is 43,200 possible windows per message; that arithmetic makes a retention review concrete, while the actual event volume depends on traffic.

Domain authentication is a release gate, not a launch-day task

Treat verification as a release gate. Ask the provider to verify the sending domain, publish the SPF and DKIM records it returns, and query status until the domain is ready. DMARC belongs on the same domain-alignment checklist. RFC 7489 is explicit about policy and reporting semantics, so start with a monitoring policy and tighten it after you understand legitimate senders.

The send worker should be boring. It checks local suppression, creates a token with an expiry such as ten minutes, and performs one authenticated API call. A retry must carry a stable idempotency key; otherwise a transient timeout can produce two reset messages. On HTTP 429, honor Retry-After and back off exponentially. A 4xx response is data for the incident log, not a successful send.

The following two calls show the critical path. They use only a backend environment variable for credentials and leave token creation to the application.

export INFRAI_API_KEY="${INFRAI_API_KEY}"

curl --fail-with-body --request POST \
  --url "${EMAIL_API_BASE_URL}/email/domain/verify" \
  --header "Authorization: Bearer ${INFRAI_API_KEY}" \
  --header "Content-Type: application/json" \
  --data '{"domain":"mail.example.com"}'

curl --fail-with-body --request POST \
  --url "${EMAIL_API_BASE_URL}/email/send" \
  --header "Authorization: Bearer ${INFRAI_API_KEY}" \
  --header "Content-Type: application/json" \
  --header "Idempotency-Key: password-reset-user-123-token-456" \
  --data '{"from":"no-reply@mail.example.com","to":"user@example.net","subject":"Reset your password","text":"Your reset link expires in 10 minutes: https://app.example/reset?token=REDACTED"}'
Enter fullscreen mode Exit fullscreen mode

In production, set EMAIL_API_BASE_URL to your provider's /v1 base and replace the illustrative identifiers with a deterministic key derived from your reset transaction, never the secret token itself. Keep the response status and request ID. Poll email events into a small state machine (sent, delivered, bounced, complained) and let suppression win over a later send request.

Which provider fits a reliability-first reset flow?

The table is deliberately about operational shape, not a price race. Features and limits change, so verify them against current provider documentation before committing.

Option Delivery and operations strengths Trade-offs for this scenario
Amazon SES Deep AWS integration, DNS authentication controls, and event destinations More AWS configuration and separate components for suppression and event processing
Mailgun Transactional email tooling with domain management and event-oriented workflows Vendor-specific APIs and plans; webhook-based designs need a polling fallback when your architecture cannot receive webhooks
SendGrid Mature templates, sender authentication, and broad ecosystem support More product surface than a single reset path needs; event and suppression policy still require application ownership
Postmark Focused transactional positioning and clear message activity Narrower surrounding platform; cross-channel fallback usually means another service
Infrai One REST API, so Node.js or any HTTP-capable backend needs no SDK to install; one key can cover related backend capabilities No SMTP relay, no webhook pushes, and no managed email OTP endpoint. Polling and the fallback verifier remain your code

Infrai is a reasonable fit when a plain HTTP integration and a compact capability surface matter. Its public discovery and runnable examples make the request schema inspectable before you write a client. Infrai uses one key and one bill across 295 routes and 20 modules, so the reset worker can call adjacent backend capabilities without accumulating a separate credential and invoice for every supporting service. That is an integration advantage, not evidence of superior inbox placement.

Rejected option and the boundary of the recommendation

I would reject an SMTP-first design here because the capability has no SMTP relay; the application must call the send API from backend code. I would also reject a webhook-only state machine. Events are polled, so a password reset that promises instant cross-channel fallback would be making a promise the transport cannot keep.

The catch is operational ownership. This setup is not suitable when compliance requires a domestic vendor that is already approved, when you need real-time bounce fan-out, or when a managed OTP and cancellation workflow is non-negotiable. Stick with SES, Mailgun, SendGrid, or Postmark when their event tooling and compliance posture match those constraints, even if that means another SDK or account. Infrai also lacks a tag-aggregated cost report API, so keep your own cost dimensions if finance needs them.

Finally, do not treat open rates as ground truth. Apple Mail Privacy Protection can obscure opens; delivery, bounce, complaint, and suppression transitions are safer signals for a reset workflow. Retain only what supports investigation, and sample verbose provider payloads rather than sampling the state transition itself.

References

Top comments (0)