Recipes
Worked patterns that combine Better Auth hooks with Payload, starting with creating an organization when a user signs up.
Patterns that go beyond configuration. Each one uses standard Better Auth APIs.
Create an organization on sign-up
Keep a lazy reference to the auth instance so database hooks can call auth.api.createOrganization(). Going through the API, rather than the adapter, makes Better Auth's organizationHooks fire.
Create an auth instance singleton
let authInstance: AuthInstance | null = null
export function setAuthInstance(auth: AuthInstance) { authInstance = auth }
export function getAuthInstance() {
if (!authInstance) throw new Error('Auth not initialized')
return authInstance
}Store it after creation
createBetterAuthPlugin({
createAuth: (payload) => {
const auth = betterAuth({ ...betterAuthOptions, database: payloadAdapter({ payloadClient: payload }) })
setAuthInstance(auth)
return auth
},
})Use it in database hooks
databaseHooks: {
user: {
update: {
after: async (user, ctx) => {
if (!user.emailVerified) return
const existing = await ctx?.context?.adapter?.findOne({
model: 'member', where: [{ field: 'userId', value: user.id }],
})
if (existing) return
const auth = getAuthInstance()
await auth.api.createOrganization({
body: { name: `${user.name}'s Workspace`, slug: generateSlug(user.name), userId: user.id },
})
},
},
},
}Always use auth.api.createOrganization() instead of raw adapter calls. The adapter bypasses organizationHooks entirely.
Only create social users who pass the role gate
See Social sign-in.
Plugin compatibility
Which Better Auth plugins work with the Payload adapter, which collections they generate, and how to add joins and cascade deletes.
Login, sign-up and password reset
The admin login the plugin injects, how it detects which sign-in methods to show, and the standalone forgot- and reset-password views.

