Import and configure the invite plugin in your Better Auth server configuration:
auth.ts
import { betterAuth } from "better-auth";import { admin } from "better-auth/plugins";import { invite } from "better-auth-invite-plugin";export const auth = betterAuth({ database: { // your database configuration }, plugins: [ // Admin plugin is recommended for role management admin({ roles: { user: { name: "user" }, admin: { name: "admin" } }, defaultRole: "user" }), invite({ // Where to redirect users after they accept an invite defaultRedirectAfterUpgrade: "/auth/invited", // Email sending function for private invites async sendUserInvitation({ email, role, url, token, newAccount, name }) { // Send the invitation email using your email service await sendEmail({ to: email, subject: newAccount ? `You're invited to join our platform!` : `Role upgrade invitation`, html: ` <p>Hi ${name || 'there'},</p> <p>${newAccount ? 'You have been invited to create an account.' : 'You have been invited to upgrade your role.'} </p> <p>Role: ${role}</p> <p><a href="${url}">Click here to accept</a></p> <p>Or use this code: ${token}</p> ` }); } }) ], emailAndPassword: { enabled: true }});
Replace sendEmail with your actual email sending function. Popular options include Resend, SendGrid, Nodemailer, or AWS SES.
Private invites are sent to a specific email address:
import { authClient } from "@/lib/auth-client";// User must be authenticated to create invitesconst { data, error } = await authClient.invite.create({ role: "admin", email: "[email protected]", // The email will be sent automatically via sendUserInvitation});if (error) { console.error("Failed to create invite:", error);} else { console.log("Invite sent successfully!"); // Response: { status: true, message: "The invitation was sent" }}
If the user clicks the email link, the invite is automatically activated via the callback URL. The token is stored in a cookie and will be consumed when they complete sign-up or sign-in.
You can also manually activate an invite using the API:
const { data, error } = await authClient.invite.activate({ token: "kx8f9j2l3m4n5p6q7r8s9t0u", callbackURL: "/auth/sign-up" // Optional: where to redirect});if (error) { console.error("Failed to activate invite:", error);} else { console.log("Invite activated!", data); // Cookie is now set with the invite token // Redirect user to sign up or sign in}
After activation, a cookie named {your-app-name}.invite-code is set in the user’s browser. This cookie will be automatically validated and consumed during authentication.
Create a welcome page at the redirect URL to show users their new role:
app/auth/invited/page.tsx
"use client";import { useSession } from "@/lib/auth-client";export default function InvitedPage() { const { data: session } = useSession(); return ( <div> <h1>Welcome to the platform!</h1> <p>You've successfully joined with the role: {session?.user.role}</p> <a href="/dashboard">Go to Dashboard</a> </div> );}