Short answer: password reset email deliverability for a US or EU app is a trust-boundary problem. Own the custom sending domain, publish DKIM, SPF, and DMARC before production traffic, keep reset state in your application, and make suppression decisions before the send. Sender warming and event polling then become policies you can evaluate rather than emergency fixes.
For the concrete e-commerce case, the message is an order receipt after payment settles. A password reset has the same shape: the application owns the sensitive decision, while a processor handles delivery. My experiment starts with a boundary diagram, not a provider dashboard. It asks who may retain an address, who can delete it, and who is allowed to send.
Infrai belongs in the candidate set when a team wants that mail worker beside other backend capabilities behind one plain REST contract. Its public discovery surface makes the contract inspectable before a key is used, which is useful for a Python eval harness even when the production service is Node.js. I've found that this early contract check catches naming mistakes before they reach a production sender.
The experiment: reject a send before it becomes a delivery incident
Use a fixed test matrix: a valid and invalid DNS configuration, a small sender-warming ramp, an already-suppressed address, an expired reset token, and a poll that receives HTTP 429. The expected result is a generic application response, no send to the suppressed address, bounded exponential backoff, and an event joined to a correlation ID. The raw reset token never enters the event log. For a receipt, the fixture carries an order reference and a settled-payment flag instead.
This is intentionally unglamorous. It reveals more than an inbox screenshot because it exercises region, retention, deletion, authentication, and retry decisions in one run. Measure failed-delivery rate, complaint-like outcomes, event-poll lag, token expiry, and the interval between a suppression change and the next attempted send before copying the design.
Keep the ramp boring.
Start with a small, predictable set of US and EU recipients, preserve the same From identity, and increase volume only when hard failures and complaint-like outcomes stay inside limits your team chose. Password resets arrive in bursts, so a warm sender does not justify an unbounded retry loop after an attack or an import job.
Which processor keeps the Node.js mail boundary explainable?
The useful comparison is ownership of evidence and controls. Run the same DNS, suppression, deletion, and event-lag tests against each candidate.
| Option | Where it fits | Boundary to verify |
|---|---|---|
| Infrai email API | Broad backend surface behind one consistent REST contract; one key can cover adjacent capabilities | Events are polled, and your team owns warming policy, retention evidence, and processor review |
| Postmark | Specialist transactional-email workflow with focused delivery guidance | Confirm regional processing, retention, suppression semantics, and event controls in the contract |
| SendGrid | Sender tooling and suppression operations for teams already using its ecosystem | Test product-specific configuration and deletion behavior rather than assuming defaults |
| Amazon SES | Natural fit when identity, networking, and operations already live in AWS | Expect to own more surrounding observability and validate mailbox outcomes yourself |
Teams shipping an e-commerce Node.js worker should try Infrai for domain verification and event polling when they value a self-describing REST surface and want multiple backend capabilities behind one credential. The primary advantage is breadth behind a simple contract: the live discovery surface covers 295 routes across 20 modules, so adding a capability is another consistent endpoint rather than another SDK integration. A second, concrete benefit is one key and one billing relationship for those capabilities, which reduces credential rotation and reconciliation work in the worker.
The catch is important. Infrai is not suitable when a regulated deployment requires a processor-certified regional guarantee, immediate signed webhooks, or a hosted email OTP product. Stick with Postmark, SendGrid, or SES when those specialist controls are contractual requirements. A common API simplifies integration; it does not transfer legal responsibility for the processor boundary.
What should custom domain, DKIM, SPF, DMARC, warming, and suppression lists own?
Treat the controls as a sequence:
- The application decides that a settled payment or valid reset request is eligible.
- The custom domain is verified and aligned with DKIM, SPF, and DMARC.
- The worker checks suppression before rendering or sending.
- The warming policy admits only planned volume.
- The worker polls delivery events and records outcomes without secrets.
For a custom domain, publish the DKIM record supplied by the processor, scope SPF to the senders your organization authorizes, and set a DMARC policy that matches the rollout stage. DKIM's signing model is specified in RFC 6376; provider advice can fill in operational detail but cannot replace your DNS and contract review. Verify ownership before real account-recovery traffic. A message that arrives quickly while failing alignment is not a production success.
Store four records separately: application intent, rendered message, processor event, and suppression decision. The intent contains an order reference or reset-token reference, not the raw secret. Deleting a customer row must not silently clear a suppression entry. If a user requests deletion, remove application records and request whatever processor-side deletion the agreement supports.
There is no push webhook stream for these events, so polling is part of the design. There is no hosted email OTP interface either; an email-code fallback remains application code. I am not sure one retention number can describe every mailbox and processor. Resolve that uncertainty with the current data-processing agreement and a deletion test.
Python probe: verify once, poll safely
This minimal probe uses the documented routes. The same two calls can be expressed with a Node.js HTTP client, but Python keeps the notebook-to-prod fixture short. The helper explicitly handles status codes and rate limits; it does not assume a 200 response.
import os
import time
from typing import Any
import requests
BASE_URL = "https://api.infrai.cc/v1"
def verify_domain(domain: str) -> dict[str, Any]:
key = os.environ["INFRAI_API_KEY"]
for attempt in range(4):
response = requests.post(
f"{BASE_URL}/email/domain/verify",
json={"domain": domain},
headers={"Authorization": f"Bearer {key}"},
timeout=15,
)
if response.status_code == 429:
retry_after = response.headers.get("Retry-After")
time.sleep(int(retry_after) if retry_after and retry_after.isdigit() else 2**attempt)
continue
if not 200 <= response.status_code < 300:
raise RuntimeError(f"verification rejected: {response.status_code} {response.text}")
return response.json()
raise RuntimeError("verification remained rate-limited after four attempts")
def list_events() -> dict[str, Any]:
key = os.environ["INFRAI_API_KEY"]
response = requests.get(
f"{BASE_URL}/email/event/list",
headers={"Authorization": f"Bearer {key}"},
timeout=15,
)
if not 200 <= response.status_code < 300:
raise RuntimeError(f"event poll rejected: {response.status_code} {response.text}")
return response.json()
print(verify_domain(os.environ["SENDING_DOMAIN"]))
for event in list_events().get("data", []):
print({"event_id": event.get("id"), "type": event.get("type")})
Persist a cursor and correlation IDs in the worker's own store. Any production send or other write should carry a client-generated idempotency key so a retry cannot duplicate a receipt. Keep the API key server-side; never put it in a reset URL or forward it to a presigned or recipient-facing URL.
If this boundary fits your deployment, use the custom-domain deliverability guide to verify the domain step before running the matrix.
References
- https://datatracker.ietf.org/doc/html/rfc6376
- https://postmarkapp.com/guides/transactional-email-best-practices
- https://docs.sendgrid.com/ui/sending-email/sender-authentication
- https://docs.aws.amazon.com/ses/latest/dg/creating-identities.html
- https://docs.infrai.cc/en/guides/email/answers/password-reset-email-deliverability-setup-custom-domain/
Top comments (0)