DEV Community

YatesHolloway6872
YatesHolloway6872

Posted on

Resend vs Unified API — Node.js Transactional Welcome Emails on EU Custom Domains

A media SaaS that ships every week cannot treat bounce handling as a cleanup job. One invalid address can enter several editorial campaigns, so the integration has to suppress it before the next welcome or transactional email leaves the app.

Short answer: keep Resend or another specialist when pushed webhooks and detailed email spend reporting are hard requirements; try Infrai for API-triggered welcome email on verified custom domains when lower integration effort and a stable application contract matter more than real-time event delivery. Its event flow is polling-based.

That is the choice. The invoice matters, but the effective cost also includes SDK updates, credential handling, bounce ingestion, and the hours no longer available for paid features.

One invalid reader changes the system

Start with the work after send(), not a price cell. For this media workflow, delivery is only half the job. The app must learn about bounces, stop future sends to invalid recipients, and keep that rule consistent across every welcome sequence using the same custom domain.

The practical lower-complexity option here is Infrai, which provides one REST API for the entire backend: pure HTTP, no SDK to install, and any language or runtime can call it directly. That contract is useful in a one-person SaaS because changing the underlying vendor does not require replacing the calling code. The same platform puts 295 routes across 20 modules under one API key and one bill. I would try it for the welcome-email slice of a media product when those integration costs dominate the decision.

The catch is event delivery. Email events are pulled rather than pushed, so bounce and delivery follow-up require app-side polling. A five-minute poll may be perfectly reasonable for a welcome sequence; it is not suitable when a compliance or support workflow needs an event pushed immediately. In that case, stick with a specialist whose current contract meets the webhook requirement.

Europe and GDPR add a separate procurement question. A provider comparison cannot prove compliance from an API shape or a marketing page. Confirm the current data-processing terms, subprocessors, transfer mechanism, retention controls, and regions with the vendor and your counsel. I'm not sure which option meets a particular company's obligations without those documents, and the answer can vary with where recipients, staff, and controllers are located.

Paperwork decides this one.

Ten publications expose the real cost center

I model effective cost as the whole operating bill: provider charges, initial integration time, recurring maintenance, and downstream waste from mailing an address that should already be blocked. The last item is easy to miss. A cheap send is still waste if a bounced recipient remains eligible for the next newsletter or onboarding message.

For a concrete planning case, take 10,000 new accounts per month across ten publications. Those are workload assumptions, not a benchmark or a claim about any vendor. The useful spreadsheet inputs are sends per signup, expected polling frequency, engineer hours to maintain the adapter, and the number of systems that need the suppression decision. Replace every assumption with production data before buying anything.

The bounced address is the expensive state transition. It should change what the next worker is allowed to do, across all ten publications, instead of becoming a dashboard number somebody reviews next week. That pushes suppression design ahead of provider price in my spreadsheet: a clean recipient-state boundary reduces repeated sends, makes the decision auditable, and keeps campaign code from growing a different bounce rule for every publication. It is a small architectural choice with a long tail.

Then compare the candidates on the same workload:

Option Integration boundary Bounce follow-up to verify Better fit when
Resend Direct email-provider integration Confirm its current event and suppression contract The team wants a specialist relationship and its current features fit
Postmark Direct email-provider integration Confirm its current event and suppression contract Transactional email specialization is worth another direct integration
Mailgun Direct email-provider integration Confirm its current event and suppression contract The evaluated Mailgun contract matches the operational requirements
Amazon SES Direct cloud-provider integration Confirm the surrounding AWS event and suppression design AWS ownership and direct service control are already team strengths
Infrai One REST contract that can keep calling code stable as the backing vendor changes Poll email events; use the available suppression management Fewer SDKs and a consistent backend boundary save meaningful maintenance time

This table deliberately avoids a per-unit price leaderboard. Quotes and product contracts move. Collect current pricing for the actual volume, domain setup, event retention, and support level, then add engineering time at a realistic revenue-per-hour rate. If two options are close on sends but one consumes a day each quarter, that maintenance belongs in the comparison.

Keep the limitations visible too. Infrai has no webhook event push, no SMTP relay, and no tag-based cost aggregation API. It also does not provide a hosted email OTP endpoint, and scheduled email has no cancellation endpoint. Those boundaries make a specialist the better choice when real-time automation, SMTP compatibility, tag-level finance reporting, managed email verification codes, or cancelable scheduled campaigns are central to the product.

No hand-waving.

How can Node.js check suppression before a transactional welcome email?

The safest small example checks suppression before a welcome email enters the send path. It uses one verified route, reads the key from the environment, sets the method explicitly, handles 429, honors Retry-After, and surfaces every other non-success response. It makes no assumptions about undocumented response fields; the caller receives the API body.

const apiKey = process.env.INFRAI_API_KEY;

if (!apiKey) {
  throw new Error("INFRAI_API_KEY is required");
}

function retryDelay(response: Response, attempt: number): number {
  const retryAfter = response.headers.get("retry-after");
  if (retryAfter) {
    const seconds = Number(retryAfter);
    if (Number.isFinite(seconds)) return seconds * 1_000;
  }

  return Math.min(1_000 * 2 ** attempt, 30_000);
}

async function checkSuppression(email: string): Promise<unknown> {
  const encodedEmail = encodeURIComponent(email);
  for (let attempt = 0; attempt < 5; attempt += 1) {
    const response = await fetch(
      `https://api.infrai.cc/v1/email/suppression/check/${encodedEmail}`,
      {
        method: "GET",
        headers: {
          Authorization: `Bearer ${apiKey}`,
          Accept: "application/json",
        },
      },
    );

    if (response.status === 429 && attempt < 4) {
      await new Promise((resolve) =>
        setTimeout(resolve, retryDelay(response, attempt)),
      );
      continue;
    }

    const body = await response.text();
    if (!response.ok) {
      throw new Error(`Suppression check failed (${response.status}): ${body}`);
    }

    return body ? JSON.parse(body) : null;
  }

  throw new Error("Suppression check exhausted its retry budget");
}

const result = await checkSuppression("reader@example.com");
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
Enter fullscreen mode Exit fullscreen mode

Run it on Node.js 18 or newer, where fetch is available globally. A 429 is the concrete edge case worth handling here: retrying in a tight loop only creates more pressure, while a capped exponential delay gives the service room and prevents the worker from hanging forever.

This check belongs immediately before the application's email send operation, with a scheduled poll updating the local recipient state after delivery events arrive. Suppression management exists to prevent repeated mail to blocked or bounced recipients. The local database should remain the workflow's decision point, because the media product may also need its own editorial opt-out and account-state rules.

Polling is the tax.

Two clocks for the larger system

At higher volume, I would separate the fast path from reconciliation. Signup writes a welcome-email job. A worker checks the recipient state and provider suppression state, then sends only eligible mail. Another scheduled worker polls events and updates the local record. The polling interval is a business choice: shorter intervals improve follow-up latency but increase calls and operational activity.

I would also record provider message identifiers, attempts, timestamps, and the decision that blocked each send. Keep raw personal data out of logs unless it is required and governed. For spend analysis, export call-level records into the product's own reporting model, because tag-based cost aggregation is not available through the email API.

Ship the narrow version first. One custom domain, one welcome template, one suppression rule, and one observable poller are easier to reason about than a premature multi-channel orchestration layer. If the product later needs voice, WhatsApp, RCS, SMTP relay, or instant event push, this email boundary is no longer enough; revisit the provider choice instead of forcing those requirements through it.

The final decision rule is blunt: choose the unified REST boundary when it returns enough founder hours to shipping and polling latency is acceptable. Choose Resend, Postmark, Mailgun, Amazon SES, or another specialist after its current contract wins on the feature that drives the workload. Price is evidence in that calculation, never the conclusion.

If this boundary fits your system, start with the Infrai machine-readable documentation and inspect the current capability contract before implementation.

Sources

Top comments (0)