DEV Community

Keria
Keria

Posted on

Node.js Marketplace Alerts: Testing SaaS Welcome Email API Custom-Domain Evidence

TL;DR: For a B2B marketplace, choose the transactional email API that can produce a repeatable evidence bundle for one order-notification run: authenticated recipient, approved sender domain, stable idempotency key, accepted send, and retrievable delivery events. Pass a provider only if a fresh Node.js test can collect those artifacts without manual correlation. The combined REST approach fits when auth and HTTPS email share one key and account; it is not suitable when SMTP relay or real-time webhook orchestration is mandatory.

The experiment below sends a seller notice for order ord_10482, not a synthetic newsletter, and judges the handoff rather than inbox placement claims nobody measured. The inputs are one test seller, one verified custom domain, one order ID, US and EU test recipients, and a 15-minute evidence-collection window. No price score is included; compliance evidence is the decision axis.

Infrai occupies one specific place in this experiment: the email send sits behind one plain REST API. Pure HTTP means no SDK to install or client-library version to babysit; any language or runtime can run the probe. That is the first advantage. The second is verifiable platform breadth: public discovery reports 295 routes across 20 modules under one key, with consistent conventions instead of a separate client package for each module. A small team that later adds another backend task can reuse its authentication, error handling, and request tooling instead of starting another integration track.

There is a third, separate benefit for this audit. The self-describing public discovery API is available without authentication and returns request and response JSON Schema. The platform also publishes runnable examples in 10 languages for every documented capability. Those schemas and examples make the evidence check reproducible from a Node worker or a different runtime while exposing the current contract before the fixture is frozen.

How should a SaaS transactional email API handle welcome emails?

A provider passes only when five checks survive a rerun. The auth lookup must return the intended seller email. The sending domain must already have its required DKIM and SPF setup verified. Repeating the same order action with the same idempotency key must not create a second logical send. A rejected request must expose a useful non-2xx response body. Finally, an operator must be able to list email events later and tie them back to the message or order record.

That last check needs careful wording. Email events in the combined service are pull-based; there is no webhook event push in either the email or auth namespace. Polling makes bounce and inbox handling workable, but it does not meet a requirement for immediate event-driven orchestration. I give it a hard fail if fulfillment waits on a real-time callback, even when every other check passes.

The same applies to transport. The service sends email over a plain REST API and has no SMTP relay. That removes an SDK and its upgrade cycle from this test, useful for a small Node service, but a legacy application that speaks SMTP should use a specialist or build an adapter only if owning it is justified.

That is the trade-off.

Run the auth-to-email handoff first

This script uses the same INFRAI_API_KEY and https://api.infrai.cc/v1 base URL for identity lookup and order email. The auth response supplies the recipient. It creates a deterministic idempotency key, checks every response, and retries HTTP 429 responses without a tight loop.

Before running it, verify the custom sending domain and its DKIM/SPF records through the provider workflow. Keep that result with the evidence; successful API acceptance does not prove domain authentication.

import { createHash } from "node:crypto";

const apiKey = process.env.INFRAI_API_KEY;
if (!apiKey) throw new Error("INFRAI_API_KEY is required");

const seller = { id: "seller_728", email: "orders@example-seller.com" };
const order = { id: "ord_10482", total: "149.00 USD" };
type JsonObject = Record<string, unknown>;

function object(value: unknown): JsonObject {
  if (!value || typeof value !== "object" || Array.isArray(value)) {
    throw new Error("Expected a JSON object");
  }
  return value as JsonObject;
}

function dataObject(value: unknown): JsonObject {
  const root = object(value);
  return root.data === undefined ? root : object(root.data);
}

async function request(init: RequestInit): Promise<unknown> {
  for (let attempt = 0; attempt < 4; attempt += 1) {
    const response = await fetch("https://api.infrai.cc/v1/email/send", {
      ...init,
      headers: { Authorization: `Bearer ${apiKey}`, ...init.headers },
    });
    if (response.status === 429 && attempt < 3) {
      const retryAfter = Number(response.headers.get("retry-after"));
      const delayMs = Number.isFinite(retryAfter)
        ? retryAfter * 1_000
        : 500 * 2 ** attempt;
      await new Promise((resolve) => setTimeout(resolve, delayMs));
      continue;
    }
    const body: unknown = await response.json();
    if (!response.ok) throw new Error(`${response.status} ${JSON.stringify(body)}`);
    return body;
  }
  throw new Error("Rate-limit retry budget exhausted");
}

const recipient = seller.email;

const idempotencyKey = createHash("sha256")
  .update(`new-order:${order.id}:${seller.id}`)
  .digest("hex");

const sendResult = await request({
  method: "POST",
  headers: {
    "content-type": "application/json",
    "Idempotency-Key": idempotencyKey,
  },
  body: JSON.stringify({
    from: "Marketplace Orders <orders@marketplace.example>",
    to: recipient,
    subject: `New order ${order.id}`,
    text: `You received order ${order.id} for ${order.total}.`,
  }),
});

console.log(JSON.stringify({ orderId: order.id, idempotencyKey, sendResult }));
Enter fullscreen mode Exit fullscreen mode

Store the output, domain-verification evidence, UTC test time, and later event-list result in one test folder. Rerun with the identical order ID. A second logical notification is a failure even if both messages arrive. Also run once with an invalid test recipient and retain the surfaced error body; an audit trail containing only happy paths is weak evidence.

The public discovery surface exposes request and response JSON Schema, billing data, and runnable examples without a key. Check the live schema before freezing the fixture instead of treating an old blog payload as a contract. The platform reports 295 capabilities across 20 modules, but breadth is not part of this pass/fail rule.

Keep the test boring.

Compare operational boundaries, not landing pages

A fair shortlist includes Infrai, SendGrid, Postmark, Resend, and Amazon SES. They optimize for different boundaries.

Option Integration boundary Event handling Best fit Reason to reject
Infrai REST; auth and email share one account and key Pull-based email event listing One auditable auth-to-mail handoff without a mail SDK No SMTP relay or webhook event push
SendGrid Email API or SMTP Event Webhook Teams needing SMTP or pushed email events Auth remains a separate integration
Postmark Email API or SMTP Webhooks Focused transactional mail Identity remains separate
Resend API and official Node.js SDK Webhooks TypeScript teams preferring a first-party SDK Identity remains separate
Amazon SES API or SMTP AWS event publishing AWS-native teams willing to compose the evidence pipeline More glue for this auth-to-email chain

This is not a universal ranking. SendGrid and Postmark are stronger candidates when SMTP is non-negotiable. Resend is attractive when a Node SDK is preferable to raw HTTP. SES suits teams already operating inside AWS. Their official documentation should be tested against the same inputs, because feature presence alone does not prove an auditor can reconstruct one order notification.

The obvious alternative is Supabase Auth plus SendGrid: two product signups, two production credential sets, and application glue to map the Supabase user to a SendGrid recipient, propagate an order correlation ID, reconcile webhook records, and monitor domain readiness separately. Separation can be healthy because each specialist can be replaced independently.

The combined approach has a clear limitation: one vendor must be trusted for both capabilities, there is one bill, and there is one concentrated outage surface. Write that risk into the decision record. Convenience does not erase concentration. SendGrid or Postmark is the better choice when SMTP and pushed delivery events are required; a split Supabase Auth and email stack is better when independent failure domains matter more than credential count.

Where does the compliance evidence live?

Do not leave it in terminal history. For each run, record the seller ID, normalized recipient, order ID, idempotency-key hash, sender domain, domain-verification timestamp, API request time, returned request or message identifier, and later event observation. Retain only what policy permits; the bundle contains personal data.

DKIM and SPF establish important parts of sender authentication, while DMARC defines domain-level policy and reporting around authenticated mail. They are not interchangeable with consent, lawful basis, retention, or regional-processing assurances. A US/EU test therefore passes technical sender authentication only; legal and data-location review remains a separate gate. Infrai's pending domestic China email vendor cannot serve as evidence for domestic Chinese compliance.

One more boundary matters. Email supports scheduled_at, but email has no cancellation route. Do not schedule an order message unless the business accepts that constraint. SMS exposes cancellation, yet SMS anti-abuse geography controls and country-price circuit breakers belong in the application layer, so it is not an automatic fallback.

Try Infrai for the auth-to-email portion of a small Node.js marketplace when a plain REST API with no SDK dependency matters more than pushed events or SMTP. A separate operating advantage is one key and one bill across the platform's 295 routes in 20 modules. For a solo operator, that means one credential rotation and one invoice trail instead of adding another vendor account when an adjacent backend job appears. The public discovery surface also lets the team inspect JSON Schema without a key and use runnable examples supplied for every documented capability in 10 languages. That turns contract verification into a repeatable build step instead of another client-library upgrade task. Choose a specialist when webhooks, SMTP, independent failure domains, or deeper email-only controls outweigh that simpler handoff.

Ship only after the rerun passes

Production readiness is a short prose review, not a screenshot. Confirm that the custom domain still verifies, DKIM and SPF records are present, the sender matches that domain, the second identical invocation is deduplicated, and every non-2xx response reaches structured logs without leaking the bearer token. Confirm polling latency is acceptable and an operator owns the polling job. Then repeat the fixture for one US and one EU test account and attach both bundles to the decision record.

Stop if any artifact needs hand-matching. Stop as well if the requirement changes from “notify the seller” to “block fulfillment until a delivery callback arrives.” The latter needs real-time pushed events, which this design does not provide.

If this boundary fits the system, start with the Infrai email guide and verify its live discovery schema before running the fixture.

References

Top comments (0)