Email Flows
Default email verification, password reset, and change-email flows
Email Flows
The plugin ships default implementations for the three transactional email flows Better Auth supports. They use payload.sendEmail(...), so they pick up whatever Payload email adapter you configured (Resend, SMTP, etc.). Every default is shallow-merged with your own options, so anything you pass wins.
What is enabled by default
| Flow | Default behavior |
|---|---|
| Email verification on sign-up | sendOnSignUp: true, autoSignInAfterVerification: true |
| Email verification on sign-in | sendOnSignIn: true (when the user is not yet verified) |
| Password reset | emailAndPassword.sendResetPassword wired to payload.sendEmail |
| Change-email | user.changeEmail.enabled: true, sendChangeEmailVerification wired to payload.sendEmail (sent to the current email for approval) |
emailAndPassword.enabled is also set to true by default.
Overriding a flow
Pass your own implementation through betterAuth.emailVerification, betterAuth.emailAndPassword, or betterAuth.user.changeEmail. Your value replaces the default for that field.
import { betterAuthPlugin } from '@b3nab/payload-better-auth'
betterAuthPlugin({
betterAuth: {
emailVerification: {
sendOnSignUp: true,
autoSignInAfterVerification: false,
sendVerificationEmail: async ({ user, url }) => {
await myMailer.send({
to: user.email,
subject: 'Confirm your email',
html: renderTemplate('verify', { url }),
})
},
},
emailAndPassword: {
sendResetPassword: async ({ user, url }) => {
await myMailer.send({
to: user.email,
subject: 'Reset your password',
html: renderTemplate('reset', { url }),
})
},
},
user: {
changeEmail: {
sendChangeEmailVerification: async ({ user, newEmail, url }) => {
await myMailer.send({
to: user.email,
subject: `Approve change to ${newEmail}`,
html: renderTemplate('change-email', { url, newEmail }),
})
},
},
},
},
})Notes
- The default handlers swallow errors via the plugin logger and log the verification URL/token to the console so you are never locked out during local development. In production you should provide your own implementations that surface errors properly.
payload.sendEmailrequires a Payload email adapter to be configured. Without one, the default handlers will throw.- See the Better Auth email verification docs for the full option surface (template tokens, expiry, etc.).