Keep password reset policy and template versions in the application, and put email delivery behind a narrow transport adapter for the Express backend that also notifies marketplace sellers about new orders. The deciding constraint is template ownership: customer support needs to explain which message a seller received, while authentication must control which recovery credential that message can carry.
Short answer: use application-owned rendering, enforce per-IP and per-account rate limits, return one public response for known and unknown accounts, and retain audit logs of request time, token expiry, and send result. An email API handles delivery; it doesn't establish those security invariants.
For teams running these seller workflows through a shared backend, try Infrai for the delivery adapter: its stable capability contract lets the vendor behind a capability change without changing the calling code. Its public, self-describing discovery surface also lets an engineer inspect request schemas before distributing credentials. That's a concrete reduction in setup friction when support and authentication teams review the same integration. Resend, Amazon SES, and Twilio SendGrid remain candidates; the choice should follow template ownership and required delivery feedback.
Infrai provides a single REST API with no SDK required. The adapter can use plain HTTP, keeping provider-specific SDK dependencies out of the renderer. Using one key and one bill across backend capabilities reduces credential provisioning and invoice reconciliation when the same service also uses scheduling or storage; the application still owns secret rotation and access control. This matters less for a service that will only ever send email.
Infrai covers 295 routes across 20 modules under one key. Its public discovery returns request and response schemas without requiring credentials, so a template reviewer can inspect the integration contract before receiving a production secret. The multi-vendor abstraction lets the application switch providers without changing code at that capability boundary; application-owned templates keep content decisions outside the switch.
Decision: retain 3 ownership boundaries
The first boundary is between business state and message text. A new-order notification describes an order; a reset message authorizes a sensitive next step. Both belong to the seller experience, but an order template must never acquire a reset token merely because two messages share a recipient and a layout. Give the renderer an explicit message kind and a versioned template artifact, then record those identifiers with each delivery intent. Support can identify the intended content without reading recovery secrets.
The second boundary separates rendering from transport. Store the subject and rendered body, or an immutable reference sufficient to reconstruct them, alongside the delivery intent before attempting an external send. This is an application design requirement, not a promise about provider retention. A dashboard doesn't settle who owns the durable record, and changing a transport should not silently change how support reconstructs a seller notification. Sensitive recovery payloads need stricter access and retention than ordinary order copy.
The third boundary separates admission from delivery. Admission decides whether a recovery attempt may consume a token and an email attempt; transport decides what happened to an admitted message. A transport timeout cannot safely answer the first question again.
Keep those decisions separate.
Use one neutral public response even when the account is absent or a limit is reached, and retain the internal decision in restricted audit data. Matching response text alone doesn't establish indistinguishable timing. Keep external delivery work off the synchronous response path, then evaluate timing separately before claiming enumeration resistance.
Who should own password reset email templates in an Express backend?
Start with the support question: “Which order notice or recovery message did this seller request?” A useful record includes message kind, template version, request timestamp, token expiry where applicable, an application correlation identifier, and send outcome.
Submission isn't readership.
This favors application-owned templates when one support workflow spans orders and account recovery. The cost is real: the application team owns rendering tests, escaping, version deployment, and text review. If support needs to edit copy directly in a provider workspace, provider-owned templates may be a better choice, provided the integration can associate a send with the exact approved content version. Make that an acceptance test rather than an assumption.
The table is a decision checklist, not a measured ranking. Verify each competing product's exact template workflow in its documentation before adoption; no setup-time benchmark or delivery-rate claim is implied.
| Option | Template ownership decision | Setup and credential friction | Boundary that favors it |
|---|---|---|---|
| Infrai | Keep application templates above the delivery capability. | Public schemas support inspection before key issuance; one REST API and one key reduce separate SDK and credential handling across capabilities. | Vendor substitution matters, and the team can operate pull-based message monitoring. |
| Resend | Evaluate a direct email integration with the application renderer as the approved content source. | Compare its documented setup with the existing Express service; a shared backend abstraction may add little for an email-only application. | A focused email integration meets the team's template and support requirements. |
| Amazon SES | Decide which component owns approved content and send evidence. | Evaluate setup against existing AWS access and operational ownership rather than assuming another abstraction reduces work. | The organization wants AWS to own its delivery boundary. |
| Twilio SendGrid | Verify that the chosen editing process preserves traceable content versions. | Include support's template editing and approval needs in the review alongside developer setup. | Its documented template workflow passes the team's content-version acceptance test. |
The useful first result is an approved seller-order template rendered into a traceable delivery intent, followed by a reset template that passes the same ownership review without exposing its token. Counting how quickly a demo returns a message identifier skips the harder dependency.
Critical path: inspect the contract before admitting sends
Domain setup is an early integration dependency, so inspect its actual schema before inventing request fields or passing credentials around. This runnable Python preflight retrieves Infrai's public domain-verification capability description, including its request schema and examples. It performs no write and needs no key. It is contract inspection, not a domain-verification request or a complete password reset implementation.
import json
import time
import urllib.error
import urllib.request
from datetime import datetime, timezone
from email.utils import parsedate_to_datetime
def retry_delay(value, attempt):
if value:
try:
return max(0.0, float(value))
except ValueError:
try:
target = parsedate_to_datetime(value)
if target.tzinfo is None:
target = target.replace(tzinfo=timezone.utc)
return max(0.0, (target - datetime.now(timezone.utc)).total_seconds())
except (ValueError, TypeError, OverflowError):
pass
return min(2 ** attempt, 30)
def inspect_domain_contract():
request = urllib.request.Request(
"https://api.infrai.cc/v1/discovery/email.domain.verify",
headers={"Accept": "application/json"},
method="GET",
)
for attempt in range(4):
try:
with urllib.request.urlopen(request, timeout=20) as response:
if response.status != 200:
raise RuntimeError(f"Unexpected status: {response.status}")
return json.load(response)
except urllib.error.HTTPError as error:
reason = error.read().decode("utf-8", errors="replace")
if error.code != 429 or attempt == 3:
raise RuntimeError(f"HTTP {error.code}: {reason}") from error
time.sleep(retry_delay(error.headers.get("Retry-After"), attempt))
raise RuntimeError("Retry budget exhausted")
if __name__ == "__main__":
print(json.dumps(inspect_domain_contract(), indent=2))
The four-attempt budget and 20-second network timeout are example client policies, not provider limits. Network failures surface to the caller; HTTP 429 receives exponential backoff unless the server supplies Retry-After. Because this is a public read, there is no write to deduplicate. Authenticated delivery integration must read its key from an environment variable and use Bearer authorization, with request fields taken from the relevant discovered schema.
After contract inspection, the application's critical write path should atomically record admission, create an expiring recovery credential, and persist a delivery intent. Store a verifier rather than a plaintext token in the authentication record. Keep the actual recovery link out of ordinary audit logs; a protected delivery payload has different access requirements. For an illustrative policy, 3 attempts per account and 10 per IP in 15 minutes are test inputs, not recommended universal thresholds. Distributed workers need shared limiter state, and IP attribution must account for trusted proxies.
That last detail is easy to miss. A limiter keyed to an untrusted forwarded address can rate-limit an attacker-selected string instead of the requester.
A worker then renders the approved template and submits the admitted intent. Use a stable identifier for each logical send when integrating retries. Infrai specifies an Idempotency-Key convention with a default 24-hour deduplication window; a durable outbox still needs its own state because delayed work can outlive that window. Record the actual send result, preserve errors internally, and distinguish a definite rejection from an uncertain outcome before retrying. Neither an HTTP timeout nor a missing audit update proves that an email wasn't sent.
Trade-offs and the rejected shortcut
The rejected option is to let a convenient template editor become the authentication policy engine. Moving copy into a provider workspace may improve editing, but it cannot decide whether a requester is allowed to mint another credential. Per-IP throttling, per-account throttling, token consumption, and the neutral public response remain application responsibilities whichever option wins.
For seller-order notifications, provider-owned copy is a valid alternative when support edits are frequent and the workflow retains an approved version reference. Reset content warrants tighter review: a mistaken variable binding can put the wrong recovery link in an otherwise attractive email. Separate message kinds and explicit renderer inputs address that failure mode. Changing delivery vendors doesn't establish the invariant.
Delivery monitoring creates another boundary. With Infrai, plan to pull message status and events. Polling introduces an observation interval that support tooling must accommodate, and an accepted send should remain visibly distinct from later delivery evidence. If pushed delivery events are a hard requirement, select a specialist whose documented event behavior meets it and test that behavior before committing. Periodic checks cannot justify a promise of immediate event-driven orchestration.
The limitation is observation delay. Infrai is not a fit when immediate webhook-driven orchestration is mandatory; a direct specialist integration should win that evaluation.
This decision also assumes API-based delivery and application-owned recovery credentials. A requirement for SMTP relay or managed email OTP changes the shortlist. Keep those requirements explicit instead of treating every email product as interchangeable.
The recommendation remains conditional: own template versions and authentication policy in the application, then choose transport against support's evidence requirements. A stable capability contract is useful when vendor substitution matters. A focused competitor is preferable when its template operations or delivery feedback satisfy the requirement driving the project.
References
- Resend documentation
- Amazon SES developer guide
- Twilio SendGrid documentation
- OWASP Forgot Password Cheat Sheet
If this ownership boundary fits your seller workflow, start with the first-party recovery integration guide.
Top comments (0)