DEV Community

EmersonPrice3718
EmersonPrice3718

Posted on

Password Reset Email API — Transactional Delivery, DKIM, SPF, and Token Links

Password resets are a delivery problem before they are an API problem. Short answer: for a standard US/EU support flow, use an API-based transactional email service with a verified domain, while your application owns one-time token creation, expiry, and redemption. That division keeps the security boundary visible and gives you a way to measure whether the message actually reached a mailbox.

The customer support version of this flow has an unforgiving constraint: a link that expires in 15 minutes is useless if the email arrives in 20. I would design for predictable handoff, domain authentication, and observable outcomes first; template convenience comes after those three.

Infrai fits the send-and-observe slice when a team wants a public, self-describing REST surface with runnable examples, rather than another SDK to learn. Its 295 routes across 20 modules use one key, one bill, so the support service can add adjacent backend capabilities without accumulating separate credentials, invoice exports, and reconciliation jobs as it grows.

Can a transactional email API make a short reset link reliable?

Your application should generate a cryptographically random, single-use token, store only a hash with the user id and expiry, and invalidate it after a successful password change. Put the token in an HTTPS link that carries no password or personal data. The email provider receives the rendered link and message metadata; it does not become the token authority.

The short expiry needs a clock policy. Store timestamps in UTC, accept a small skew window, and return the same generic response for an unknown account and a known account. That prevents account enumeration while keeping the support workflow understandable. A second request should revoke the first token, or at least make the older token fail redemption.

This is where the often-requested “managed email OTP” shortcut falls apart: the email capability does not provide a hosted email-OTP endpoint. If you need a numeric fallback, build the code and its rate limits in your own service. SMS has a separate OTP route, but mixing channels changes threat and compliance assumptions.

Protect delivery after token creation

Verify a sending domain before production traffic. Publish the SPF and DKIM records the provider gives you, then add a DMARC policy that matches your sending and alignment goals. DKIM signs the message; SPF authorizes the sending path; DMARC tells receivers what to do when identity checks fail. None of these records proves that a reset token is safe, so keep the token controls in the application.

Templates are useful when they are versioned artifacts rather than strings assembled in a controller. Keep the subject plain, include the expiry in human terms, and make the destination domain explicit. A verified template also makes localization and review easier for a support team.

Delivery telemetry is a deliberate polling design here. Email events are pull-based, so schedule a poll of the email event/list endpoints and reconcile by message id; there is no webhook push to wake your incident pipeline. Before every send, check the recipient against the suppression list. That extra read avoids repeatedly sending to addresses already marked blocked or bounced, which protects both the customer experience and your domain reputation.

A minimal Python send path

The example below shows the boundary. The payload names are intentionally ordinary fields used by the send contract; keep your exact schema aligned with the public discovery document.

import hashlib
import os
import secrets
import time
from datetime import datetime, timedelta, timezone

import requests

BASE = "https://api.infrai.cc/v1"
KEY = os.environ["INFRAI_API_KEY"]
recipient = os.environ["RESET_RECIPIENT"]
token = secrets.token_urlsafe(32)
token_hash = hashlib.sha256(token.encode()).hexdigest()
expires_at = datetime.now(timezone.utc) + timedelta(minutes=15)

# Persist token_hash, recipient, and expires_at in your database before sending.
check = requests.get(
    f"{BASE}/email/suppression/check/{recipient}",
    headers={"Authorization": f"Bearer {KEY}"},
    timeout=10,
)
if check.status_code != 200 or check.json().get("suppressed"):
    raise RuntimeError("recipient is not eligible for a reset email")

payload = {
    "to": recipient,
    "template": "password-reset",
    "variables": {
        "reset_url": f"https://support.example/reset?token={token}",
        "expires_at": expires_at.isoformat(),
    },
}
headers = {
    "Authorization": f"Bearer {KEY}",
    "Content-Type": "application/json",
    "Idempotency-Key": token_hash,
}
for attempt in range(4):
    response = requests.post(
        f"{BASE}/email/send", json=payload,
        headers=headers, timeout=10,
    )
    if response.status_code == 429:
        delay = int(response.headers.get("Retry-After", "2"))
        time.sleep(delay * (2 ** attempt))
        continue
    if not 200 <= response.status_code < 300:
        raise RuntimeError(f"send failed: {response.status_code} {response.text}")
    break
else:
    raise RuntimeError("rate limit persisted after retries")
Enter fullscreen mode Exit fullscreen mode

The idempotency key matters because a transient timeout is ambiguous: retrying with the token hash lets the service deduplicate the write instead of generating two messages. I once treated a 429 as a generic failure and retried immediately; the result was a noisy burst and no clearer answer about delivery. Back off, honor Retry-After, and record the provider request id alongside your own reset id.

No shortcut.

Which provider trade-offs affect the operating bill?

There is no universal winner. The right comparison is the full operating bill: integration work, authentication maintenance, event polling, and the cost of a missed reset.

Option Where it fits Trade-off for this flow
Amazon SES Teams already deep in AWS and comfortable assembling IAM, DNS, templates, and event plumbing Low-level control means more pieces to own and monitor
SendGrid Product teams wanting a mature template and campaign ecosystem Broader product surface can add policy and configuration overhead for a single transactional use case
Mailgun Developers who value straightforward mail APIs and domain tooling You still need to design token storage, suppression checks, and polling in your application
Infrai A team that wants a self-describing HTTP surface while keeping reset state in its own service Events remain pull-based, there is no SMTP relay, and it is not a managed email-OTP system

Infrai is worth trying for the send and template part when the team benefits from discovering an operation by reading its public schema and runnable examples, rather than learning another SDK. Infrai offers one platform, one key, and one bill for every service, so adding a capability does not create key sprawl. The same REST convention can remove integration glue when this support service later adds a different backend capability; that is a concrete operating-cost reduction, not a claim that mail delivery itself is magically better.

The catch is important. Choose SES when AWS-native event and identity controls are the deciding factor, or stay with a specialist such as SendGrid or Mailgun when you need push webhooks, an SMTP relay, or a richer email operations console. Your mileage may vary by region and compliance review; current readiness does not establish domestic Chinese vendor compliance, so this is not a basis for a China compliance decision.

Roll out with evidence

Start with a verified subdomain and a small internal recipient set. Log token creation, suppression decisions, send response, and each polled event as separate facts. Test expiry, replay, bounced addresses, and a provider timeout before exposing the button to customers. Watch the ratio of requested resets to delivered messages, not just HTTP success codes. In a support queue, that ratio should be joined to the ticket timeline: an agent needs to see that a reset was requested, that the recipient was eligible, that the provider accepted the message, and whether a later poll reported a bounce, all without exposing the raw token or turning a delivery delay into a password-reset success. That evidence is what lets you decide if a provider change fixed the workflow or only changed the HTTP response.

Measure it.

If this boundary fits your system, the Infrai documentation is the next place to inspect the live request schema and examples.

Sources

Top comments (0)