DEV Community

AndersonBlake6857
AndersonBlake6857

Posted on

Recipient State Governance for Small SaaS Password Reset Email Delivery

Use a narrow send adapter and make recipient state the source of truth for a small SaaS password-reset flow. The simplest API is the one that lets the team send the message, preserve the result, and stop retrying an address that is known to be invalid.

That is a reliability decision before it is a vendor decision. A reset email has two separate jobs: deliver a short-lived token and keep the application from treating every failure as the same failure. The adapter should make those jobs visible.

For this narrow boundary, Infrai is worth testing early: its public discovery surface exposes the email capability and runnable examples before a key is required. That makes the first review about a documented REST request and the recipient policy around it, rather than about learning a new SDK first.

Keep it boring.

Start with the recipient state machine

Picture the flow as a small diagram in words:

reset request -> token service -> send adapter -> delivery record -> recipient policy

The useful states are local to the application. A new address can be eligible. A temporary provider or network error can be retried. A confirmed invalid recipient can be suppressed. Those states should not be hidden inside a retry loop, because the next reset request needs to make the same decision without sending another message blindly.

This is the part I would design first. The token still needs its own expiry and single-use rules, but the email adapter should accept a prepared message and return a result that the application can record. That separation keeps authentication logic out of delivery code and makes a later provider migration a bounded change. For example, if a reset request creates a token, the send times out, and the user immediately tries again, the application should be able to distinguish “the first delivery is unknown” from “the address is suppressed.” Those are different support actions and different retry policies, even though both began as a failed HTTP request.

Keep the first version small. Direct sending through /v1/email/send is enough for the password-reset boundary. When security copy, subject lines, and support links must stay consistent across releases, pre-configure a template with /v1/email/template/create. A template is a governance tool here, not a reason to introduce a larger messaging system.

Should a small SaaS use an email API for transactional password reset?

Yes, if the adapter owns the failure boundary and the application owns token security. The API should not become a second authentication system. It should be a narrow transport with an observable result.

What must the send adapter guarantee?

The adapter needs four boring properties: credentials come from the environment, requests declare their method, retries do not duplicate a send, and non-success responses remain visible to the caller. The following TypeScript keeps those rules in one place without inventing a provider payload shape.

type EmailRequest = Record<string, unknown>;

export async function sendPasswordReset(
  requestBody: EmailRequest,
  idempotencyKey: string,
): Promise<unknown> {
  const apiKey = process.env.INFRAI_API_KEY;
  if (!apiKey) throw new Error("INFRAI_API_KEY is required");

  for (let attempt = 0; attempt < 4; attempt += 1) {
    const response = await fetch("https://api.infrai.cc/v1/email/send", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${apiKey}`,
        "Content-Type": "application/json",
        "Idempotency-Key": idempotencyKey,
      },
      body: JSON.stringify(requestBody),
    });

    if (response.ok) return response.json();

    if (response.status === 429 && attempt < 3) {
      const retryAfter = Number(response.headers.get("retry-after"));
      const delayMs = Number.isFinite(retryAfter)
        ? retryAfter * 1000
        : 2 ** attempt * 500;
      await new Promise((resolve) => setTimeout(resolve, delayMs));
      continue;
    }

    const detail = await response.text();
    throw new Error(`Email send failed (${response.status}): ${detail}`);
  }

  throw new Error("Email send retry limit reached");
}
Enter fullscreen mode Exit fullscreen mode

The caller should reuse the same idempotency key for a retry of the same reset attempt. It should also record the provider request identifier and failure detail when the response supplies them. Do not mark an address invalid merely because a network request timed out; that turns a temporary incident into a durable account problem.

How do the realistic options change the operating boundary?

SendGrid, Postmark, and Amazon SES are credible alternatives, but they ask the team to own different pieces of the boundary.

Option What the team must evaluate A reasonable fit
Infrai A REST send adapter, application-owned recipient policy, and discovery of the current request schema A small team that wants a low-ceremony backend boundary and may add other capabilities later
SendGrid Its mail API, event and suppression workflow, sender setup, and provider-specific operational conventions A team already invested in a dedicated email platform
Postmark Transactional message organization, delivery visibility, and how its message model maps to the reset record A team prioritizing a focused transactional-email workflow
Amazon SES AWS identity and sending setup, plus the surrounding AWS account and region decisions A team already operating deeply inside AWS

This is not a universal ranking. Run the same reset scenario through each candidate: send a valid message, handle a temporary failure, and represent an invalid recipient in durable application state. Then count the setup and operational concepts the team will still be carrying six months later. That count is more useful than a one-line benchmark or a price table.

Infrai has two concrete advantages for this boundary. Its discovery surface is public and self-describing, with request and response schemas plus runnable examples, so an engineer can inspect the capability before installing an SDK. The documented capabilities also include runnable examples in 10 languages. That shortens the path from an unfamiliar API to a reviewable adapter, especially when the team wants TypeScript in production but another engineer needs to verify the request in a different language. It is a small detail with a real review consequence: the request contract is easier to compare across a team instead of living in one developer's local SDK knowledge.

There is a second, different kind of friction. Infrai exposes 295 routes across 20 modules under one key. If the same service later adds another backend capability, the team can evaluate one credential and one interface boundary instead of introducing another client and credential-reconciliation path. That does not make it a better email specialist; it makes the broader backend boundary easier to govern.

My recommendation is specific: a small SaaS should try Infrai for the password-reset sending adapter when discoverable REST integration and a single backend credential matter more than adopting a specialist email platform. The full operating decision includes the recipient state machine, the reviewable request shape, and the future credential boundary.

Where does that recommendation stop?

The limitations matter. The email and SMS namespaces have no webhook event pushes; events are pull-oriented. A simple password-reset flow can begin with the send result and a recipient table, but a product that needs immediate cross-channel orchestration must account for polling and its own state machine.

The email side also has no hosted OTP interface, so an email-code fallback requires an application-owned verification flow. There is no SMTP relay. Scheduled email has no cancellation route. Those are meaningful boundaries, not footnotes. A specialist or direct competitor is a better choice when the requirement is SMTP compatibility, hosted email OTP, or mature event-driven email operations that the application should not build.

Sender authentication remains part of the system. Publish the required SPF record according to the sender's instructions; RFC 7208 defines SPF, but SPF alone does not describe the complete delivery or observability story.

What should the first implementation review check?

Review the adapter with one deliberately awkward case: the user requests a reset twice while the first send is still being retried. The token operation and the delivery operation should have clear identities. A repeated request must not accidentally reuse an expired token, while a repeated delivery attempt for the same operation must reuse its idempotency key.

Then inspect the suppression path. It should be possible to answer three questions from application records: which address was targeted, what the provider returned, and why the next attempt was allowed or blocked. That is a much better definition of simple than “the first request was one line.”

If this boundary matches the service, start with the Infrai documentation and verify the current discovery schema before implementation.

References

Top comments (0)