Radon

Senders

Deliver auth email through Resend, SendGrid, Postmark, or AWS SES — or chain them for failover, or write your own in a few lines.

A sender is how transactional email leaves your app — verification codes, magic links, password-reset links. Any provider that emails users takes one. Senders are separate import paths, so you only bundle the one you use, and the HTTP-based ones talk to their API over fetchno SDK to install.

import { resendSender } from "@radonsdk/auth/senders/resend";

const sender = resendSender({ apiKey: process.env.RESEND_API_KEY!, from: "Acme <auth@acme.com>" });
// then pass `sender` to any email provider:
providers: { emailCode: { sender }, magicLink: { sender, baseUrl } }

Shared options

Every built-in sender accepts from (default From address) and replyTo. from is required unless every message sets its own. You can also inject a custom fetch for proxies/tests.

Resend

The default. Get a key at resend.com and verify a sending domain.

import { resendSender } from "@radonsdk/auth/senders/resend";

resendSender({
  apiKey: process.env.RESEND_API_KEY!,   // required, starts with "re_"
  from: "Acme <auth@acme.com>",
  replyTo: "support@acme.com",           // optional
});

SendGrid

import { sendgridSender } from "@radonsdk/auth/senders/sendgrid";

sendgridSender({
  apiKey: process.env.SENDGRID_API_KEY!, // required
  from: "auth@acme.com",
});

Postmark

import { postmarkSender } from "@radonsdk/auth/senders/postmark";

postmarkSender({
  serverToken: process.env.POSTMARK_TOKEN!, // required (Server API token)
  from: "auth@acme.com",
  messageStream: "outbound",                // optional; defaults to "outbound"
});

AWS SES

SES uses SigV4 request signing, so this is the one sender that needs a package:

npm install @aws-sdk/client-sesv2
import { sesSender } from "@radonsdk/auth/senders/ses";

// Uses the AWS default credential chain:
sesSender({ region: "us-east-1", from: "auth@acme.com" });

// …or inject a pre-configured client:
import { SESv2Client } from "@aws-sdk/client-sesv2";
sesSender({ client: new SESv2Client({ region: "us-east-1" }), from: "auth@acme.com" });
OptionDescription
regionAWS region — used to build a client if client isn't given.
clientAn existing SESv2Client. If set, region/credentials are ignored.
credentialsExplicit { accessKeyId, secretAccessKey, sessionToken? }. Otherwise the default chain is used.

Failover across providers

Wrap several senders in multiSender. It tries each in order and returns the first success — so a primary outage doesn't stop your auth email.

import { multiSender } from "@radonsdk/auth";
import { resendSender } from "@radonsdk/auth/senders/resend";
import { sendgridSender } from "@radonsdk/auth/senders/sendgrid";

const sender = multiSender(
  [
    resendSender({ apiKey: process.env.RESEND_API_KEY!, from: "auth@acme.com" }),
    sendgridSender({ apiKey: process.env.SENDGRID_API_KEY!, from: "auth@acme.com" }),
  ],
  { onError: (err, i) => console.warn(`sender ${i} failed`, err) },
);

If every sender fails, multiSender throws an EmailSendError aggregating the failures.

Write your own

A sender is one method. Implement EmailSender to use any transport — SMTP, an internal service, whatever:

import type { EmailSender } from "@radonsdk/auth";

const smtpSender: EmailSender = {
  async send({ to, subject, html, text, from }) {
    await myTransport.sendMail({ to, from, subject, html, text });
    return { provider: "smtp" }; // optionally include an `id`
  },
};

providers: { emailCode: { sender: smtpSender } };

The send method receives { to, subject, html?, text?, from?, replyTo? } and returns { provider: string, id?: string }.

Next steps

On this page