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 fetch — no 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-sesv2import { 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" });| Option | Description |
|---|---|
region | AWS region — used to build a client if client isn't given. |
client | An existing SESv2Client. If set, region/credentials are ignored. |
credentials | Explicit { 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
Adapters
Store Radon's data in any database — MongoDB, PostgreSQL, MySQL, SQLite, Prisma, Supabase, or Firebase. One small contract; swap freely.
Radon Pro
Unlock 50 OAuth providers, passkeys, 2FA, phone OTP, refresh tokens, multi-device sessions, API keys, orgs, and six more framework integrations with a one-time license.