---
title: Email Service
description: Better Auth Infrastructure provides a managed transactional email service with pre-built templates for common authentication flows. Send verification emails, password resets, invitations, and more without managing email infrastructure.
---

## Overview

The email service offers:

* Pre-built, professionally designed email templates
* Multiple provider support (AWS SES, SendGrid, Resend)
* Type-safe template variables
* No infrastructure to manage
* Deliverability optimization

## Installation

The email service is included in the `@better-auth/infra` package:

```ts
import { sendEmail, createEmailSender } from "@better-auth/infra";
```

## Quick Start

### Send a Single Email

```ts
import { sendEmail } from "@better-auth/infra";

await sendEmail({
  template: "verify-email",
  to: "user@example.com",
  variables: {
    verificationUrl: "https://yourapp.com/verify?token=abc123",
    userEmail: "user@example.com",
    userName: "John",
    appName: "Your App",
  },
});
```

### Create a Reusable Sender

```ts
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: "user@example.com",
  variables: {
    resetLink: "https://yourapp.com/reset?token=xyz",
    userEmail: "user@example.com",
  },
});
```

## Available Templates

### verify-email

Sends an email verification link to new users.

```ts
await sendEmail({
  template: "verify-email",
  to: "user@example.com",
  variables: {
    verificationUrl: "https://yourapp.com/verify?token=abc",
    userEmail: "user@example.com",
    verificationCode: "123456",        // Optional: for code-based verification
    userName: "John",                   // Optional
    appName: "Your App",               // Optional
    expirationMinutes: "60",           // Optional
  },
});
```

### reset-password

Sends a password reset link.

```ts
await sendEmail({
  template: "reset-password",
  to: "user@example.com",
  variables: {
    resetLink: "https://yourapp.com/reset?token=xyz",
    userEmail: "user@example.com",
    userName: "John",                   // Optional
    appName: "Your App",               // Optional
    expirationMinutes: "60",           // Optional
  },
});
```

### change-email

Confirms an email address change request.

```ts
await sendEmail({
  template: "change-email",
  to: "newemail@example.com",
  variables: {
    confirmationLink: "https://yourapp.com/confirm-email?token=abc",
    newEmail: "newemail@example.com",
    currentEmail: "oldemail@example.com",
    userName: "John",                   // Optional
    appName: "Your App",               // Optional
    expirationMinutes: "60",           // Optional
  },
});
```

### sign-in-otp

Sends a one-time password for passwordless sign-in.

```ts
await sendEmail({
  template: "sign-in-otp",
  to: "user@example.com",
  variables: {
    otpCode: "123456",
    userEmail: "user@example.com",
    appName: "Your App",               // Optional
    expirationMinutes: "10",           // Optional
  },
});
```

### verify-email-otp

Sends an OTP code for email verification.

```ts
await sendEmail({
  template: "verify-email-otp",
  to: "user@example.com",
  variables: {
    otpCode: "123456",
    userEmail: "user@example.com",
    appName: "Your App",               // Optional
    expirationMinutes: "10",           // Optional
  },
});
```

### reset-password-otp

Sends an OTP code for password reset.

```ts
await sendEmail({
  template: "reset-password-otp",
  to: "user@example.com",
  variables: {
    otpCode: "123456",
    userEmail: "user@example.com",
    appName: "Your App",               // Optional
    expirationMinutes: "10",           // Optional
  },
});
```

### magic-link

Sends a magic link for passwordless authentication.

```ts
await sendEmail({
  template: "magic-link",
  to: "user@example.com",
  variables: {
    magicLink: "https://yourapp.com/auth/magic?token=abc",
    userEmail: "user@example.com",
    appName: "Your App",               // Optional
    expirationMinutes: "15",           // Optional
  },
});
```

### two-factor

Sends a two-factor authentication code.

```ts
await sendEmail({
  template: "two-factor",
  to: "user@example.com",
  variables: {
    otpCode: "123456",
    userEmail: "user@example.com",
    userName: "John",                   // Optional
    appName: "Your App",               // Optional
    expirationMinutes: "5",            // Optional
  },
});
```

### invitation

Sends an organization invitation.

```ts
await sendEmail({
  template: "invitation",
  to: "newmember@example.com",
  variables: {
    inviteLink: "https://yourapp.com/invite?token=abc",
    inviterName: "John Smith",
    inviterEmail: "john@company.com",
    organizationName: "Acme Corp",
    role: "Member",
    appName: "Your App",               // Optional
    expirationDays: "7",               // Optional
  },
});
```

### application-invite

Sends an application-level invitation (inviting users to the platform).

```ts
await sendEmail({
  template: "application-invite",
  to: "newuser@example.com",
  variables: {
    inviteLink: "https://yourapp.com/join?token=abc",
    inviterName: "John Smith",
    inviterEmail: "john@company.com",
    inviteeEmail: "newuser@example.com",
    appName: "Your App",               // Optional
    expirationDays: "7",               // Optional
  },
});
```

### delete-account

Sends account deletion confirmation.

```ts
await sendEmail({
  template: "delete-account",
  to: "user@example.com",
  variables: {
    deletionLink: "https://yourapp.com/confirm-delete?token=abc",
    userEmail: "user@example.com",
    userName: "John",                   // Optional
    appName: "Your App",               // Optional
    expirationMinutes: "60",           // Optional
  },
});
```

### stale-account-user

Notifies a user that their dormant account was accessed.

```ts
await sendEmail({
  template: "stale-account-user",
  to: "user@example.com",
  variables: {
    userEmail: "user@example.com",
    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
  },
});
```

### stale-account-admin

Notifies an admin about dormant account reactivation.

```ts
await sendEmail({
  template: "stale-account-admin",
  to: "admin@yourapp.com",
  variables: {
    userEmail: "user@example.com",
    userId: "user_123",
    adminEmail: "admin@yourapp.com",
    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
  },
});
```

## Configuration

### EmailConfig

```ts
interface EmailConfig {
  apiKey?: string;   // Your Better Auth Infrastructure API key
  apiUrl?: string;   // Custom API URL (optional)
}
```

### Environment Variables

The email service automatically reads from environment variables:

```dotenv
BETTER_AUTH_API_KEY=your_api_key_here
BETTER_AUTH_API_URL=https://api.betterauth.com  # Optional
```

## Response Format

### SendEmailResult

```ts
interface SendEmailResult {
  success: boolean;
  messageId?: string;  // Email provider message ID
  error?: string;      // Error message if failed
}
```

### Example Usage

```ts
const result = await sendEmail({
  template: "verify-email",
  to: "user@example.com",
  variables: {
    verificationUrl: "https://yourapp.com/verify?token=abc",
    userEmail: "user@example.com",
  },
});

if (result.success) {
  console.log("Email sent:", result.messageId);
} else {
  console.error("Failed to send email:", result.error);
}
```

## Plan Requirements

| Feature             | Starter | Pro | Business | Enterprise |
| ------------------- | ------- | --- | -------- | ---------- |
| Transactional Email | -       | Yes | Yes      | Yes        |

Transactional email is available on Pro plans and above.

## Integration with Better Auth

The email service integrates seamlessly with Better Auth's authentication flows. Here's a complete example:

```ts title="auth.ts"
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",
          },
        });
      },
    }),
  ],
});
```

<Callout type="warn">
  Avoid awaiting the email sending in production to prevent timing attacks. On serverless platforms, use `waitUntil` or similar to ensure the email is sent without blocking the response.
</Callout>
