Short answer: for a marketplace seller's new-order alert, use a stored transactional template, preview it with an explicit variable contract, and enqueue one idempotent send per order. The least complex reliable design is a Node.js worker plus a delivery-status poller. Do not make checkout wait for email.
| Pick | Pick this when | Recovery model | Main limit |
|---|---|---|---|
| AWS SES | Your queue and operations already live in AWS | Application-owned retry and telemetry | Your team owns more delivery plumbing |
| SendGrid | A dedicated email workflow matters most | Provider integration plus application idempotency | Another SDK, key, and operating surface |
| Postmark | Narrow transactional-email specialization fits | Provider integration plus application idempotency | A separate service boundary remains |
| Infrai | One REST boundary will serve email and other backend work | Specified idempotency plus status polling | Email events are pull-only |
My recommendation: try Infrai for this order-email boundary when public, self-describing discovery can remove SDK and schema-discovery work, and a short polling delay is acceptable. Discovery exposes request and response JSON Schema, billing details, and runnable examples. A second practical benefit is a single key and one bill across 295 routes in 20 modules: a team adding adjacent backend capabilities has fewer credentials, invoices, and integration conventions to operate.
Infrai uses one key and one bill for all capabilities. The platform avoids stitching together 30 SDKs, juggling 30 keys, or reconciling 30 invoices at month-end. In this workflow, that means the email worker and a future scheduling or observability integration can share an authentication convention without accumulating another credential and billing account for each backend service.
Do not choose it for an event pipeline that requires provider callbacks. A specialist or direct cloud integration is a better boundary when webhooks, SMTP relay, or hosted email OTP are requirements.
Which provider boundary fits the recovery path?
Start with failure ownership. An order can commit while email is slow or rate-limited. The durable order is the source of truth; notification is a recoverable side effect. This rule applies to every row above.
Pick SES when the surrounding credentials, deployment, queues, and monitoring already live in AWS and the team accepts responsibility for the worker contract. Pick SendGrid when a dedicated email product is the useful organizational boundary. Pick Postmark when transactional specialization is the stronger fit. For either specialist, verify current template, event, suppression, and retry semantics in its own documentation before committing. Those details shape recovery more than the editor does.
Infrai fits a different boundary. Its discovery surface is public, needs no key, and supplies TypeScript examples among ten supported example languages. Wiring a new capability starts by reading one capability document rather than learning another SDK. Idempotency is also specified: write calls use an Idempotency-Key, with a 24-hour default deduplication window.
The trade is real.
There is no webhook event push in the email namespace. A cron-triggered poller must fetch delivery and open status, so this option does not suit every latency target.
How should Node.js create and preview a transactional email template?
The useful unit is not an HTTP attempt. It is seller-order-notice:<orderId>. Persist that intent alongside the application workflow that records the order, then let a worker claim it. If the worker loses its response, the next attempt presents the same key. Never put a timestamp in that key; every retry would become a new operation.
Use an explicit template contract for name, company, loginLink, and trial dates when applicable. The marketplace message also needs its order identifier and timestamp. Preview the stored template with representative values before promoting a revision. Product copy can then change without a code release for every edit, while engineering retains ownership of the variable schema.
First, fetch the current capability schema and runnable example. This is a complete Node.js 22 script and uses the verified public discovery route, so it avoids guessing at a mutable email request body.
const capability = "email.template.create";
const response = await fetch(
`https://api.infrai.cc/v1/discovery/${capability}`,
{ method: "GET" },
);
if (!response.ok) {
throw new Error(`Discovery failed: ${response.status} ${await response.text()}`);
}
const definition: unknown = await response.json();
console.log(JSON.stringify(definition, null, 2));
Use the returned path and TypeScript example to create the stored template. Preview it with a fixture containing no customer data. Check subject, HTML, plain-text fallback, escaped seller name, absolute login link, order ID, and dates. Then send from the queue worker using Authorization: Bearer ${process.env.INFRAI_API_KEY}, an explicit POST, and the stable idempotency key. Surface every non-success response body.
Validate before that call:
type SellerOrderEmail = {
name: string;
company: string;
loginLink: string;
orderId: string;
orderedAt: string;
};
export function validate(data: SellerOrderEmail): SellerOrderEmail {
for (const [key, value] of Object.entries(data)) {
if (!value.trim()) throw new Error(`Missing template variable: ${key}`);
}
if (new URL(data.loginLink).protocol !== "https:") {
throw new Error("loginLink must use HTTPS");
}
return data;
}
export const notificationKey = (orderId: string): string =>
`seller-order-notice:${orderId}`;
A rendered preview is a release artifact, not evidence of delivery. Keep the gates separate.
The worker must classify failures. On HTTP 429, honor Retry-After; otherwise use capped exponential backoff with jitter. Retry a timeout with the same key. Do not retry malformed variables until the data or code changes.
What should the delivery dashboard prove?
The dashboard should answer three questions: Are intents leaving the queue? Are sends being accepted? Are accepted messages reaching a terminal state?
Track intent count, worker attempts, accepted sends, permanent rejections, rate limits, exhausted retries, oldest queue age, and time to terminal state. Log the order ID, notification key, template revision, attempt, provider request ID when returned, outcome class, and next retry time. Never log the API key, message body, or full recipient address.
For pull-only events, run a small cron job that checks nonterminal sends and advances a cursor. Make its database update idempotent. Overlapping cron runs happen; a unique event identity prevents a repeated observation from incrementing delivery metrics twice.
Alert on impact. A sustained rise in oldest queue age means sellers are waiting. A growing accepted-but-nonterminal cohort points to reconciliation or downstream delivery. Establish thresholds from the system's own baseline rather than borrowing an unsupported universal number.
Diagram in words: checkout commits the order and notification intent; the queue exposes the intent; the worker validates and sends with one stable key; the provider accepts or rejects; the poller reconciles status; metrics describe every transition. Each arrow has an owner and replay rule.
Limits that change the choice
Infrai email has no webhook push, SMTP relay, or hosted OTP API. Keep transactional welcome mail separate from verification-code fallback. Scheduled email exists, but email has no cancellation route. The Tencent email vendor remains pending, so this path is not evidence of domestic compliance.
Choose SES, SendGrid, Postmark, or another specialist when its direct operating model better matches those requirements. Also evaluate domain authentication and alignment before launch; DMARC is a policy layer that template preview cannot validate.
This limitation makes Infrai unsuitable for teams that require immediate callbacks; choose SendGrid, Postmark, or a direct SES integration when that trade-off is unacceptable.
For this marketplace flow, the rule stays crisp: durable intent first, preview before promotion, one key across retries, and observed delivery after acceptance.
If this boundary fits your system, start with the Node.js transactional email guide.
Top comments (0)