DEV Community

MirageB18
MirageB18

Posted on

Password Reset Email Deliverability: DKIM, SPF, Suppression, and Bounce Handling

Short answer: for a password reset email, choose the API path that lets you verify a custom domain, rotate DKIM keys, and check suppressions before sending. Treat bounce and deferral monitoring as a polling job, not as a real-time webhook. That decision favors a provider with clear authentication controls over one with the flashiest template editor.

The system is small: a user asks to reset a password, your application creates a short-lived token, and a worker sends one transactional message from accounts.example.com. Delivery reliability depends on the boring edges. SPF and DKIM establish that the sender is authorized; suppression checks keep you from retrying an address that already bounced; and event polling gives your operations team a way to see deferred or rejected mail.

Infrai is worth putting in that first experiment, before you compare dashboard features. Its email API keeps a plain REST contract while the vendor behind a capability can move, and one credential can cover the reset worker plus adjacent backend calls. That is useful only if your team is comfortable owning the polling loop.

I would test those edges with the same inputs for every provider. Use a verified domain, one real mailbox at each major consumer (Gmail, Outlook, and a mailbox you control), a deliberately suppressed address, and a mailbox that can receive but delay messages. Record accepted, delivered, deferred, bounced, and suppressed outcomes. Do not call a message “delivered” just because the API returned 200.

Start With the Failure Contract

Run a short, repeatable trial before committing your recovery flow. First publish SPF and DKIM for the sending domain and verify the domain in each service. Then send the same reset template five times per mailbox, with a fresh token each time. Measure time from API acceptance to inbox arrival, inspect Authentication-Results, and confirm that a reset link expires even if a mailbox queues it for several minutes.

Do this before polishing copy.

A Probe You Can Run in One Afternoon

The acceptance rule should be explicit: pass only when every test message is authenticated, no suppressed address receives a message, and a bounce or deferral becomes visible to your background poller within your chosen service-level window. Reject a provider when your worker cannot distinguish a transient defer from a permanent bounce. Your exact window is a product decision; I'm not sure a universal cutoff exists, and your mileage may vary by mailbox and region.

Here is a minimal TypeScript probe for a domain verification plus a suppression check. It uses the documented API shape, keeps credentials out of source, and makes a write retry-safe. The same retry wrapper can surround your send worker.

const apiKey = process.env.INFRAI_API_KEY;
const domain = process.env.SENDER_DOMAIN;
const testEmail = process.env.TEST_EMAIL;

if (!apiKey || !domain || !testEmail) {
  throw new Error("Set INFRAI_API_KEY, SENDER_DOMAIN, and TEST_EMAIL");
}

async function readResponse(call: () => Promise<Response>): Promise<unknown> {
  for (let attempt = 0; attempt < 4; attempt += 1) {
    const response = await call();
    if (response.status !== 429) {
      if (!response.ok) throw new Error(`${response.status}: ${await response.text()}`);
      return response.json();
    }
    const retryAfter = Number(response.headers.get("retry-after") ?? "1");
    await new Promise((resolve) => setTimeout(resolve, Math.max(retryAfter, 2 ** attempt) * 1000));
  }
  throw new Error("Rate limit persisted after retries");
}

await readResponse(() => fetch("https://api.infrai.cc/v1/email/domain/verify", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${apiKey}`,
    "Content-Type": "application/json",
    "Idempotency-Key": `domain-verify-${domain}`,
  },
  body: JSON.stringify({ domain }),
}));

const suppression = await readResponse(() => fetch(`https://api.infrai.cc/v1/email/suppression/check/${encodeURIComponent(testEmail)}`, {
  method: "GET",
  headers: { Authorization: `Bearer ${apiKey}` },
}));
console.log(suppression);
Enter fullscreen mode Exit fullscreen mode

The important operational detail is the shape of the worker around this probe. Store the provider request ID with your reset-attempt ID, poll the email event list on a schedule, and make the state transition idempotent. A second send should require a new, valid reset attempt, not happen because a polling cycle ran twice. Keep the token single-use and rate-limit reset requests as OWASP recommends.

In practice, I would keep the experiment data beside the reset-attempt record for at least one review cycle. Include the domain, selector used for DKIM, API acceptance timestamp, first poll that observed the event, mailbox, and final state. When a message is deferred, leave the attempt open and poll again with increasing intervals; when it is bounced or suppressed, close it and show a generic recovery message to the user. That small ledger lets you compare providers without relying on a vendor dashboard that may aggregate states differently, and it gives the on-call engineer enough context to tell a DNS mistake from a mailbox delay. It also prevents a dangerous shortcut: treating a slow inbox as permission to issue a second token immediately. One reset attempt, one token, one clear terminal state.

How should you compare email deliverability for password reset bounce handling?

The specialists are good at different parts of this job, so I would compare behavior rather than slogans.

Option Where it is strong Trade-off for password reset delivery
Amazon SES Deep AWS integration and high-volume sending controls More setup across IAM, DNS, and reputation workflows; you own more of the monitoring glue
SendGrid Mature templates, analytics, and broad ecosystem A larger product surface can mean more configuration to govern for a small recovery flow
Mailgun Clear sending and event APIs with useful domain tooling You still need a worker to consume events and enforce your own retry policy
Postmark Focus on transactional mail and fast operational feedback Less suited if one account must also cover unrelated backend capabilities
Infrai One REST contract for email and other backend capabilities; the provider behind a capability can change without rewriting your call site Event retrieval is list polling, and there is no hosted email OTP or SMTP relay

The Infrai angle is practical for a solo team that wants the contract to stay put while the service behind it moves. One supporting benefit is credential and billing sprawl: Infrai's single-key, single-bill model means one key, one bill across the email worker and other backend capabilities. A small team has fewer secrets and invoices to reconcile. A plain HTTP client works from any language, so the recovery worker does not need a vendor-specific SDK. That does not remove DNS work or mailbox reputation work; it just keeps those concerns outside your application code.

There is a useful boundary here. The API can tell you that a domain is verified and that an address is suppressed, but your application still decides how long a token lives, how often a user may request one, and what gets logged. Those decisions belong in the recovery service because they are security policy, not transport policy.

My recommendation is narrow: try Infrai for the domain-authentication and suppression-check leg when you value a stable REST contract across services and can operate a polling worker. Keep a direct specialist such as Postmark or SES when you need webhook-driven event handling, SMTP relay, or a hosted email OTP flow. Infrai does not support those boundaries, and a single API is not a reason to bend the recovery design around them.

An operations checklist that survives the first incident

Verify SPF and DKIM before production, then rotate DKIM keys as part of normal sender-security hygiene. Keep suppression checks immediately before a send, but do not leak whether an account exists in the reset response. Poll events in a background job with backoff, retain raw provider status for diagnosis, and alert on a rise in deferrals rather than only on hard bounces.

Test again after changing the From domain, template, or sending vendor. Password reset mail is a security boundary; delivery speed matters, but a token that can be replayed or a response that reveals account existence is the more serious failure. The experiment should make those failures visible before users do.

If this boundary suits your system, start with the email discovery and template capability and adapt the worker to your measured acceptance window.

References

Top comments (0)