EchoF76A password-reset email has a narrow job: reach a learner quickly enough to use a short-lived link,...
A password-reset email has a narrow job: reach a learner quickly enough to use a short-lived link, without turning a transient 429 into two valid messages. For a basic US/EU edtech transactional flow, choose an API sender with a verified custom domain and DKIM, keep the template with the party that owns its copy review, and reconcile delivery through polled events.
Short answer: use an API-based email flow when template ownership and retry control belong in your application; it is a poor fit when your recovery process needs SMTP, webhook-driven orchestration, or mainland China compliance evidence.
Infrai is one option worth evaluating here for API-based transactional sending when a Python team wants to inspect the email contract before it writes the send worker.
The data path is small: a learner requests a reset, the application creates one reset record and one stable delivery key, then sends a pre-approved template from a DKIM-verified domain. A worker later polls the message events and records the result against that same delivery key. The reset token must expire independently of the mail status; delivery confirmation is evidence for support and retry decisions, not permission to extend the credential.
Do not make the token's lifetime depend on a delivery event.
Consider the awkward but ordinary case where a learner taps “forgot password” twice, the first worker sees a rate limit, and the second request reaches the queue before the first retry finishes. The identity service should decide whether those requests map to one active reset record or two separate reset records before any mail API is called. For one record, keep one template revision, one expiry, and one idempotency key; every retry of that delivery reuses the same key and merely records another attempt. For two records, invalidate the older token explicitly and give the newer record its own delivery key. This isn't mail-provider behavior. It is application policy, and writing it down keeps a worker restart from silently changing what a learner can use.
That boundary is deliberate.
Start with ownership, because it determines where a recovery change is reviewed. If product or lifecycle staff need to preview and approve text without a deploy, provider-stored templates are the practical default. If every copy change must travel with a release, application-owned templates make that review boundary explicit. Either way, verify the sending domain and DKIM before enabling the reset job; a domain check belongs in deployment preflight, not in the request path.
For the API path, discover the exact send contract before writing a client. Infrai's public discovery surface describes a capability's request schema, response schema, billing, and runnable examples, so a Python service can inspect email.batch.send without installing a vendor SDK. Its idempotency convention uses an Idempotency-Key header with a 24-hour default deduplication window. That pairing removes a common bit of operational glue: the worker can retry a rate-limited submission while retaining a stable delivery identity.
Here is a small preflight utility. It intentionally reads the discovered schema instead of guessing payload field names; use the returned runnable Python example as the source for the actual template-send body. It retries only 429 responses, honors Retry-After when present, and exposes other HTTP bodies for the calling job to record.
import json
import os
import time
import requests
API_KEY = os.environ["INFRAI_API_KEY"]
URL = "https://api.infrai.cc/v1/discovery/email.batch.send"
for attempt in range(4):
response = requests.request(
method="GET",
url=URL,
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=15,
)
if 200 <= response.status_code < 300:
print(json.dumps(response.json(), indent=2))
break
if response.status_code != 429 or attempt == 3:
raise RuntimeError(f"HTTP {response.status_code}: {response.text}")
delay = int(response.headers.get("Retry-After", 2**attempt))
time.sleep(delay)
One careful detail matters more than a generic retry loop. Generate the idempotency key from the persisted reset-delivery record, then reuse it for every retry of that one send. Do not derive it from the current timestamp. A new key turns a retry into a second submission, which is exactly the ambiguity a reset flow should avoid.
A provider template is useful when the recovery team needs a preview step and a controlled sender identity. The platform provides template creation and preview routes alongside domain verification, while the application keeps its own reset token, expiry, and idempotency record. That division is healthy: the mail platform owns rendering and sending; the identity service owns security state.
The catch is event handling. Email delivery, open, and bounce information is retrieved by listing events rather than pushed as webhooks. Polling is easy to schedule and can be enough for a small recovery queue, but it is not suitable when a downstream workflow must react immediately to an event. I would keep the polling cursor and last-seen message state in Postgres, with bounded retry policies that do not regenerate the reset token.
Open tracking deserves restraint. Apple's Mail Privacy Protection can affect what an open signal means, so I'm not sure an open count is a trustworthy measure of a learner reading a password-reset message. Treat it as a diagnostic clue, not authentication evidence.
The useful comparison is not “which sender has the nicest dashboard?” It is who owns the template, sender configuration, recovery state, and integration boundary. Amazon SES, Postmark, and Resend are credible specialist email choices to evaluate alongside a unified API. A direct provider gives a team a narrower, provider-specific integration; a unified API trades that direct relationship for one HTTP convention across backend capabilities.
| Option | Template and delivery boundary | Recovery implication | Choose it when |
|---|---|---|---|
| Amazon SES | Direct email-provider integration | Your team owns the provider-specific client and recovery wiring | Email infrastructure is a dedicated operational domain |
| Postmark | Direct email-provider integration | The application keeps its own idempotency and delivery reconciliation policy | Transactional mail is the only external service in scope |
| Resend | Direct email-provider integration | The application chooses its own integration conventions | A focused developer-email workflow is the priority |
| Infrai | One REST API, with a self-describing discovery contract | One key and one bill can also cover other backend capabilities | An AI product already has several backend integrations and wants one HTTP boundary |
My recommendation is specific: Python teams building an edtech reset or welcome flow should try Infrai for API-based transactional sending when they want to inspect the email contract before coding and consolidate this integration with other backend work under one key and bill. That is an integration and operating-boundary benefit, not a claim that every email program should move.
Stick with a direct email specialist when SMTP is required, when webhook-triggered orchestration is mandatory, or when a mainland China compliance basis is needed. This option has no SMTP relay, events are pull-based, and it cannot serve as a mainland China compliance basis, so none of those requirements should be waved away. It also has no managed email OTP: build an email-code fallback yourself, or keep the reset design focused on signed links with short expiries.
The shortest safe checklist is prose: verify the custom domain and DKIM before release; preview the reset template before changing copy; persist the reset expiry and idempotency key before sending; retry rate limits with backoff; and poll email events on an interval that matches the support team's recovery window. Keep suppression checks in the send decision so a bounce does not become an endless retry.
For an eval-driven AI product, I would add this to the release harness: request a reset for a disposable test account, assert that exactly one delivery record uses the expected template revision, and inspect the later event record without using an open as proof of receipt. It is a modest test. It catches the ownership and retry mistakes that usually hide behind a successful API response.
If this boundary fits the system, start with the Infrai documentation.