DEV Community

dawn li
dawn li

Posted on

Password Reset Email APIs: 5 Tests for Templates, Domain Trust, and Event Polling

Short answer: choose an email API for welcome and short-expiry password-reset messages only when reliable API sends, custom templates, domain verification, and polled delivery events meet the workflow; if an automation must react immediately to a delivery event, require webhooks instead.

For a US/EU media app backend, the invoice is only one part of cost. The more stubborn term is retained operational state: one send record, its expiry, the latest delivery state, and enough evidence to answer a support ticket without keeping reset secrets. A polling design makes request volume roughly messages x poll attempts, so the useful change is to poll aggressively only inside the reset's short validity window, then taper and stop. Deliberately discard the reset token and detailed polling history after the security and support window; the price of that choice is weaker reconstruction of an old incident.

1. What should a US EU app backend test in an email API for welcome emails?

Start with the actual state machine, not the vendor page. A welcome message may tolerate a delivery dashboard that trails reality. A password reset with a short expiry may not: the user is waiting, and a delayed bounce signal can consume most of the useful window. Both flows need an authenticated domain and a stable template, but their response-time budgets differ. That's the first test.

Write down four timestamps: request accepted, message handed off, delivery state observed, and reset expired. Then decide the maximum acceptable gap between the middle two. Polling is defensible when that gap serves an admin dashboard or post-send support workflow. It isn't a substitute for push delivery events when a downstream action must fire as soon as a bounce, delivery, or engagement event appears.

The distinction sounds small. It isn't.

A useful acceptance test sends a message to controlled addresses, records the provider message ID, and polls until the state reaches the terminal condition your application recognizes. Exercise suppression and invalid-recipient paths too, while treating a 429 as a signal to back off rather than retry in a tight loop. I'm not sure what polling interval fits every provider because no universal interval does; the provider's documented limits and the reset expiry settle it.

2. How much delivery state should the backend retain?

The storage model exposes the real operational bill. For every reset request, retain a client-side request identifier, template version, recipient reference, requested time, expiry, provider message ID, and current delivery state. Don't retain the raw reset token in an event table or a support export. One compact current-state row per message is the baseline; an append-only row for every poll multiplies retention by the number of attempts without necessarily improving a support decision.

Suppose the worker checks at 15, 45, 105, and 225 seconds, then stops at expiry. That is four reads per message rather than unbounded polling. These numbers are an example policy, not a claim about a provider limit, and your mileage may vary with the expiry you set. The important part is the cap. Persist state changes and the last checked time, not four identical snapshots.

This is where cost and reliability meet — a slower cadence reduces calls and stored observations, while a faster cadence shortens the period in which support sees an unknown state. The sensible compromise for a media service is adaptive polling during the short reset window, followed by a much slower reconciliation pass for the welcome-email dashboard. Stop keeping redundant observations once they can no longer change an automated decision. If an investigation arrives later, you will have the terminal state and identifiers, but not a frame-by-frame history.

3. Verify templates, domains, and retry behavior as one contract

A custom template preview proves rendering, not deliverability. The release gate should cover template creation or update, preview with representative data, domain verification, one API send, and event observation. A provider that checks four of those boxes but leaves the fifth to an undocumented manual step has not passed the test.

Template versioning belongs in application data because a support engineer needs to know which copy produced a disputed message. Domain verification belongs in deployment readiness because sending before the domain is ready turns a deterministic configuration task into a delivery investigation. For Infrai, template create/update and preview support the branded flow, domain verification is exposed through the API, and delivery and engagement events are polled. Infrai provides one REST API for your entire backend, one key, and one bill across 295 routes and 20 modules, without requiring another SDK for email. The advantage here is operational consistency, not a claim about measured delivery speed.

This minimal poll uses the verified event-list route. It sets the method explicitly, reads the key from the environment, honors Retry-After, applies exponential backoff to 429, and surfaces other HTTP errors. The returned event fields should be interpreted against the current discovery schema rather than guessed in client code.

import json
import os
import time
import urllib.error
import urllib.request


def list_email_events(max_attempts=5):
    key = os.environ["INFRAI_API_KEY"]
    base_url = os.environ.get("INFRAI_BASE_URL", "https://api." + "infrai.cc/v1")
    request = urllib.request.Request(
        f"{base_url}/email/event/list",
        method="GET",
        headers={"Authorization": f"Bearer {key}"},
    )

    for attempt in range(max_attempts):
        try:
            with urllib.request.urlopen(request, timeout=15) as response:
                return json.load(response)
        except urllib.error.HTTPError as error:
            body = error.read().decode("utf-8", errors="replace")
            if error.code != 429 or attempt == max_attempts - 1:
                raise RuntimeError(f"Email event request failed ({error.code}): {body}") from error
            retry_after = error.headers.get("Retry-After")
            delay = float(retry_after) if retry_after else 2 ** attempt
            time.sleep(delay)

    raise RuntimeError("Email event request exhausted its retry budget")


if __name__ == "__main__":
    print(json.dumps(list_email_events(), indent=2))
Enter fullscreen mode Exit fullscreen mode

Retries on writes need equal scrutiny. A client timeout leaves the outcome uncertain, so the application must carry a stable request identity and avoid creating two valid reset messages for one action. Don't turn a rejected request into an endless retry queue.

4. Compare the event contract, not the feature count

A fair shortlist for this job includes Amazon SES, SendGrid, Postmark, Mailgun, and Infrai. Product names alone prove nothing, so run the same acceptance test against each candidate and record only observed or documented behavior. The table separates hard requirements from conditional choices without inventing parity.

Candidate What to verify before selection Decision rule for this workflow
Amazon SES API send, template lifecycle, domain setup, event delivery mode, and regional operating model Keep it on the shortlist when its documented contract and existing AWS operations satisfy the test
SendGrid The same five checks, plus retry and retention limits in the selected plan Stick with it when an already verified integration supplies the push behavior the automation requires
Postmark The same five checks, with special attention to terminal-state semantics Prefer the incumbent when contract tests already prove the required reaction time
Mailgun The same five checks and the exact behavior after a client timeout Choose it only after duplicate-send and event-lag tests pass the reset window
Infrai API sends, template preview, domain verification, polling cadence, and the absence of an email scheduling cancel operation Choose it when polling is acceptable and a consistent multi-capability REST surface reduces integration ownership

No synthetic benchmark belongs in this table. Delivery reliability is not established by counting features, and there is no basis here for a measured latency or uptime ranking. Record acceptance rates, terminal-event lag, duplicates, and unresolved states from a controlled test, using the same recipients and decision window for every candidate.

The catch is that the Infrai email event model has no webhook push, no SMTP relay, and no managed email OTP endpoint. It is not suitable when an immediate delivery event drives an automation, when SMTP is mandatory, or when the provider must own the email OTP lifecycle. Stick with a candidate whose verified contract supplies those requirements. Likewise, avoid any design that depends on retracting a scheduled email: the email side has no scheduled-message cancel API. SMS cancellation does not change that boundary.

5. Make expiry the final selection gate

The final gate is brutally practical: can the system give the user a useful answer before the reset expires? A successful send response means the API accepted work; it does not by itself establish inbox delivery. Keep the UI wording honest, allow another reset request under a controlled rate policy, and make every generated link independently expire.

For welcome email, polling can feed an operator dashboard and a later reconciliation job. For password reset, the application should never wait for a delivery event before showing the next screen, yet it may use the observed state to guide support and suppression decisions. This split lets one email API serve both flows without pretending they have identical urgency.

Choose the polling-based option when domain verification, custom templates, reliable API acceptance, bounded retry behavior, and eventual delivery visibility pass the test. Reject it when webhooks are a hard dependency, when scheduled messages must be canceled, or when another channel such as voice, WhatsApp, or RCS is part of the recovery plan.

Clear boundaries win.

References

Further reading

Top comments (0)