LogoRobo.js

Email delivery

Configure email mailers, templates, and notification events for authentication flows.

The plugin sends transactional emails for signup, sign-in, email verification, and password reset. Configure a mailer, customize templates per event, or disable specific notifications entirely.

Emails only send when emails.mailer is configured. Without it, all email notifications are silently skipped.

Setting up Resend

The plugin ships a first-party Resend integration:

config/plugins/robojs/auth.ts
import { createResendMailer } from '@robojs/auth/emails'
import type { AuthPluginOptions } from '@robojs/auth'

export default <AuthPluginOptions>{
	secret: process.env.AUTH_SECRET,
	emails: {
		from: { name: 'My App', address: 'noreply@myapp.com' },
		mailer: createResendMailer({
			apiKey: process.env.RESEND_API_KEY!
		})
	}
}
config/plugins/robojs/auth.js
import { createResendMailer } from '@robojs/auth/emails'

export default {
	secret: process.env.AUTH_SECRET,
	emails: {
		from: { name: 'My App', address: 'noreply@myapp.com' },
		mailer: createResendMailer({
			apiKey: process.env.RESEND_API_KEY!
		})
	}
}

Requires the resend npm package installed in your project.

Mailer options

Three ways to provide a mailer.

Direct instance:

import { createResendMailer } from '@robojs/auth/emails'

emails: {
	mailer: createResendMailer({ apiKey: '...' })
}
import { createResendMailer } from '@robojs/auth/emails'

emails: {
	mailer: createResendMailer({ apiKey: '...' })
}

Lazy factory:

emails: {
	mailer: async () => {
		const { createResendMailer } = await import('@robojs/auth/emails')
		return createResendMailer({ apiKey: process.env.RESEND_API_KEY! })
	}
}
emails: {
	mailer: async () => {
		const { createResendMailer } = await import('@robojs/auth/emails')
		return createResendMailer({ apiKey: process.env.RESEND_API_KEY! })
	}
}

Module spec (auto-imported at runtime):

emails: {
	mailer: { module: 'resend', export: 'Resend' }
}
emails: {
	mailer: { module: 'resend', export: 'Resend' }
}

If the mailer implements verify(), it's called during startup to validate the connection.

Custom mailer

Implement the AuthMailer interface:

import type { AuthMailer, MailMessage } from '@robojs/auth'

const customMailer: AuthMailer = {
	async send(message: MailMessage) {
		await myEmailService.send({
			to: typeof message.to === 'string' ? message.to : message.to.address,
			from: message.from,
			subject: message.subject,
			html: message.html,
			text: message.text
		})
	}
}

export default <AuthPluginOptions>{
	emails: { mailer: customMailer }
}

const customMailer = {
	async send(message) {
		await myEmailService.send({
			to: typeof message.to === 'string' ? message.to : message.to.address,
			from: message.from,
			subject: message.subject,
			html: message.html,
			text: message.text
		})
	}
}

export default {
	emails: { mailer: customMailer }
}

Email events

Six events trigger emails automatically:

EventWhen it firesDefault template
user:createdAfter signupWelcome email
session:createdOn new sign-in (non-new users only)Sign-in notification
email:verification-requestedWhen verification token is generatedVerification link
email:verifiedAfter email is confirmedConfirmation notice
password:reset-requestedWhen reset token is generatedReset link
password:reset-completedAfter password is changedCompletion notice

Template overrides

Override templates per event:

config/plugins/robojs/auth.ts
emails: {
	templates: {
		'user:created': {
			subject: (ctx) => `Welcome to ${ctx.appName}`,
			text: (ctx) => `Hi ${ctx.user.name ?? 'there'}, thanks for signing up.`,
			html: (ctx) => `<h1>Welcome</h1><p>Hi ${ctx.user.name ?? 'there'}</p>`
		},
		'session:created': false,  // disable login alerts
		'email:verified': false     // disable verification confirmation
	}
}
config/plugins/robojs/auth.js
emails: {
	templates: {
		'user:created': {
			subject: (ctx) => `Welcome to ${ctx.appName}`,
			text: (ctx) => `Hi ${ctx.user.name ?? 'there'}, thanks for signing up.`,
			html: (ctx) => `<h1>Welcome</h1><p>Hi ${ctx.user.name ?? 'there'}</p>`
		},
		'session:created': false,  // disable login alerts
		'email:verified': false     // disable verification confirmation
	}
}

Set a template to false to suppress that event.

EmailContext

Every template and trigger receives an EmailContext:

FieldTypeDescription
appNamestringPlugin's appName config value
user.idstringUser ID
user.emailstring | nullUser email
user.namestring | nullUser display name
session.idstring | nullSession ID
session.ipstring | nullClient IP address
session.userAgentstring | nullClient user agent
tokens.verifyEmailstring | undefinedRaw verification token
tokens.resetPasswordstring | undefinedRaw reset token
request.originstring | nullRequest origin
links.verifyEmailstring | undefinedFull verification URL
links.resetPasswordstring | undefinedFull reset URL

Custom triggers

Triggers are builder functions that run for specific events:

emails: {
	triggers: {
		'password:reset-requested': async (ctx) => ({
			to: ctx.user.email!,
			subject: 'Password Reset',
			html: `<p>Reset your password: <a href="${ctx.links?.resetPassword}">Click here</a></p>`
		}),
		'user:created': [
			async (ctx) => {
				await trackSignup(ctx.user.id)
				return null  // returning null skips sending from this builder
			},
			(ctx) => ({
				to: ctx.user.email!,
				subject: 'Welcome',
				html: '<p>Welcome aboard.</p>'
			})
		]
	}
}
emails: {
	triggers: {
		'password:reset-requested': async (ctx) => ({
			to: ctx.user.email!,
			subject: 'Password Reset',
			html: `<p>Reset your password: <a href="${ctx.links?.resetPassword}">Click here</a></p>`
		}),
		'user:created': [
			async (ctx) => {
				await trackSignup(ctx.user.id)
				return null  // returning null skips sending from this builder
			},
			(ctx) => ({
				to: ctx.user.email!,
				subject: 'Welcome',
				html: '<p>Welcome aboard.</p>'
			})
		]
	}
}

Triggers can be arrays. Each builder runs sequentially. Return null to skip sending. If both a trigger and a template exist for an event, triggers run first, then the template.

React Email templates

Use React components for rich templates:

import type { AuthPluginOptions } from '@robojs/auth'

export default <AuthPluginOptions>{
	emails: {
		mailer: createResendMailer({ apiKey: '...' }),
		templates: {
			'password:reset-requested': {
				subject: (ctx) => `Reset your ${ctx.appName} password`,
				react: (ctx) => <ResetEmail link={ctx.links?.resetPassword} user={ctx.user} />
			}
		}
	}
}

export default {
	emails: {
		mailer: createResendMailer({ apiKey: '...' }),
		templates: {
			'password:reset-requested': {
				subject: (ctx) => `Reset your ${ctx.appName} password`,
				react: (ctx) => <ResetEmail link={ctx.links?.resetPassword} user={ctx.user} />
			}
		}
	}
}

Requires @react-email/components peer dependency. Templates render via react-dom/server.

Provider templates

Use provider-driven templates (e.g., Resend template IDs):

templates: {
	'user:created': {
		templateId: 'tmpl_welcome_123',
		variables: (ctx) => ({
			name: ctx.user.name,
			verifyLink: ctx.links?.verifyEmail
		})
	}
}
templates: {
	'user:created': {
		templateId: 'tmpl_welcome_123',
		variables: (ctx) => ({
			name: ctx.user.name,
			verifyLink: ctx.links?.verifyEmail
		})
	}
}

Next steps

On this page