Private packages

@onyx/mail

Transactional email with React Email templates delivered through Resend.

@onyx/mail owns every transactional email the platform sends. It ships React Email templates for the welcome, email verification, password reset, organization invitation, invitation reminder, and notification digest emails, and wraps them behind one typed factory, createMailer. When no Resend API key is configured (typical in local dev), emails are logged to the console with their action link instead of sent, so link-based flows like password reset still work end to end.

Creating a mailer

The package has a single entry point. createMailer(options) returns a Mailer, and the Mailer type is useful for passing the mailer into other packages (for example @onyx/auth accepts one):

import { createMailer } from '@onyx/mail';
import { env } from 'cloudflare:workers';

const mailer = createMailer({
  apiKey: env.RESEND_API_KEY,
  from: env.MAIL_FROM,
});

apiKey is optional. If it is omitted, every send becomes a [mail] console log that includes the email's primary action URL.

apps/web already creates a mailer this way and hands it to the auth layer, which sends the welcome, verification, reset, and invitation emails at the right lifecycle moments. Job handlers also receive a ready-made mailer in their JobContext (see @onyx/jobs).

Sender methods

Each method takes a typed options object and renders the matching template:

await mailer.sendWelcomeEmail({ to, name, appLink });
await mailer.sendVerificationEmail({ to, name, verifyLink });
await mailer.sendResetPassword({ to, name, resetLink });
await mailer.sendOrganizationInvitation({ to, inviterName, organizationName, inviteLink });
await mailer.sendInvitationReminder({ to, inviterName, organizationName, inviteLink });
await mailer.sendNotificationDigest({ to, organizationName, items, appLink });

sendNotificationDigest is called by @onyx/notifications when a user's email outbox flushes, so a burst of activity lands as one digest email. Its items are NotificationDigestItem values (also exported from the package); it is a no-op when items is empty, and uses the single item's title as the subject when there is exactly one.

A failed Resend delivery throws, so callers decide whether a send failure should fail the surrounding request.

Configuration

NameKindPurpose
RESEND_API_KEYsecretResend API key. Optional locally; without it emails are logged, not sent.
MAIL_FROMvar in wrangler.jsoncSender address, e.g. Onyx <hello@yourdomain.com>.

In production MAIL_FROM must use a Resend-verified domain. The default onboarding@resend.dev only delivers to the Resend account owner.

On this page