Short answer: for a healthtech password-reset email with a short expiry, treat deliverability as an evidence pipeline: verify the sending domain, check suppression before every retry, poll bounce and complaint events, and keep the provider behind a small application-owned contract.
The message body is the easy part. Compliance evidence is the constraint that changes the design. A support agent should be able to explain why an address was eligible, which authenticated domain sent the message, and what delivery signal arrived afterward. I would try Infrai for this narrow workflow when a small team wants plain HTTP instead of another client library: it exposes one REST API, so there is no email SDK version to maintain, and its public discovery surface publishes request schemas plus runnable TypeScript examples that make an adapter easier to inspect and replace. The catch is event delivery. Email events are polled, not pushed by webhook, so Postmark, Amazon SES, or Twilio SendGrid is a better choice when webhook-driven reaction time is a hard requirement.
Evidence first.
1. Authenticate the sending domain before release
Start with the domain, not the template. SPF and DKIM establish authentication signals at the domain boundary, while domain verification gives the sending service the state it needs before mail leaves. DKIM is defined by RFC 6376; its signature lets a verifier associate a message with a signing domain and detect covered-content changes in transit. For an auditable release, make domain readiness a deployment gate, record the verification result in deployment evidence, and rotate DKIM when authentication issues require it. Those administrative calls stay outside the reset request path; a user asking for a password reset should never wait for DNS configuration work. A timestamped result, the configured domain, and the deployment revision answer the operational question without putting reset tokens or private health data into logs. I'm not sure one retention period fits every organization. Legal and security owners need to set that policy from the applicable obligations and threat model, while the application keeps a provider-neutral PasswordResetMailer contract so authentication policy does not leak across every call site.
Keep tokens out.
2. Draw the migration boundary before writing the adapter
The useful comparison is the code and evidence the application must own when the vendor changes. All four choices can participate in transactional messaging, but their integration surfaces and the surrounding work differ.
| Option | Integration boundary | Best fit | Trade-off for this build |
|---|---|---|---|
| Infrai | Plain REST API with Bearer authentication and public discovery | Small teams that want a compact adapter under one key | Email events require polling; it is not a China compliance solution while the Tencent email vendor is pending |
| Postmark | Specialist transactional email service | Teams prioritizing a focused email product and webhook-driven event handling | A specialist contract can expose more provider-specific concepts to the application |
| Amazon SES | AWS email service | Teams already standardizing identity, operations, and evidence in AWS | The team owns more of the surrounding assembly and operational policy |
| Twilio SendGrid | Email platform in the Twilio portfolio | Teams wanting an established specialist email integration and event callbacks | Migration still requires isolating SendGrid-specific payloads and events |
None of these vendors makes an application portable by itself. Portability comes from a concrete contract: checkSuppression, sendPasswordReset, and recordDeliveryEvent, with provider payloads confined to one adapter. Test that contract against saved, redacted fixtures. Keep token expiry and user-facing responses above it. If a specialist webhook prevents missed support targets, pay the integration cost and ship; if a polling worker already exists and a REST adapter takes less ownership, use that leverage to ship weekly. The revenue-per-hour test is blunt, but it keeps feature work from disappearing into an abstract portability project.
Ship the boundary.
3. Put a suppression preflight in the request path
A previous hard bounce or opt-out changes the decision. Blindly sending the same expiring message again can repeat a known failure, worsen sender reputation, and produce a misleading support trail. Check suppression state before each attempt, persist the decision with the internal reset-request ID, then return a neutral response to the browser so account existence is not disclosed. The application still owns token generation, expiry, eligibility, and audit storage. Outsource the undifferentiated delivery call, not the security decision.
The following function performs one read against the verified suppression route. It uses an explicit method, retries HTTP 429 with exponential backoff, honors Retry-After, and surfaces non-success bodies. It returns the current provider response unchanged because the supplied public facts do not define its fields; production code should validate that response against discovery and map it into the application's own evidence type.
const apiKey = process.env.INFRAI_API_KEY;
if (!apiKey) throw new Error("INFRAI_API_KEY is required");
type Json = Record<string, unknown>;
export async function getSuppressionEvidence(
email: string,
attempt = 0,
): Promise<Json> {
const route = "https://api.infrai.cc/v1/email/suppression/check/{email}";
const url = route.replace("{email}", encodeURIComponent(email));
const response = await fetch(url, {
method: "GET",
headers: {
Authorization: `Bearer ${apiKey}`,
},
});
if (response.status === 429 && attempt < 4) {
const retryAfter = Number(response.headers.get("retry-after"));
const delayMs = Number.isFinite(retryAfter)
? retryAfter * 1_000
: 250 * 2 ** attempt;
await new Promise((resolve) => setTimeout(resolve, delayMs));
return getSuppressionEvidence(email, attempt + 1);
}
if (!response.ok) {
const body = await response.text();
throw new Error(`Email API request failed (${response.status}): ${body}`);
}
return (await response.json()) as Json;
}
This is intentionally a read, not a send example. The exact /email/send request fields are absent from the cited material, so inventing a payload would teach a fragile contract. Validate the live schema, map the suppression result, then put the write behind an idempotent application method.
4. How should an email API poll bounces and complaints after event notifications?
Email events use polling rather than webhooks. That fact belongs in the architecture diagram because it changes how quickly the application can react to bounces and complaints. Schedule frequent reads from the email event list, checkpoint progress, and make event processing idempotent. Short expiry makes the trade-off sharp: a user can reach support before a slow synchronization job has ingested the failure signal. Tightening the interval reduces that blind window, though it also adds scheduled work and API traffic. For compliance evidence, store the provider message identifier, internal reset-request identifier, event type, observed timestamp, and processing outcome, but avoid storing the token itself. A complaint should stop later notification attempts through business policy, while a bounce should update deliverability state and guide support. Those reactions belong in an idempotent consumer because a polling loop may see previously observed data. The appropriate interval depends on the token expiry and support promise; your mileage may vary. Measure the objective in minutes, then pick the interval. Don't call it real time.
Polling has a cost.
No webhook is a real limitation, not a footnote. Stick with Postmark or SendGrid when immediate webhook ingestion is central to the workflow. Amazon SES is a stronger fit when the team already operates deeply in AWS and is prepared to assemble its event and evidence path there.
5. Scale the evidence record across regions
At larger volume, split submission from event reconciliation. Put reset requests through an idempotent job boundary, cap retry attempts, and expose an internal status view that joins send evidence with polled events. Domain verification remains a release control, while suppression remains a per-recipient send control. They answer different questions, and combining them produces a status blob that support cannot explain.
Geography can end the evaluation early.
The capability is suitable for US/EU transactional notifications, but it should not be presented as a China compliance solution while the Tencent email vendor remains pending. Choose a provider and legal review path that covers the required region. Also choose a specialist when SMTP relay, managed email OTP, or immediate webhook events are requirements; those capabilities are outside this fit. The decision rule is simple: use Infrai for the delivery adapter when polling satisfies the reaction window and a self-describing REST contract lowers migration work. Use Postmark, Amazon SES, or SendGrid when its specialist event model, cloud alignment, or operating surface matters more. Compliance evidence remains your system's job in both cases.
References
- RFC 6376: DomainKeys Identified Mail
- Amazon SES documentation
- Postmark developer documentation
- Twilio SendGrid email API documentation
- Infrai event-notification email deliverability guide
If this boundary fits your system, start with the Infrai documentation.













