docs/content/docs/infrastructure/services/email.mdx
The email service offers:
The email service is included in the @better-auth/infra package:
import { sendEmail, createEmailSender } from "@better-auth/infra";
import { sendEmail } from "@better-auth/infra";
await sendEmail({
template: "verify-email",
to: "[email protected]",
variables: {
verificationUrl: "https://yourapp.com/verify?token=abc123",
userEmail: "[email protected]",
userName: "John",
appName: "Your App",
},
});
import { createEmailSender } from "@better-auth/infra";
const emailSender = createEmailSender({
apiKey: process.env.BETTER_AUTH_API_KEY,
apiUrl: process.env.BETTER_AUTH_API_URL,
});
// Send multiple emails
await emailSender.send({
template: "reset-password",
to: "[email protected]",
variables: {
resetLink: "https://yourapp.com/reset?token=xyz",
userEmail: "[email protected]",
},
});
Sends an email verification link to new users.
await sendEmail({
template: "verify-email",
to: "[email protected]",
variables: {
verificationUrl: "https://yourapp.com/verify?token=abc",
userEmail: "[email protected]",
verificationCode: "123456", // Optional: for code-based verification
userName: "John", // Optional
appName: "Your App", // Optional
expirationMinutes: "60", // Optional
},
});
Sends a password reset link.
await sendEmail({
template: "reset-password",
to: "[email protected]",
variables: {
resetLink: "https://yourapp.com/reset?token=xyz",
userEmail: "[email protected]",
userName: "John", // Optional
appName: "Your App", // Optional
expirationMinutes: "60", // Optional
},
});
Confirms an email address change request.
await sendEmail({
template: "change-email",
to: "[email protected]",
variables: {
confirmationLink: "https://yourapp.com/confirm-email?token=abc",
newEmail: "[email protected]",
currentEmail: "[email protected]",
userName: "John", // Optional
appName: "Your App", // Optional
expirationMinutes: "60", // Optional
},
});
Sends a one-time password for passwordless sign-in.
await sendEmail({
template: "sign-in-otp",
to: "[email protected]",
variables: {
otpCode: "123456",
userEmail: "[email protected]",
appName: "Your App", // Optional
expirationMinutes: "10", // Optional
},
});
Sends an OTP code for email verification.
await sendEmail({
template: "verify-email-otp",
to: "[email protected]",
variables: {
otpCode: "123456",
userEmail: "[email protected]",
appName: "Your App", // Optional
expirationMinutes: "10", // Optional
},
});
Sends an OTP code for password reset.
await sendEmail({
template: "reset-password-otp",
to: "[email protected]",
variables: {
otpCode: "123456",
userEmail: "[email protected]",
appName: "Your App", // Optional
expirationMinutes: "10", // Optional
},
});
Sends a magic link for passwordless authentication.
await sendEmail({
template: "magic-link",
to: "[email protected]",
variables: {
magicLink: "https://yourapp.com/auth/magic?token=abc",
userEmail: "[email protected]",
appName: "Your App", // Optional
expirationMinutes: "15", // Optional
},
});
Sends a two-factor authentication code.
await sendEmail({
template: "two-factor",
to: "[email protected]",
variables: {
otpCode: "123456",
userEmail: "[email protected]",
userName: "John", // Optional
appName: "Your App", // Optional
expirationMinutes: "5", // Optional
},
});
Sends an organization invitation.
await sendEmail({
template: "invitation",
to: "[email protected]",
variables: {
inviteLink: "https://yourapp.com/invite?token=abc",
inviterName: "John Smith",
inviterEmail: "[email protected]",
organizationName: "Acme Corp",
role: "Member",
appName: "Your App", // Optional
expirationDays: "7", // Optional
},
});
Sends an application-level invitation (inviting users to the platform).
await sendEmail({
template: "application-invite",
to: "[email protected]",
variables: {
inviteLink: "https://yourapp.com/join?token=abc",
inviterName: "John Smith",
inviterEmail: "[email protected]",
inviteeEmail: "[email protected]",
appName: "Your App", // Optional
expirationDays: "7", // Optional
},
});
Sends account deletion confirmation.
await sendEmail({
template: "delete-account",
to: "[email protected]",
variables: {
deletionLink: "https://yourapp.com/confirm-delete?token=abc",
userEmail: "[email protected]",
userName: "John", // Optional
appName: "Your App", // Optional
expirationMinutes: "60", // Optional
},
});
Notifies a user that their dormant account was accessed.
await sendEmail({
template: "stale-account-user",
to: "[email protected]",
variables: {
userEmail: "[email protected]",
daysSinceLastActive: "90",
loginTime: "February 20, 2026, 3:45 PM UTC",
userName: "John", // Optional
appName: "Your App", // Optional
loginLocation: "New York, US", // Optional
loginDevice: "Chrome on Windows", // Optional
loginIp: "192.168.1.1", // Optional
},
});
Notifies an admin about dormant account reactivation.
await sendEmail({
template: "stale-account-admin",
to: "[email protected]",
variables: {
userEmail: "[email protected]",
userId: "user_123",
adminEmail: "[email protected]",
daysSinceLastActive: "90",
loginTime: "February 20, 2026, 3:45 PM UTC",
userName: "John", // Optional
appName: "Your App", // Optional
loginLocation: "New York, US", // Optional
loginDevice: "Chrome on Windows", // Optional
loginIp: "192.168.1.1", // Optional
},
});
interface EmailConfig {
apiKey?: string; // Your Better Auth Infrastructure API key
apiUrl?: string; // Custom API URL (optional)
}
The email service automatically reads from environment variables:
BETTER_AUTH_API_KEY=your_api_key_here
BETTER_AUTH_API_URL=https://api.betterauth.com # Optional
interface SendEmailResult {
success: boolean;
messageId?: string; // Email provider message ID
error?: string; // Error message if failed
}
const result = await sendEmail({
template: "verify-email",
to: "[email protected]",
variables: {
verificationUrl: "https://yourapp.com/verify?token=abc",
userEmail: "[email protected]",
},
});
if (result.success) {
console.log("Email sent:", result.messageId);
} else {
console.error("Failed to send email:", result.error);
}
| Feature | Starter | Pro | Business | Enterprise |
|---|---|---|---|---|
| Transactional Email | - | Yes | Yes | Yes |
Transactional email is available on Pro plans and above.
The email service integrates seamlessly with Better Auth's authentication flows. Here's a complete example:
import { betterAuth } from "better-auth";
import { organization } from "better-auth/plugins";
import { sendEmail } from "@better-auth/infra";
export const auth = betterAuth({
emailAndPassword: {
enabled: true,
async sendResetPassword({ user, url }) {
await sendEmail({
template: "reset-password",
to: user.email,
variables: {
resetLink: url,
userEmail: user.email,
userName: user.name,
appName: "Your App",
},
});
},
},
emailVerification: {
sendOnSignUp: true,
async sendVerificationEmail({ user, url }) {
await sendEmail({
template: "verify-email",
to: user.email,
variables: {
verificationUrl: url,
userEmail: user.email,
userName: user.name,
appName: "Your App",
},
});
},
},
plugins: [
organization({
async sendInvitationEmail(data) {
const inviteLink = `https://yourapp.com/accept-invitation/${data.id}`;
await sendEmail({
template: "invitation",
to: data.email,
variables: {
inviteLink,
inviterName: data.inviter.user.name,
inviterEmail: data.inviter.user.email,
organizationName: data.organization.name,
role: data.role,
appName: "Your App",
},
});
},
}),
],
});