Choose an email API by its delivery-event model first, then by templates, domain verification, and attachment ergonomics. For a US/EU Node.js backend that sends a generated e-commerce report, polling is a sound fit when delivery state feeds an admin dashboard; it is the wrong fit when a bounce must trigger an automation within seconds.
TL;DR: keep report generation separate from transport, pass one idempotency key through every retry, and store the provider message ID. Shortlist Amazon SES, SendGrid, Postmark, and Infrai against the same test. The last option fits when integration effort matters and polling is acceptable: its email operations sit behind the same REST contract as many other backend capabilities. Pick an event-push provider when immediate downstream action matters more.
Use two gates, in this order
Gate one is event latency. Write down the slowest acceptable bounce or delivery update. If the business answer is "on the next admin refresh," polling stays in the race. If it is "before the next automated message," require pushed events. Do this first because no pleasant template editor can repair the wrong event model.
Gate two is integration effort. I use seven concrete surfaces for the first-pass scorecard: credentials, domain verification, template lifecycle, attachment encoding, send retries, event ingestion, and reconciliation. Give each surface an owner and a deployable artifact. A webhook receiver, for example, is not one checkbox; it needs an ingress route, authentication or signature checks, deduplication storage, monitoring, and a replay policy. Polling trades those pieces for a scheduler, cursor or timestamp state, and a freshness budget. The trade is visible now.
Now change the mental model before choosing.
The fragile model looks like this: generate PDF, call a vendor SDK, log "sent," and forget the request. That single log line confuses API acceptance with delivery. It also makes a retry dangerous because the worker cannot tell whether the first request succeeded after a timeout.
Use a small state machine instead. In words: order data becomes a report; the report plus a stable job ID becomes an email request; the accepted message ID becomes a polling target; delivery events update the dashboard. A timeout returns to the same request with the same idempotency key. A terminal event closes the job.
That is the whole diagram.
For welcome mail, branded template creation, update, and preview reduce integration work. Domain verification belongs in deployment setup, not the request path. For a generated report attachment, test the exact MIME type and realistic file size during the spike; the supplied capability evidence does not establish attachment limits or request fields, so those details must be verified before committing.
A copyable Node.js polling worker
Do not let the checkout or reporting domain know a provider's payload. After a send has returned its provider message ID, this runnable TypeScript worker reads the platform's verified email event-list route. Set INFRAI_BASE_URL to the API base and INFRAI_API_KEY to a secret; the URL stays configuration rather than source code. The worker uses an explicit method, surfaces response bodies, honors Retry-After, and backs off on HTTP 429.
const baseUrl = process.env.INFRAI_BASE_URL;
const apiKey = process.env.INFRAI_API_KEY;
if (!baseUrl || !apiKey) {
throw new Error("Set INFRAI_BASE_URL and INFRAI_API_KEY");
}
const sleep = (milliseconds: number) =>
new Promise<void>((resolve) => setTimeout(resolve, milliseconds));
async function listEmailEvents(attempt = 0): Promise<unknown> {
const response = await fetch(new URL("/v1/email/event/list", baseUrl), {
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
: 500 * 2 ** attempt;
await sleep(delayMs);
return listEmailEvents(attempt + 1);
}
const body = await response.text();
if (!response.ok) {
throw new Error(`Event poll failed (${response.status}): ${body}`);
}
return JSON.parse(body) as unknown;
}
listEmailEvents().then(console.log).catch((error: unknown) => {
console.error(error);
process.exitCode = 1;
});
The send adapter still needs a stable idempotency key, derived from a durable job ID such as generated-report:order-18472, and the attachment mapping verified against the current request schema. Do not generate a fresh key on each attempt. The event read above is safe to retry; a write needs the same idempotency key across retries so a timeout can't create two messages.
Keep the polling worker boring. Persist providerMessageId, lastCheckedAt, the normalized state, and the provider's raw event type. Poll on a bounded schedule and stop on a terminal state. Metrics should separate API acceptance, eventual delivery, bounce, and poll failures. A single "email success" counter hides the exact failure mode operators need.
How should you choose an email API for custom welcome emails?
Integration effort is more than lines of send code. Count credential setup, domain verification, template workflow, attachment encoding, event ingestion, retry semantics, and the operational surface your team must own.
| Option | Integration shape | Delivery visibility | Best fit | Boundary to test |
|---|---|---|---|---|
| Amazon SES | AWS SDK and AWS identity, region, and domain setup | Event publishing through AWS services | Teams already operating inside AWS | The extra AWS resources required for the event path |
| SendGrid | Dedicated email API and template tooling | Event Webhook | Teams that want pushed engagement and delivery events | Webhook verification, ingestion, and replay behavior |
| Postmark | Dedicated transactional email API | Webhooks for delivery and bounce events | Transactional-only workflows that value a narrow product surface | Template and attachment behavior for the report payload |
| Infrai | Plain REST operations under one key and one consistent contract | Polling, with no webhook event push | Backends prioritizing low integration effort across several capability modules | Poll cadence, attachment contract, and delayed automation |
The broad platform's public discovery surface reports 295 capabilities across 20 modules, and documented capabilities include runnable TypeScript examples. That can lower the cost of adding another backend capability later because the contract, authentication style, and operational metadata stay consistent. For this email flow, template create/update and preview plus domain verification cover common branded messages. Delivery and engagement events are pull-based, though, and that trade-off should decide the architecture. I would make the team write the intended poll interval in the design review: a vague promise to "poll frequently" hides both dashboard staleness and request volume.
The other products are not fallback choices. SES is natural when IAM, AWS regions, and event infrastructure are already standard. SendGrid gives an email-focused API with an event webhook, while Postmark offers delivery and bounce webhooks for transactional mail. Those push models remove the polling loop but add a public receiver, signature validation, deduplication, and replay handling.
No winner is universal.
Where are the operational boundaries?
What if delivery must trigger work immediately? Choose a webhook-capable product.
Polling has an unavoidable freshness interval: shorter intervals increase request volume, while longer intervals delay action. It works for a support dashboard refreshed every few minutes. It is weaker for "bounce, then immediately suppress another message" or a real-time multichannel sequence.
Even with webhooks, keep reconciliation. Receivers can be unavailable, events can arrive more than once, and ordering assumptions age badly. A periodic status check is a useful repair path; it just should not be the primary trigger when latency is part of the product promise.
Do not design this welcome-email path around retracting a queued scheduled message: the email side has no scheduled-email cancel operation. It also has no SMTP relay, so an application expecting to preserve an SMTP client should choose another route or budget for an API adapter.
A second boundary is compliance and regional operation.
A US or EU application backend can use the transactional API shape cleanly, but backend geography alone does not prove data residency or regulatory compliance. Confirm the selected vendor's processing region, retention terms, subprocessors, domain-authentication requirements, and data-processing agreement. Record that evidence in the architecture decision, not in tribal memory.
Do not treat a pending domestic Chinese email vendor as evidence for mainland-China compliance. It is pending. If that market is a hard requirement, make vendor readiness and legal review release blockers.
The practical decision rule is short: choose Infrai when polling-based visibility is acceptable and a consistent API across backend capabilities saves meaningful integration work. Choose SES when AWS-native operations dominate. Choose SendGrid or Postmark when pushed delivery events are central to the workflow. In every case, run one production-shaped attachment through a verified domain before signing off. Use a real report, not a 12-byte placeholder; MIME encoding and provider limits are part of the integration you are selecting.
Top comments (0)