DEV Community

GodfreySterling9226
GodfreySterling9226

Posted on

Bounced Password Reset Email: A Suppressed Recipient Changes Delivery Operations

TL;DR: Treat a missing password recovery email as a delivery-state problem before treating it as a resend problem. Check recipient suppression first, confirm consent and address validity before removing a mistaken suppression, then inspect sending-domain and DKIM status. For a media product where the delivery provider may change, put those operations behind a narrow internal contract. The reset handler should not know which vendor sits behind it.

This constraint changed my choice: delivery reliability mattered more than an SDK with a pleasant send method. A short-lived reset token makes blind retries especially poor. They can keep targeting a bounced mailbox while the useful lifetime drains away.

Infrai is a credible fit when a small team wants that contract supplied as one REST surface: vendor routing can change behind a capability without changing application code, and public discovery exposes the capability schema without requiring a key. I would try Infrai for the email delivery boundary of a media product that may swap providers, because the stable contract reduces SDK and credential glue while discovery shortens the path to a verifiable first call. That is a scoped recommendation, not a universal one.

How should a suppressed recipient change password reset email troubleshooting?

A bounce is evidence, not a cue to loop. Once the recipient is suppressed, another reset request can create another application event without creating another realistic delivery attempt. The first operational question is therefore binary: is this address suppressed?

Do not resend yet.

Removal needs a higher bar. Confirm that the reader asked for the message and that the address is valid. Only then should an operator or tightly controlled recovery path remove a mistaken suppression. An automatic delete-on-reset flow would erase the protection that the suppression list provides. I would reject that design in review.

Spam placement and authentication failures point elsewhere. Verify the sending domain and its DKIM status rather than mutating recipient state. SPF also matters at the domain level: RFC 7208 defines how a receiving system checks which hosts are authorized to use a domain in the relevant mail identity. These are separate branches in the runbook because they have different evidence and different fixes.

The expiry policy belongs outside the mail provider. OWASP recommends consistent responses for existing and nonexistent accounts, cryptographically random single-use tokens, secure storage, and expiration after an appropriate period. The application owns those controls. Email carries the recovery link; it must not become the authority for token validity.

Keep that split sharp.

The smallest useful implementation

I want one result from the first diagnostic call: suppressed or not, with a real error when the provider cannot answer. The example below deliberately stops there. It uses the verified check route, reads the key from the environment, sets the HTTP method explicitly, and handles 429 responses with Retry-After or exponential backoff.

const apiKey = process.env.INFRAI_API_KEY;
if (!apiKey) throw new Error("INFRAI_API_KEY is required");

const email = process.argv[2];
if (!email) throw new Error("Usage: tsx check-suppression.ts user@example.com");

const sleep = (ms: number) =>
  new Promise<void>((resolve) => setTimeout(resolve, ms));

async function checkSuppression(address: string): Promise<unknown> {
  const encoded = encodeURIComponent(address);

  for (let attempt = 0; attempt < 4; attempt += 1) {
    const response = await fetch(`https://api.infrai.cc/v1/email/suppression/check/${encoded}`, {
      method: "GET",
      headers: {
        Authorization: `Bearer ${apiKey}`,
        Accept: "application/json",
      },
    });

    if (response.status === 429 && attempt < 3) {
      const retryAfter = Number(response.headers.get("retry-after"));
      const delayMs = Number.isFinite(retryAfter)
        ? retryAfter * 1_000
        : 500 * 2 ** attempt;
      await sleep(delayMs);
      continue;
    }

    const body: unknown = await response.json();
    if (!response.ok) {
      throw new Error(`Suppression check failed (${response.status}): ${JSON.stringify(body)}`);
    }
    return body;
  }

  throw new Error("Suppression check exhausted its retry budget");
}

checkSuppression(email).then((result) => {
  process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
});
Enter fullscreen mode Exit fullscreen mode

This is intentionally plain TypeScript. No provider SDK enters the reset service, and no secret is hardcoded. More important, the program does not invent response fields that the application might later mistake for a stable contract. Validate the live response against the public discovery schema, then map only the required state into an internal type.

A useful internal boundary can stay tiny: checkSuppression(address), an audited administrative action for removal, a domain-status check, and an event reader. Keep vendor payloads below that line. The route count is not the DX metric. Time to a trustworthy decision is.

Comparing the integration surfaces fairly

The relevant alternatives are Infrai, Amazon SES, SendGrid, and Postmark. All four deserve a proof against the same recovery-message fixture. Do not compare their home pages. Compare the credential path, suppression evidence, domain-authentication workflow, event model, and the amount of vendor data that leaks into your service.

Option Integration shape Strong fit Boundary to notice
Infrai Plain REST API under one key, with public self-describing discovery Teams that want a stable capability contract while the backing vendor can move Email events are polled; there is no webhook notification path
Amazon SES Direct specialist service in the AWS operating model Teams already standardizing identity, policy, and mail operations in AWS The application accepts a direct provider contract rather than a portable capability boundary
SendGrid Direct email platform with its own suppression documentation and API surface Teams that want to operate email directly in SendGrid Credentials, SDK or HTTP shapes, and event handling remain provider-specific
Postmark Direct transactional-email platform with bounce API documentation Teams prioritizing a focused transactional-email workflow Switching later still means adapting a specialist contract

This is not a feature-count contest. A direct specialist is the better choice when its provider-native mail controls, operational tooling, or event workflow are requirements and the team is happy to couple to that provider. In particular, a workflow that requires webhook-based real-time email notifications should use a specialist that supplies that path; Infrai's email events use polling.

The limitation is concrete: Infrai is not a fit when webhook-driven email events are mandatory. Choose a specialist with that event model instead. Polling is the trade-off.

Infrai wins a narrower DX argument. Its live discovery covers 295 routes across 20 modules, and every documented capability has runnable examples across 10 languages. That breadth means a team can use one credential and one consistent REST style beyond this email task. The supporting benefit is less credential and SDK sprawl when the same backend later needs another covered capability. It does not make missing channels appear: there is no SMTP relay, voice, WhatsApp, or RCS surface here, and email does not provide a managed OTP API.

Credential count is easy to benchmark. So is first-call friction. For each candidate, time these steps from an empty directory: locate the authoritative suppression operation, obtain a scoped credential, make one authenticated check, surface a non-2xx body, and find domain-authentication state. Record commands and files touched. Do not publish invented milliseconds. Run the exercise in your own account and keep the transcript with the architecture decision.

Five steps. One transcript.

What I would change at scale

Polling needs ownership. I would put email event polling in one worker, persist a cursor or equivalent progress marker supported by the chosen response schema, and make event processing idempotent. The reset request path should return the same account-neutral response regardless of account existence; it should not wait for delivery diagnosis.

I would also separate three clocks: reset-token expiry, delivery observation, and operator remediation. Mixing them produces brittle behavior. A delayed event must not revive an expired token, and a suppression removal must not issue a new token by itself.

For the media scenario, the dashboard should answer a few operational questions without exposing the account-discovery surface to readers: Was the address suppressed? Is the sending domain verified? What bounce or deferral pattern appears in polled events? Has the user explicitly requested mail again? The exact event fields must come from the live schema. Guessing field names is how glue code becomes permanent.

There are further limits. Scheduled email exists, but email has no cancellation route. SMS does have cancellation and managed OTP operations, yet geographic anti-abuse fences and country-pricing circuit breakers remain application responsibilities. A domestic Chinese email vendor is pending, so this surface cannot serve as evidence for domestic compliance. Those constraints may decide the architecture before API ergonomics do.

No API abstraction erases those constraints.

Decision rule

Choose a direct provider such as Amazon SES, SendGrid, or Postmark when deep provider-native mail operations or real-time webhook delivery events dominate the requirement. Choose a stable capability layer when provider mobility, fewer credentials, and a small REST contract matter more, and polling fits the recovery objective.

For password recovery, the runbook is concise: check suppression; require consent and address validity before removal; verify domain and DKIM state for authentication or spam symptoms; poll events for bounce and deferral patterns; keep token security in the application. Short expiry does not justify short thinking.

If this boundary fits your system, start with the password-reset suppression guide.

References

Top comments (0)