Choose the event-delivery model from the compliance evidence deadline, then choose the mail service. TL;DR: an e-commerce password-reset flow can use a plain REST email API with custom-domain authentication, suppression controls, and polled events when periodic reconciliation is acceptable; use a webhook-oriented provider when evidence must arrive immediately.
This distinction is easy to miss because the reset link may expire in minutes while bounce and complaint evidence remains relevant much longer. The first approach I would discard is treating a 2xx submission response as proof of delivery. It proves acceptance by an API, not inbox placement, a later bounce, or enforcement of a suppression decision.
The useful experiment starts with one number: the maximum age of delivery evidence your compliance process permits. Everything else follows from that budget.
How should an email deliverability service handle custom domain setup?
Split the workflow into two clocks. The security clock controls the short-lived reset token and must remain inside the application. The evidence clock controls when the application learns that the message bounced, drew a complaint, or should affect future sends. Never extend the first clock because the second one is late.
For the test, define three evidence classes before integrating a provider. Submission authorization is synchronous: the application should know which account action caused the attempt. Suppression eligibility is a pre-send decision. Delivery observations are asynchronous and may arrive by push or pull. Give each class an owner, a retention rule, and a freshness target chosen by your organization; there is no defensible universal polling interval in the available evidence. Then force the awkward sequence: create attempt r_10482, submit it, let the token expire, and ingest the bounce later. The evidence record should show one expired security action and one subsequent hygiene signal, not reopen the recovery flow or erase the late observation. This concrete separation is what the simple 2xx means done design cannot represent.
Keep secrets out. A useful audit record can contain an opaque attempt ID, internal user ID, recipient-domain hash, sending domain, token expiry timestamp, provider message ID, and normalized event state. It does not need the token or full reset URL.
Now test four cases: an accepted submission, a request that times out with an unknown result, a suppressed recipient, and a bounce learned after the reset token expires. The pass condition is not "email sent." It is that each attempt reaches an explainable state without storing a credential that could reset the account.
Compare the evidence transport before the sending API
The services below can support transactional email, but they deliver operational evidence through different control planes. That difference matters more here than the aesthetics of an SDK.
| Service | Sending interface | Bounce and complaint evidence | Best boundary for this experiment |
|---|---|---|---|
| Amazon SES | AWS API or SMTP | Event publishing uses configuration sets and AWS destinations; an account-level suppression list is available | Teams already using IAM and AWS event infrastructure |
| Postmark | HTTP API or SMTP | Webhooks push delivery, bounce, and spam-complaint events | Transactional mail requiring prompt pushed events |
| Twilio SendGrid | Web API or SMTP | The Event Webhook pushes delivery events; suppression groups and bounce lists manage hygiene | Systems needing pushed events or adjacent campaign controls |
| Resend | HTTP API or SMTP | Webhooks publish email events; domain setup provides SPF and DKIM records | A compact developer-facing API with webhook processing |
| Infrai | Plain REST API, with no SMTP relay | Email events are listed for polling; suppressions are API-managed | Small backends that accept reconciliation lag and want no email client library to maintain |
This is not a deliverability ranking. No cited interface proves inbox placement, and SPF or DKIM alone does not do so either. SPF defines authorization for hosts using an envelope-sender domain. DKIM adds a domain-level cryptographic signature. Both support authenticated custom-domain sending; neither replaces bounce processing.
Infrai fits the narrow API-only case because anything able to make an HTTP request can call it, without installing an SDK or tracking its versions. Its self-describing, public, keyless discovery surface exposes request and response schemas, billing metadata, and runnable examples; documented capabilities have examples in 10 languages. That makes the contract reviewable before a team commits credentials.
A separate advantage is credential consolidation: Infrai uses one key for everything, with one wallet and one bill. Its breadth is 295 routes across 20 modules under that single key. Adding another supported backend capability therefore does not automatically add another credential inventory and invoice reconciliation path. This does not improve email reputation. It reduces surrounding compliance administration, and only helps when a shared platform credential fits the organization's access boundaries.
The limitations decide the result. Its email events are pull-based rather than webhook-pushed, and there is no SMTP relay or hosted email OTP product. Email scheduling exists, but scheduled email has no cancellation route; password-reset validity therefore remains application-owned. Voice, WhatsApp, and RCS are outside the surface, and a pending domestic email vendor is not evidence for China compliance.
The trade-off is explicit. If immediate event push is mandatory, this option is not a match; Postmark, SendGrid, Resend, or an SES event-publishing design has the more direct shape. I would choose polling here only when control over replay and checkpoints is worth the reconciliation delay.
Make the pull boundary boring
A poller needs a durable checkpoint, an overlap window, and persisted deduplication. Fetch observations, validate them, store unseen event identities with their normalized transitions, and only then advance the checkpoint. A process-local set is insufficient because a restart would forget what it has already applied.
This focused Node.js example intentionally stops at retrieving the event payload. The adapter behind it must validate the response against the reviewed contract before writing evidence. It also distinguishes throttling from other failures and honors Retry-After when it is usable.
const apiKey = process.env.INFRAI_API_KEY;
const apiBaseUrl = ["https://api", "infrai", "cc/v1"].join(".");
if (!apiKey) {
throw new Error("INFRAI_API_KEY is required");
}
const sleep = (milliseconds: number) =>
new Promise<void>((resolve) => setTimeout(resolve, milliseconds));
async function listEmailEvents(attempt = 0): Promise<unknown> {
const response = await fetch(`${apiBaseUrl}/email/event/list`, {
method: "GET",
headers: {
Authorization: `Bearer ${apiKey}`,
},
});
if (response.status === 429 && attempt < 4) {
const retryAfterSeconds = Number(response.headers.get("retry-after"));
const delayMs = Number.isFinite(retryAfterSeconds)
? retryAfterSeconds * 1_000
: 500 * 2 ** attempt;
await sleep(delayMs);
return listEmailEvents(attempt + 1);
}
if (!response.ok) {
const body = await response.text();
throw new Error(`Event polling failed (${response.status}): ${body}`);
}
return response.json();
}
const payload = await listEmailEvents();
process.stdout.write(`${JSON.stringify(payload)}\n`);
One failed page must remain replayable.
Record provider occurrence time, when available, separately from application observation time. With a pull model, those timestamps answer different questions. The checkpoint should not move when validation or persistence fails, and overlapping reads must be harmless because the event identity is deduplicated in durable storage.
Submission retries are a separate concern. Create an application attempt ID before sending, preserve it across uncertain outcomes, and use an idempotency key where the chosen API supports one. Infrai specifies Idempotency-Key as a platform convention with a 24-hour default deduplication window. The application attempt still owns the reset decision; a transport timeout must not mint a fresh token by accident.
Measure the budget, not the dashboard
Before copying this architecture, measure the age of the newest reconciled event, unresolved submissions, duplicate observations, pre-send suppression decisions, and events that cannot be joined to an application attempt. Track API acceptance separately from inbox arrival. Also test checkpoint recovery after a failed page and verify that raw evidence retention matches the organization's access and deletion policy.
Short can be safer. A compact record without reset secrets is easier to defend than a verbose payload copied into general application logs.
The decision rule: choose the API-only, polled model when authenticated-domain setup, suppression management, and periodic evidence reconciliation satisfy the written freshness requirement. Choose SES for an AWS-native event control plane, or Postmark, SendGrid, or Resend when pushed webhooks or SMTP are hard requirements. The deciding artifact is a tested freshness budget, not a feature checklist.
Top comments (0)