#
tokens: 40851/50000 1/1092 files (page 65/67)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 65 of 67. Use http://codebase.md/better-auth/better-auth?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .gitattributes
├── .github
│   ├── CODEOWNERS
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE
│   │   ├── bug_report.yml
│   │   └── feature_request.yml
│   ├── renovate.json5
│   └── workflows
│       ├── ci.yml
│       ├── e2e.yml
│       ├── preview.yml
│       └── release.yml
├── .gitignore
├── .npmrc
├── .nvmrc
├── .vscode
│   └── settings.json
├── banner-dark.png
├── banner.png
├── biome.json
├── bump.config.ts
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── demo
│   ├── expo-example
│   │   ├── .env.example
│   │   ├── .gitignore
│   │   ├── app.config.ts
│   │   ├── assets
│   │   │   ├── bg-image.jpeg
│   │   │   ├── fonts
│   │   │   │   └── SpaceMono-Regular.ttf
│   │   │   ├── icon.png
│   │   │   └── images
│   │   │       ├── adaptive-icon.png
│   │   │       ├── favicon.png
│   │   │       ├── logo.png
│   │   │       ├── partial-react-logo.png
│   │   │       ├── react-logo.png
│   │   │       ├── [email protected]
│   │   │       ├── [email protected]
│   │   │       └── splash.png
│   │   ├── babel.config.js
│   │   ├── components.json
│   │   ├── expo-env.d.ts
│   │   ├── index.ts
│   │   ├── metro.config.js
│   │   ├── nativewind-env.d.ts
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── src
│   │   │   ├── app
│   │   │   │   ├── _layout.tsx
│   │   │   │   ├── api
│   │   │   │   │   └── auth
│   │   │   │   │       └── [...route]+api.ts
│   │   │   │   ├── dashboard.tsx
│   │   │   │   ├── forget-password.tsx
│   │   │   │   ├── index.tsx
│   │   │   │   └── sign-up.tsx
│   │   │   ├── components
│   │   │   │   ├── icons
│   │   │   │   │   └── google.tsx
│   │   │   │   └── ui
│   │   │   │       ├── avatar.tsx
│   │   │   │       ├── button.tsx
│   │   │   │       ├── card.tsx
│   │   │   │       ├── dialog.tsx
│   │   │   │       ├── input.tsx
│   │   │   │       ├── separator.tsx
│   │   │   │       └── text.tsx
│   │   │   ├── global.css
│   │   │   └── lib
│   │   │       ├── auth-client.ts
│   │   │       ├── auth.ts
│   │   │       ├── icons
│   │   │       │   ├── iconWithClassName.ts
│   │   │       │   └── X.tsx
│   │   │       └── utils.ts
│   │   ├── tailwind.config.js
│   │   └── tsconfig.json
│   └── nextjs
│       ├── .env.example
│       ├── .gitignore
│       ├── app
│       │   ├── (auth)
│       │   │   ├── forget-password
│       │   │   │   └── page.tsx
│       │   │   ├── reset-password
│       │   │   │   └── page.tsx
│       │   │   ├── sign-in
│       │   │   │   ├── loading.tsx
│       │   │   │   └── page.tsx
│       │   │   └── two-factor
│       │   │       ├── otp
│       │   │       │   └── page.tsx
│       │   │       └── page.tsx
│       │   ├── accept-invitation
│       │   │   └── [id]
│       │   │       ├── invitation-error.tsx
│       │   │       └── page.tsx
│       │   ├── admin
│       │   │   └── page.tsx
│       │   ├── api
│       │   │   └── auth
│       │   │       └── [...all]
│       │   │           └── route.ts
│       │   ├── apps
│       │   │   └── register
│       │   │       └── page.tsx
│       │   ├── client-test
│       │   │   └── page.tsx
│       │   ├── dashboard
│       │   │   ├── change-plan.tsx
│       │   │   ├── client.tsx
│       │   │   ├── organization-card.tsx
│       │   │   ├── page.tsx
│       │   │   ├── upgrade-button.tsx
│       │   │   └── user-card.tsx
│       │   ├── device
│       │   │   ├── approve
│       │   │   │   └── page.tsx
│       │   │   ├── denied
│       │   │   │   └── page.tsx
│       │   │   ├── layout.tsx
│       │   │   ├── page.tsx
│       │   │   └── success
│       │   │       └── page.tsx
│       │   ├── favicon.ico
│       │   ├── features.tsx
│       │   ├── fonts
│       │   │   ├── GeistMonoVF.woff
│       │   │   └── GeistVF.woff
│       │   ├── globals.css
│       │   ├── layout.tsx
│       │   ├── oauth
│       │   │   └── authorize
│       │   │       ├── concet-buttons.tsx
│       │   │       └── page.tsx
│       │   ├── page.tsx
│       │   └── pricing
│       │       └── page.tsx
│       ├── components
│       │   ├── account-switch.tsx
│       │   ├── blocks
│       │   │   └── pricing.tsx
│       │   ├── logo.tsx
│       │   ├── one-tap.tsx
│       │   ├── sign-in-btn.tsx
│       │   ├── sign-in.tsx
│       │   ├── sign-up.tsx
│       │   ├── theme-provider.tsx
│       │   ├── theme-toggle.tsx
│       │   ├── tier-labels.tsx
│       │   ├── ui
│       │   │   ├── accordion.tsx
│       │   │   ├── alert-dialog.tsx
│       │   │   ├── alert.tsx
│       │   │   ├── aspect-ratio.tsx
│       │   │   ├── avatar.tsx
│       │   │   ├── badge.tsx
│       │   │   ├── breadcrumb.tsx
│       │   │   ├── button.tsx
│       │   │   ├── calendar.tsx
│       │   │   ├── card.tsx
│       │   │   ├── carousel.tsx
│       │   │   ├── chart.tsx
│       │   │   ├── checkbox.tsx
│       │   │   ├── collapsible.tsx
│       │   │   ├── command.tsx
│       │   │   ├── context-menu.tsx
│       │   │   ├── copy-button.tsx
│       │   │   ├── dialog.tsx
│       │   │   ├── drawer.tsx
│       │   │   ├── dropdown-menu.tsx
│       │   │   ├── form.tsx
│       │   │   ├── hover-card.tsx
│       │   │   ├── input-otp.tsx
│       │   │   ├── input.tsx
│       │   │   ├── label.tsx
│       │   │   ├── menubar.tsx
│       │   │   ├── navigation-menu.tsx
│       │   │   ├── pagination.tsx
│       │   │   ├── password-input.tsx
│       │   │   ├── popover.tsx
│       │   │   ├── progress.tsx
│       │   │   ├── radio-group.tsx
│       │   │   ├── resizable.tsx
│       │   │   ├── scroll-area.tsx
│       │   │   ├── select.tsx
│       │   │   ├── separator.tsx
│       │   │   ├── sheet.tsx
│       │   │   ├── skeleton.tsx
│       │   │   ├── slider.tsx
│       │   │   ├── sonner.tsx
│       │   │   ├── switch.tsx
│       │   │   ├── table.tsx
│       │   │   ├── tabs.tsx
│       │   │   ├── tabs2.tsx
│       │   │   ├── textarea.tsx
│       │   │   ├── toast.tsx
│       │   │   ├── toaster.tsx
│       │   │   ├── toggle-group.tsx
│       │   │   ├── toggle.tsx
│       │   │   └── tooltip.tsx
│       │   └── wrapper.tsx
│       ├── components.json
│       ├── hooks
│       │   └── use-toast.ts
│       ├── lib
│       │   ├── auth-client.ts
│       │   ├── auth-types.ts
│       │   ├── auth.ts
│       │   ├── email
│       │   │   ├── invitation.tsx
│       │   │   ├── resend.ts
│       │   │   └── reset-password.tsx
│       │   ├── metadata.ts
│       │   ├── shared.ts
│       │   └── utils.ts
│       ├── next.config.ts
│       ├── package.json
│       ├── postcss.config.mjs
│       ├── proxy.ts
│       ├── public
│       │   ├── __og.png
│       │   ├── _og.png
│       │   ├── favicon
│       │   │   ├── android-chrome-192x192.png
│       │   │   ├── android-chrome-512x512.png
│       │   │   ├── apple-touch-icon.png
│       │   │   ├── favicon-16x16.png
│       │   │   ├── favicon-32x32.png
│       │   │   ├── favicon.ico
│       │   │   ├── light
│       │   │   │   ├── android-chrome-192x192.png
│       │   │   │   ├── android-chrome-512x512.png
│       │   │   │   ├── apple-touch-icon.png
│       │   │   │   ├── favicon-16x16.png
│       │   │   │   ├── favicon-32x32.png
│       │   │   │   ├── favicon.ico
│       │   │   │   └── site.webmanifest
│       │   │   └── site.webmanifest
│       │   ├── logo.svg
│       │   └── og.png
│       ├── README.md
│       ├── tailwind.config.ts
│       ├── tsconfig.json
│       └── turbo.json
├── docker-compose.yml
├── docs
│   ├── .env.example
│   ├── .gitignore
│   ├── app
│   │   ├── api
│   │   │   ├── ai-chat
│   │   │   │   └── route.ts
│   │   │   ├── analytics
│   │   │   │   ├── conversation
│   │   │   │   │   └── route.ts
│   │   │   │   ├── event
│   │   │   │   │   └── route.ts
│   │   │   │   └── feedback
│   │   │   │       └── route.ts
│   │   │   ├── chat
│   │   │   │   └── route.ts
│   │   │   ├── og
│   │   │   │   └── route.tsx
│   │   │   ├── og-release
│   │   │   │   └── route.tsx
│   │   │   ├── search
│   │   │   │   └── route.ts
│   │   │   └── support
│   │   │       └── route.ts
│   │   ├── blog
│   │   │   ├── _components
│   │   │   │   ├── _layout.tsx
│   │   │   │   ├── blog-list.tsx
│   │   │   │   ├── changelog-layout.tsx
│   │   │   │   ├── default-changelog.tsx
│   │   │   │   ├── fmt-dates.tsx
│   │   │   │   ├── icons.tsx
│   │   │   │   ├── stat-field.tsx
│   │   │   │   └── support.tsx
│   │   │   ├── [[...slug]]
│   │   │   │   └── page.tsx
│   │   │   └── layout.tsx
│   │   ├── changelogs
│   │   │   ├── _components
│   │   │   │   ├── _layout.tsx
│   │   │   │   ├── changelog-layout.tsx
│   │   │   │   ├── default-changelog.tsx
│   │   │   │   ├── fmt-dates.tsx
│   │   │   │   ├── grid-pattern.tsx
│   │   │   │   ├── icons.tsx
│   │   │   │   └── stat-field.tsx
│   │   │   ├── [[...slug]]
│   │   │   │   └── page.tsx
│   │   │   └── layout.tsx
│   │   ├── community
│   │   │   ├── _components
│   │   │   │   ├── header.tsx
│   │   │   │   └── stats.tsx
│   │   │   └── page.tsx
│   │   ├── docs
│   │   │   ├── [[...slug]]
│   │   │   │   ├── page.client.tsx
│   │   │   │   └── page.tsx
│   │   │   ├── layout.tsx
│   │   │   └── lib
│   │   │       └── get-llm-text.ts
│   │   ├── global.css
│   │   ├── layout.config.tsx
│   │   ├── layout.tsx
│   │   ├── llms.txt
│   │   │   ├── [...slug]
│   │   │   │   └── route.ts
│   │   │   └── route.ts
│   │   ├── not-found.tsx
│   │   ├── page.tsx
│   │   ├── reference
│   │   │   └── route.ts
│   │   ├── sitemap.xml
│   │   ├── static.json
│   │   │   └── route.ts
│   │   └── v1
│   │       ├── _components
│   │       │   └── v1-text.tsx
│   │       ├── bg-line.tsx
│   │       └── page.tsx
│   ├── assets
│   │   ├── Geist.ttf
│   │   └── GeistMono.ttf
│   ├── components
│   │   ├── ai-chat-modal.tsx
│   │   ├── anchor-scroll-fix.tsx
│   │   ├── api-method-tabs.tsx
│   │   ├── api-method.tsx
│   │   ├── banner.tsx
│   │   ├── blocks
│   │   │   └── features.tsx
│   │   ├── builder
│   │   │   ├── beam.tsx
│   │   │   ├── code-tabs
│   │   │   │   ├── code-editor.tsx
│   │   │   │   ├── code-tabs.tsx
│   │   │   │   ├── index.tsx
│   │   │   │   ├── tab-bar.tsx
│   │   │   │   └── theme.ts
│   │   │   ├── index.tsx
│   │   │   ├── sign-in.tsx
│   │   │   ├── sign-up.tsx
│   │   │   ├── social-provider.tsx
│   │   │   ├── store.ts
│   │   │   └── tabs.tsx
│   │   ├── display-techstack.tsx
│   │   ├── divider-text.tsx
│   │   ├── docs
│   │   │   ├── docs.client.tsx
│   │   │   ├── docs.tsx
│   │   │   ├── layout
│   │   │   │   ├── nav.tsx
│   │   │   │   ├── theme-toggle.tsx
│   │   │   │   ├── toc-thumb.tsx
│   │   │   │   └── toc.tsx
│   │   │   ├── page.client.tsx
│   │   │   ├── page.tsx
│   │   │   ├── shared.tsx
│   │   │   └── ui
│   │   │       ├── button.tsx
│   │   │       ├── collapsible.tsx
│   │   │       ├── popover.tsx
│   │   │       └── scroll-area.tsx
│   │   ├── endpoint.tsx
│   │   ├── features.tsx
│   │   ├── floating-ai-search.tsx
│   │   ├── fork-button.tsx
│   │   ├── generate-apple-jwt.tsx
│   │   ├── generate-secret.tsx
│   │   ├── github-stat.tsx
│   │   ├── icons.tsx
│   │   ├── landing
│   │   │   ├── gradient-bg.tsx
│   │   │   ├── grid-pattern.tsx
│   │   │   ├── hero.tsx
│   │   │   ├── section-svg.tsx
│   │   │   ├── section.tsx
│   │   │   ├── spotlight.tsx
│   │   │   └── testimonials.tsx
│   │   ├── logo-context-menu.tsx
│   │   ├── logo.tsx
│   │   ├── markdown-renderer.tsx
│   │   ├── markdown.tsx
│   │   ├── mdx
│   │   │   ├── add-to-cursor.tsx
│   │   │   └── database-tables.tsx
│   │   ├── message-feedback.tsx
│   │   ├── mobile-search-icon.tsx
│   │   ├── nav-bar.tsx
│   │   ├── nav-link.tsx
│   │   ├── nav-mobile.tsx
│   │   ├── promo-card.tsx
│   │   ├── resource-card.tsx
│   │   ├── resource-grid.tsx
│   │   ├── resource-section.tsx
│   │   ├── ripple.tsx
│   │   ├── search-dialog.tsx
│   │   ├── side-bar.tsx
│   │   ├── sidebar-content.tsx
│   │   ├── techstack-icons.tsx
│   │   ├── theme-provider.tsx
│   │   ├── theme-toggler.tsx
│   │   └── ui
│   │       ├── accordion.tsx
│   │       ├── alert-dialog.tsx
│   │       ├── alert.tsx
│   │       ├── aside-link.tsx
│   │       ├── aspect-ratio.tsx
│   │       ├── avatar.tsx
│   │       ├── background-beams.tsx
│   │       ├── background-boxes.tsx
│   │       ├── badge.tsx
│   │       ├── breadcrumb.tsx
│   │       ├── button.tsx
│   │       ├── calendar.tsx
│   │       ├── callout.tsx
│   │       ├── card.tsx
│   │       ├── carousel.tsx
│   │       ├── chart.tsx
│   │       ├── checkbox.tsx
│   │       ├── code-block.tsx
│   │       ├── collapsible.tsx
│   │       ├── command.tsx
│   │       ├── context-menu.tsx
│   │       ├── dialog.tsx
│   │       ├── drawer.tsx
│   │       ├── dropdown-menu.tsx
│   │       ├── dynamic-code-block.tsx
│   │       ├── fade-in.tsx
│   │       ├── form.tsx
│   │       ├── hover-card.tsx
│   │       ├── input-otp.tsx
│   │       ├── input.tsx
│   │       ├── label.tsx
│   │       ├── menubar.tsx
│   │       ├── navigation-menu.tsx
│   │       ├── pagination.tsx
│   │       ├── popover.tsx
│   │       ├── progress.tsx
│   │       ├── radio-group.tsx
│   │       ├── resizable.tsx
│   │       ├── scroll-area.tsx
│   │       ├── select.tsx
│   │       ├── separator.tsx
│   │       ├── sheet.tsx
│   │       ├── sidebar.tsx
│   │       ├── skeleton.tsx
│   │       ├── slider.tsx
│   │       ├── sonner.tsx
│   │       ├── sparkles.tsx
│   │       ├── switch.tsx
│   │       ├── table.tsx
│   │       ├── tabs.tsx
│   │       ├── textarea.tsx
│   │       ├── toggle-group.tsx
│   │       ├── toggle.tsx
│   │       ├── tooltip-docs.tsx
│   │       ├── tooltip.tsx
│   │       └── use-copy-button.tsx
│   ├── components.json
│   ├── content
│   │   ├── blogs
│   │   │   ├── 0-supabase-auth-to-planetscale-migration.mdx
│   │   │   ├── 1-3.mdx
│   │   │   ├── authjs-joins-better-auth.mdx
│   │   │   └── seed-round.mdx
│   │   ├── changelogs
│   │   │   ├── 1-2.mdx
│   │   │   └── 1.0.mdx
│   │   └── docs
│   │       ├── adapters
│   │       │   ├── community-adapters.mdx
│   │       │   ├── drizzle.mdx
│   │       │   ├── mongo.mdx
│   │       │   ├── mssql.mdx
│   │       │   ├── mysql.mdx
│   │       │   ├── other-relational-databases.mdx
│   │       │   ├── postgresql.mdx
│   │       │   ├── prisma.mdx
│   │       │   └── sqlite.mdx
│   │       ├── authentication
│   │       │   ├── apple.mdx
│   │       │   ├── atlassian.mdx
│   │       │   ├── cognito.mdx
│   │       │   ├── discord.mdx
│   │       │   ├── dropbox.mdx
│   │       │   ├── email-password.mdx
│   │       │   ├── facebook.mdx
│   │       │   ├── figma.mdx
│   │       │   ├── github.mdx
│   │       │   ├── gitlab.mdx
│   │       │   ├── google.mdx
│   │       │   ├── huggingface.mdx
│   │       │   ├── kakao.mdx
│   │       │   ├── kick.mdx
│   │       │   ├── line.mdx
│   │       │   ├── linear.mdx
│   │       │   ├── linkedin.mdx
│   │       │   ├── microsoft.mdx
│   │       │   ├── naver.mdx
│   │       │   ├── notion.mdx
│   │       │   ├── other-social-providers.mdx
│   │       │   ├── paypal.mdx
│   │       │   ├── reddit.mdx
│   │       │   ├── roblox.mdx
│   │       │   ├── salesforce.mdx
│   │       │   ├── slack.mdx
│   │       │   ├── spotify.mdx
│   │       │   ├── tiktok.mdx
│   │       │   ├── twitch.mdx
│   │       │   ├── twitter.mdx
│   │       │   ├── vk.mdx
│   │       │   └── zoom.mdx
│   │       ├── basic-usage.mdx
│   │       ├── comparison.mdx
│   │       ├── concepts
│   │       │   ├── api.mdx
│   │       │   ├── cli.mdx
│   │       │   ├── client.mdx
│   │       │   ├── cookies.mdx
│   │       │   ├── database.mdx
│   │       │   ├── email.mdx
│   │       │   ├── hooks.mdx
│   │       │   ├── oauth.mdx
│   │       │   ├── plugins.mdx
│   │       │   ├── rate-limit.mdx
│   │       │   ├── session-management.mdx
│   │       │   ├── typescript.mdx
│   │       │   └── users-accounts.mdx
│   │       ├── examples
│   │       │   ├── astro.mdx
│   │       │   ├── next-js.mdx
│   │       │   ├── nuxt.mdx
│   │       │   ├── remix.mdx
│   │       │   └── svelte-kit.mdx
│   │       ├── guides
│   │       │   ├── auth0-migration-guide.mdx
│   │       │   ├── browser-extension-guide.mdx
│   │       │   ├── clerk-migration-guide.mdx
│   │       │   ├── create-a-db-adapter.mdx
│   │       │   ├── next-auth-migration-guide.mdx
│   │       │   ├── optimizing-for-performance.mdx
│   │       │   ├── saml-sso-with-okta.mdx
│   │       │   ├── supabase-migration-guide.mdx
│   │       │   └── your-first-plugin.mdx
│   │       ├── installation.mdx
│   │       ├── integrations
│   │       │   ├── astro.mdx
│   │       │   ├── convex.mdx
│   │       │   ├── elysia.mdx
│   │       │   ├── expo.mdx
│   │       │   ├── express.mdx
│   │       │   ├── fastify.mdx
│   │       │   ├── hono.mdx
│   │       │   ├── lynx.mdx
│   │       │   ├── nestjs.mdx
│   │       │   ├── next.mdx
│   │       │   ├── nitro.mdx
│   │       │   ├── nuxt.mdx
│   │       │   ├── remix.mdx
│   │       │   ├── solid-start.mdx
│   │       │   ├── svelte-kit.mdx
│   │       │   ├── tanstack.mdx
│   │       │   └── waku.mdx
│   │       ├── introduction.mdx
│   │       ├── meta.json
│   │       ├── plugins
│   │       │   ├── 2fa.mdx
│   │       │   ├── admin.mdx
│   │       │   ├── anonymous.mdx
│   │       │   ├── api-key.mdx
│   │       │   ├── autumn.mdx
│   │       │   ├── bearer.mdx
│   │       │   ├── captcha.mdx
│   │       │   ├── community-plugins.mdx
│   │       │   ├── device-authorization.mdx
│   │       │   ├── dodopayments.mdx
│   │       │   ├── dub.mdx
│   │       │   ├── email-otp.mdx
│   │       │   ├── generic-oauth.mdx
│   │       │   ├── have-i-been-pwned.mdx
│   │       │   ├── jwt.mdx
│   │       │   ├── last-login-method.mdx
│   │       │   ├── magic-link.mdx
│   │       │   ├── mcp.mdx
│   │       │   ├── multi-session.mdx
│   │       │   ├── oauth-proxy.mdx
│   │       │   ├── oidc-provider.mdx
│   │       │   ├── one-tap.mdx
│   │       │   ├── one-time-token.mdx
│   │       │   ├── open-api.mdx
│   │       │   ├── organization.mdx
│   │       │   ├── passkey.mdx
│   │       │   ├── phone-number.mdx
│   │       │   ├── polar.mdx
│   │       │   ├── siwe.mdx
│   │       │   ├── sso.mdx
│   │       │   ├── stripe.mdx
│   │       │   └── username.mdx
│   │       └── reference
│   │           ├── contributing.mdx
│   │           ├── faq.mdx
│   │           ├── options.mdx
│   │           ├── resources.mdx
│   │           ├── security.mdx
│   │           └── telemetry.mdx
│   ├── hooks
│   │   └── use-mobile.ts
│   ├── ignore-build.sh
│   ├── lib
│   │   ├── blog.ts
│   │   ├── chat
│   │   │   └── inkeep-qa-schema.ts
│   │   ├── constants.ts
│   │   ├── export-search-indexes.ts
│   │   ├── inkeep-analytics.ts
│   │   ├── is-active.ts
│   │   ├── metadata.ts
│   │   ├── source.ts
│   │   └── utils.ts
│   ├── next.config.js
│   ├── package.json
│   ├── postcss.config.js
│   ├── proxy.ts
│   ├── public
│   │   ├── avatars
│   │   │   └── beka.jpg
│   │   ├── blogs
│   │   │   ├── authjs-joins.png
│   │   │   ├── seed-round.png
│   │   │   └── supabase-ps.png
│   │   ├── branding
│   │   │   ├── better-auth-brand-assets.zip
│   │   │   ├── better-auth-logo-dark.png
│   │   │   ├── better-auth-logo-dark.svg
│   │   │   ├── better-auth-logo-light.png
│   │   │   ├── better-auth-logo-light.svg
│   │   │   ├── better-auth-logo-wordmark-dark.png
│   │   │   ├── better-auth-logo-wordmark-dark.svg
│   │   │   ├── better-auth-logo-wordmark-light.png
│   │   │   └── better-auth-logo-wordmark-light.svg
│   │   ├── extension-id.png
│   │   ├── favicon
│   │   │   ├── android-chrome-192x192.png
│   │   │   ├── android-chrome-512x512.png
│   │   │   ├── apple-touch-icon.png
│   │   │   ├── favicon-16x16.png
│   │   │   ├── favicon-32x32.png
│   │   │   ├── favicon.ico
│   │   │   ├── light
│   │   │   │   ├── android-chrome-192x192.png
│   │   │   │   ├── android-chrome-512x512.png
│   │   │   │   ├── apple-touch-icon.png
│   │   │   │   ├── favicon-16x16.png
│   │   │   │   ├── favicon-32x32.png
│   │   │   │   ├── favicon.ico
│   │   │   │   └── site.webmanifest
│   │   │   └── site.webmanifest
│   │   ├── images
│   │   │   └── blogs
│   │   │       └── better auth (1).png
│   │   ├── logo.png
│   │   ├── logo.svg
│   │   ├── LogoDark.webp
│   │   ├── LogoLight.webp
│   │   ├── og.png
│   │   ├── open-api-reference.png
│   │   ├── people-say
│   │   │   ├── code-with-antonio.jpg
│   │   │   ├── dagmawi-babi.png
│   │   │   ├── dax.png
│   │   │   ├── dev-ed.png
│   │   │   ├── egoist.png
│   │   │   ├── guillermo-rauch.png
│   │   │   ├── jonathan-wilke.png
│   │   │   ├── josh-tried-coding.jpg
│   │   │   ├── kitze.jpg
│   │   │   ├── lazar-nikolov.png
│   │   │   ├── nizzy.png
│   │   │   ├── omar-mcadam.png
│   │   │   ├── ryan-vogel.jpg
│   │   │   ├── saltyatom.jpg
│   │   │   ├── sebastien-chopin.png
│   │   │   ├── shreyas-mididoddi.png
│   │   │   ├── tech-nerd.png
│   │   │   ├── theo.png
│   │   │   ├── vybhav-bhargav.png
│   │   │   └── xavier-pladevall.jpg
│   │   ├── plus.svg
│   │   ├── release-og
│   │   │   ├── 1-2.png
│   │   │   ├── 1-3.png
│   │   │   └── changelog-og.png
│   │   └── v1-og.png
│   ├── README.md
│   ├── scripts
│   │   ├── endpoint-to-doc
│   │   │   ├── index.ts
│   │   │   ├── input.ts
│   │   │   ├── output.mdx
│   │   │   └── readme.md
│   │   └── sync-orama.ts
│   ├── source.config.ts
│   ├── tailwind.config.js
│   ├── tsconfig.json
│   └── turbo.json
├── e2e
│   ├── integration
│   │   ├── package.json
│   │   ├── playwright.config.ts
│   │   ├── solid-vinxi
│   │   │   ├── .gitignore
│   │   │   ├── app.config.ts
│   │   │   ├── e2e
│   │   │   │   ├── test.spec.ts
│   │   │   │   └── utils.ts
│   │   │   ├── package.json
│   │   │   ├── public
│   │   │   │   └── favicon.ico
│   │   │   ├── src
│   │   │   │   ├── app.tsx
│   │   │   │   ├── entry-client.tsx
│   │   │   │   ├── entry-server.tsx
│   │   │   │   ├── global.d.ts
│   │   │   │   ├── lib
│   │   │   │   │   ├── auth-client.ts
│   │   │   │   │   └── auth.ts
│   │   │   │   └── routes
│   │   │   │       ├── [...404].tsx
│   │   │   │       ├── api
│   │   │   │       │   └── auth
│   │   │   │       │       └── [...all].ts
│   │   │   │       └── index.tsx
│   │   │   └── tsconfig.json
│   │   ├── test-utils
│   │   │   ├── package.json
│   │   │   └── src
│   │   │       └── playwright.ts
│   │   └── vanilla-node
│   │       ├── e2e
│   │       │   ├── app.ts
│   │       │   ├── domain.spec.ts
│   │       │   ├── postgres-js.spec.ts
│   │       │   ├── test.spec.ts
│   │       │   └── utils.ts
│   │       ├── index.html
│   │       ├── package.json
│   │       ├── src
│   │       │   ├── main.ts
│   │       │   └── vite-env.d.ts
│   │       ├── tsconfig.json
│   │       └── vite.config.ts
│   └── smoke
│       ├── package.json
│       ├── test
│       │   ├── bun.spec.ts
│       │   ├── cloudflare.spec.ts
│       │   ├── deno.spec.ts
│       │   ├── fixtures
│       │   │   ├── bun-simple.ts
│       │   │   ├── cloudflare
│       │   │   │   ├── .gitignore
│       │   │   │   ├── drizzle
│       │   │   │   │   ├── 0000_clean_vector.sql
│       │   │   │   │   └── meta
│       │   │   │   │       ├── _journal.json
│       │   │   │   │       └── 0000_snapshot.json
│       │   │   │   ├── drizzle.config.ts
│       │   │   │   ├── package.json
│       │   │   │   ├── src
│       │   │   │   │   ├── auth-schema.ts
│       │   │   │   │   ├── db.ts
│       │   │   │   │   └── index.ts
│       │   │   │   ├── test
│       │   │   │   │   ├── apply-migrations.ts
│       │   │   │   │   ├── env.d.ts
│       │   │   │   │   └── index.test.ts
│       │   │   │   ├── tsconfig.json
│       │   │   │   ├── vitest.config.ts
│       │   │   │   ├── worker-configuration.d.ts
│       │   │   │   └── wrangler.json
│       │   │   ├── deno-simple.ts
│       │   │   ├── tsconfig-exact-optional-property-types
│       │   │   │   ├── package.json
│       │   │   │   ├── src
│       │   │   │   │   ├── index.ts
│       │   │   │   │   ├── user-additional-fields.ts
│       │   │   │   │   └── username.ts
│       │   │   │   └── tsconfig.json
│       │   │   ├── tsconfig-verbatim-module-syntax-node10
│       │   │   │   ├── package.json
│       │   │   │   ├── src
│       │   │   │   │   └── index.ts
│       │   │   │   └── tsconfig.json
│       │   │   └── vite
│       │   │       ├── package.json
│       │   │       ├── src
│       │   │       │   ├── client.ts
│       │   │       │   └── server.ts
│       │   │       ├── tsconfig.json
│       │   │       └── vite.config.ts
│       │   ├── ssr.ts
│       │   ├── typecheck.spec.ts
│       │   └── vite.spec.ts
│       └── tsconfig.json
├── LICENSE.md
├── package.json
├── packages
│   ├── better-auth
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── src
│   │   │   ├── __snapshots__
│   │   │   │   └── init.test.ts.snap
│   │   │   ├── adapters
│   │   │   │   ├── adapter-factory
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── test
│   │   │   │   │   │   ├── __snapshots__
│   │   │   │   │   │   │   └── adapter-factory.test.ts.snap
│   │   │   │   │   │   └── adapter-factory.test.ts
│   │   │   │   │   └── types.ts
│   │   │   │   ├── create-test-suite.ts
│   │   │   │   ├── drizzle-adapter
│   │   │   │   │   ├── drizzle-adapter.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── test
│   │   │   │   │       ├── .gitignore
│   │   │   │   │       ├── adapter.drizzle.mysql.test.ts
│   │   │   │   │       ├── adapter.drizzle.pg.test.ts
│   │   │   │   │       ├── adapter.drizzle.sqlite.test.ts
│   │   │   │   │       └── generate-schema.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── kysely-adapter
│   │   │   │   │   ├── bun-sqlite-dialect.ts
│   │   │   │   │   ├── dialect.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── kysely-adapter.ts
│   │   │   │   │   ├── node-sqlite-dialect.ts
│   │   │   │   │   ├── test
│   │   │   │   │   │   ├── adapter.kysely.mssql.test.ts
│   │   │   │   │   │   ├── adapter.kysely.mysql.test.ts
│   │   │   │   │   │   ├── adapter.kysely.pg.test.ts
│   │   │   │   │   │   ├── adapter.kysely.sqlite.test.ts
│   │   │   │   │   │   └── node-sqlite-dialect.test.ts
│   │   │   │   │   └── types.ts
│   │   │   │   ├── memory-adapter
│   │   │   │   │   ├── adapter.memory.test.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── memory-adapter.ts
│   │   │   │   ├── mongodb-adapter
│   │   │   │   │   ├── adapter.mongo-db.test.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── mongodb-adapter.ts
│   │   │   │   ├── prisma-adapter
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── prisma-adapter.ts
│   │   │   │   │   └── test
│   │   │   │   │       ├── .gitignore
│   │   │   │   │       ├── base.prisma
│   │   │   │   │       ├── generate-auth-config.ts
│   │   │   │   │       ├── generate-prisma-schema.ts
│   │   │   │   │       ├── get-prisma-client.ts
│   │   │   │   │       ├── prisma.mysql.test.ts
│   │   │   │   │       ├── prisma.pg.test.ts
│   │   │   │   │       ├── prisma.sqlite.test.ts
│   │   │   │   │       └── push-prisma-schema.ts
│   │   │   │   ├── test-adapter.ts
│   │   │   │   ├── test.ts
│   │   │   │   ├── tests
│   │   │   │   │   ├── auth-flow.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── normal.ts
│   │   │   │   │   ├── number-id.ts
│   │   │   │   │   ├── performance.ts
│   │   │   │   │   └── transactions.ts
│   │   │   │   └── utils.ts
│   │   │   ├── api
│   │   │   │   ├── check-endpoint-conflicts.test.ts
│   │   │   │   ├── index.test.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── middlewares
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── origin-check.test.ts
│   │   │   │   │   └── origin-check.ts
│   │   │   │   ├── rate-limiter
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── rate-limiter.test.ts
│   │   │   │   ├── routes
│   │   │   │   │   ├── account.test.ts
│   │   │   │   │   ├── account.ts
│   │   │   │   │   ├── callback.ts
│   │   │   │   │   ├── email-verification.test.ts
│   │   │   │   │   ├── email-verification.ts
│   │   │   │   │   ├── error.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── ok.ts
│   │   │   │   │   ├── reset-password.test.ts
│   │   │   │   │   ├── reset-password.ts
│   │   │   │   │   ├── session-api.test.ts
│   │   │   │   │   ├── session.ts
│   │   │   │   │   ├── sign-in.test.ts
│   │   │   │   │   ├── sign-in.ts
│   │   │   │   │   ├── sign-out.test.ts
│   │   │   │   │   ├── sign-out.ts
│   │   │   │   │   ├── sign-up.test.ts
│   │   │   │   │   ├── sign-up.ts
│   │   │   │   │   ├── update-user.test.ts
│   │   │   │   │   └── update-user.ts
│   │   │   │   ├── to-auth-endpoints.test.ts
│   │   │   │   └── to-auth-endpoints.ts
│   │   │   ├── auth.test.ts
│   │   │   ├── auth.ts
│   │   │   ├── call.test.ts
│   │   │   ├── client
│   │   │   │   ├── client-ssr.test.ts
│   │   │   │   ├── client.test.ts
│   │   │   │   ├── config.ts
│   │   │   │   ├── fetch-plugins.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── lynx
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── lynx-store.ts
│   │   │   │   ├── parser.ts
│   │   │   │   ├── path-to-object.ts
│   │   │   │   ├── plugins
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── infer-plugin.ts
│   │   │   │   ├── proxy.ts
│   │   │   │   ├── query.ts
│   │   │   │   ├── react
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── react-store.ts
│   │   │   │   ├── session-atom.ts
│   │   │   │   ├── solid
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── solid-store.ts
│   │   │   │   ├── svelte
│   │   │   │   │   └── index.ts
│   │   │   │   ├── test-plugin.ts
│   │   │   │   ├── types.ts
│   │   │   │   ├── url.test.ts
│   │   │   │   ├── vanilla.ts
│   │   │   │   └── vue
│   │   │   │       ├── index.ts
│   │   │   │       └── vue-store.ts
│   │   │   ├── cookies
│   │   │   │   ├── check-cookies.ts
│   │   │   │   ├── cookie-utils.ts
│   │   │   │   ├── cookies.test.ts
│   │   │   │   └── index.ts
│   │   │   ├── crypto
│   │   │   │   ├── buffer.ts
│   │   │   │   ├── hash.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── jwt.ts
│   │   │   │   ├── password.test.ts
│   │   │   │   ├── password.ts
│   │   │   │   └── random.ts
│   │   │   ├── db
│   │   │   │   ├── db.test.ts
│   │   │   │   ├── field.ts
│   │   │   │   ├── get-migration.ts
│   │   │   │   ├── get-schema.ts
│   │   │   │   ├── get-tables.test.ts
│   │   │   │   ├── get-tables.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── internal-adapter.test.ts
│   │   │   │   ├── internal-adapter.ts
│   │   │   │   ├── schema.ts
│   │   │   │   ├── secondary-storage.test.ts
│   │   │   │   ├── to-zod.ts
│   │   │   │   ├── utils.ts
│   │   │   │   └── with-hooks.ts
│   │   │   ├── index.ts
│   │   │   ├── init.test.ts
│   │   │   ├── init.ts
│   │   │   ├── integrations
│   │   │   │   ├── next-js.ts
│   │   │   │   ├── node.ts
│   │   │   │   ├── react-start.ts
│   │   │   │   ├── solid-start.ts
│   │   │   │   └── svelte-kit.ts
│   │   │   ├── oauth2
│   │   │   │   ├── index.ts
│   │   │   │   ├── link-account.test.ts
│   │   │   │   ├── link-account.ts
│   │   │   │   ├── state.ts
│   │   │   │   └── utils.ts
│   │   │   ├── plugins
│   │   │   │   ├── access
│   │   │   │   │   ├── access.test.ts
│   │   │   │   │   ├── access.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── types.ts
│   │   │   │   ├── additional-fields
│   │   │   │   │   ├── additional-fields.test.ts
│   │   │   │   │   └── client.ts
│   │   │   │   ├── admin
│   │   │   │   │   ├── access
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   └── statement.ts
│   │   │   │   │   ├── admin.test.ts
│   │   │   │   │   ├── admin.ts
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── error-codes.ts
│   │   │   │   │   ├── has-permission.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── schema.ts
│   │   │   │   │   └── types.ts
│   │   │   │   ├── anonymous
│   │   │   │   │   ├── anon.test.ts
│   │   │   │   │   ├── client.ts
│   │   │   │   │   └── index.ts
│   │   │   │   ├── api-key
│   │   │   │   │   ├── api-key.test.ts
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── rate-limit.ts
│   │   │   │   │   ├── routes
│   │   │   │   │   │   ├── create-api-key.ts
│   │   │   │   │   │   ├── delete-all-expired-api-keys.ts
│   │   │   │   │   │   ├── delete-api-key.ts
│   │   │   │   │   │   ├── get-api-key.ts
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   ├── list-api-keys.ts
│   │   │   │   │   │   ├── update-api-key.ts
│   │   │   │   │   │   └── verify-api-key.ts
│   │   │   │   │   ├── schema.ts
│   │   │   │   │   └── types.ts
│   │   │   │   ├── bearer
│   │   │   │   │   ├── bearer.test.ts
│   │   │   │   │   └── index.ts
│   │   │   │   ├── captcha
│   │   │   │   │   ├── captcha.test.ts
│   │   │   │   │   ├── constants.ts
│   │   │   │   │   ├── error-codes.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── types.ts
│   │   │   │   │   ├── utils.ts
│   │   │   │   │   └── verify-handlers
│   │   │   │   │       ├── captchafox.ts
│   │   │   │   │       ├── cloudflare-turnstile.ts
│   │   │   │   │       ├── google-recaptcha.ts
│   │   │   │   │       ├── h-captcha.ts
│   │   │   │   │       └── index.ts
│   │   │   │   ├── custom-session
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── custom-session.test.ts
│   │   │   │   │   └── index.ts
│   │   │   │   ├── device-authorization
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── device-authorization.test.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── schema.ts
│   │   │   │   ├── email-otp
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── email-otp.test.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── utils.ts
│   │   │   │   ├── generic-oauth
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── generic-oauth.test.ts
│   │   │   │   │   └── index.ts
│   │   │   │   ├── haveibeenpwned
│   │   │   │   │   ├── haveibeenpwned.test.ts
│   │   │   │   │   └── index.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── jwt
│   │   │   │   │   ├── adapter.ts
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── jwt.test.ts
│   │   │   │   │   ├── schema.ts
│   │   │   │   │   ├── sign.ts
│   │   │   │   │   ├── types.ts
│   │   │   │   │   └── utils.ts
│   │   │   │   ├── last-login-method
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── custom-prefix.test.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── last-login-method.test.ts
│   │   │   │   ├── magic-link
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── magic-link.test.ts
│   │   │   │   │   └── utils.ts
│   │   │   │   ├── mcp
│   │   │   │   │   ├── authorize.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── mcp.test.ts
│   │   │   │   ├── multi-session
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── multi-session.test.ts
│   │   │   │   ├── oauth-proxy
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── oauth-proxy.test.ts
│   │   │   │   ├── oidc-provider
│   │   │   │   │   ├── authorize.ts
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── oidc.test.ts
│   │   │   │   │   ├── schema.ts
│   │   │   │   │   ├── types.ts
│   │   │   │   │   ├── ui.ts
│   │   │   │   │   └── utils.ts
│   │   │   │   ├── one-tap
│   │   │   │   │   ├── client.ts
│   │   │   │   │   └── index.ts
│   │   │   │   ├── one-time-token
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── one-time-token.test.ts
│   │   │   │   │   └── utils.ts
│   │   │   │   ├── open-api
│   │   │   │   │   ├── generator.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── logo.ts
│   │   │   │   │   └── open-api.test.ts
│   │   │   │   ├── organization
│   │   │   │   │   ├── access
│   │   │   │   │   │   ├── index.ts
│   │   │   │   │   │   └── statement.ts
│   │   │   │   │   ├── adapter.ts
│   │   │   │   │   ├── call.ts
│   │   │   │   │   ├── client.test.ts
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── error-codes.ts
│   │   │   │   │   ├── has-permission.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── organization-hook.test.ts
│   │   │   │   │   ├── organization.test.ts
│   │   │   │   │   ├── organization.ts
│   │   │   │   │   ├── permission.ts
│   │   │   │   │   ├── routes
│   │   │   │   │   │   ├── crud-access-control.test.ts
│   │   │   │   │   │   ├── crud-access-control.ts
│   │   │   │   │   │   ├── crud-invites.ts
│   │   │   │   │   │   ├── crud-members.test.ts
│   │   │   │   │   │   ├── crud-members.ts
│   │   │   │   │   │   ├── crud-org.test.ts
│   │   │   │   │   │   ├── crud-org.ts
│   │   │   │   │   │   └── crud-team.ts
│   │   │   │   │   ├── schema.ts
│   │   │   │   │   ├── team.test.ts
│   │   │   │   │   └── types.ts
│   │   │   │   ├── passkey
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── passkey.test.ts
│   │   │   │   ├── phone-number
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── phone-number-error.ts
│   │   │   │   │   └── phone-number.test.ts
│   │   │   │   ├── siwe
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── schema.ts
│   │   │   │   │   ├── siwe.test.ts
│   │   │   │   │   └── types.ts
│   │   │   │   ├── sso
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   └── sso.test.ts
│   │   │   │   ├── two-factor
│   │   │   │   │   ├── backup-codes
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── client.ts
│   │   │   │   │   ├── constant.ts
│   │   │   │   │   ├── error-code.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── otp
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── schema.ts
│   │   │   │   │   ├── totp
│   │   │   │   │   │   └── index.ts
│   │   │   │   │   ├── two-factor.test.ts
│   │   │   │   │   ├── types.ts
│   │   │   │   │   ├── utils.ts
│   │   │   │   │   └── verify-two-factor.ts
│   │   │   │   └── username
│   │   │   │       ├── client.ts
│   │   │   │       ├── error-codes.ts
│   │   │   │       ├── index.ts
│   │   │   │       ├── schema.ts
│   │   │   │       └── username.test.ts
│   │   │   ├── social-providers
│   │   │   │   └── index.ts
│   │   │   ├── social.test.ts
│   │   │   ├── test-utils
│   │   │   │   ├── headers.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── state.ts
│   │   │   │   └── test-instance.ts
│   │   │   ├── types
│   │   │   │   ├── adapter.ts
│   │   │   │   ├── api.ts
│   │   │   │   ├── helper.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── models.ts
│   │   │   │   ├── plugins.ts
│   │   │   │   └── types.test.ts
│   │   │   └── utils
│   │   │       ├── await-object.ts
│   │   │       ├── boolean.ts
│   │   │       ├── clone.ts
│   │   │       ├── constants.ts
│   │   │       ├── date.ts
│   │   │       ├── ensure-utc.ts
│   │   │       ├── get-request-ip.ts
│   │   │       ├── hashing.ts
│   │   │       ├── hide-metadata.ts
│   │   │       ├── id.ts
│   │   │       ├── import-util.ts
│   │   │       ├── index.ts
│   │   │       ├── is-atom.ts
│   │   │       ├── is-promise.ts
│   │   │       ├── json.ts
│   │   │       ├── merger.ts
│   │   │       ├── middleware-response.ts
│   │   │       ├── misc.ts
│   │   │       ├── password.ts
│   │   │       ├── plugin-helper.ts
│   │   │       ├── shim.ts
│   │   │       ├── time.ts
│   │   │       ├── url.ts
│   │   │       └── wildcard.ts
│   │   ├── tsconfig.json
│   │   ├── tsdown.config.ts
│   │   └── vitest.config.ts
│   ├── cli
│   │   ├── CHANGELOG.md
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── src
│   │   │   ├── commands
│   │   │   │   ├── generate.ts
│   │   │   │   ├── info.ts
│   │   │   │   ├── init.ts
│   │   │   │   ├── login.ts
│   │   │   │   ├── mcp.ts
│   │   │   │   ├── migrate.ts
│   │   │   │   └── secret.ts
│   │   │   ├── generators
│   │   │   │   ├── auth-config.ts
│   │   │   │   ├── drizzle.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── kysely.ts
│   │   │   │   ├── prisma.ts
│   │   │   │   └── types.ts
│   │   │   ├── index.ts
│   │   │   └── utils
│   │   │       ├── add-svelte-kit-env-modules.ts
│   │   │       ├── check-package-managers.ts
│   │   │       ├── format-ms.ts
│   │   │       ├── get-config.ts
│   │   │       ├── get-package-info.ts
│   │   │       ├── get-tsconfig-info.ts
│   │   │       └── install-dependencies.ts
│   │   ├── test
│   │   │   ├── __snapshots__
│   │   │   │   ├── auth-schema-mysql-enum.txt
│   │   │   │   ├── auth-schema-mysql-number-id.txt
│   │   │   │   ├── auth-schema-mysql-passkey-number-id.txt
│   │   │   │   ├── auth-schema-mysql-passkey.txt
│   │   │   │   ├── auth-schema-mysql.txt
│   │   │   │   ├── auth-schema-number-id.txt
│   │   │   │   ├── auth-schema-pg-enum.txt
│   │   │   │   ├── auth-schema-pg-passkey.txt
│   │   │   │   ├── auth-schema-sqlite-enum.txt
│   │   │   │   ├── auth-schema-sqlite-number-id.txt
│   │   │   │   ├── auth-schema-sqlite-passkey-number-id.txt
│   │   │   │   ├── auth-schema-sqlite-passkey.txt
│   │   │   │   ├── auth-schema-sqlite.txt
│   │   │   │   ├── auth-schema.txt
│   │   │   │   ├── migrations.sql
│   │   │   │   ├── schema-mongodb.prisma
│   │   │   │   ├── schema-mysql-custom.prisma
│   │   │   │   ├── schema-mysql.prisma
│   │   │   │   ├── schema-numberid.prisma
│   │   │   │   └── schema.prisma
│   │   │   ├── generate-all-db.test.ts
│   │   │   ├── generate.test.ts
│   │   │   ├── get-config.test.ts
│   │   │   ├── info.test.ts
│   │   │   └── migrate.test.ts
│   │   ├── tsconfig.json
│   │   ├── tsconfig.test.json
│   │   └── tsdown.config.ts
│   ├── core
│   │   ├── package.json
│   │   ├── src
│   │   │   ├── async_hooks
│   │   │   │   └── index.ts
│   │   │   ├── context
│   │   │   │   ├── index.ts
│   │   │   │   └── transaction.ts
│   │   │   ├── db
│   │   │   │   ├── adapter
│   │   │   │   │   └── index.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── plugin.ts
│   │   │   │   ├── schema
│   │   │   │   │   ├── account.ts
│   │   │   │   │   ├── rate-limit.ts
│   │   │   │   │   ├── session.ts
│   │   │   │   │   ├── shared.ts
│   │   │   │   │   ├── user.ts
│   │   │   │   │   └── verification.ts
│   │   │   │   └── type.ts
│   │   │   ├── env
│   │   │   │   ├── color-depth.ts
│   │   │   │   ├── env-impl.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── logger.test.ts
│   │   │   │   └── logger.ts
│   │   │   ├── error
│   │   │   │   ├── codes.ts
│   │   │   │   └── index.ts
│   │   │   ├── index.ts
│   │   │   ├── middleware
│   │   │   │   └── index.ts
│   │   │   ├── oauth2
│   │   │   │   ├── client-credentials-token.ts
│   │   │   │   ├── create-authorization-url.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── oauth-provider.ts
│   │   │   │   ├── refresh-access-token.ts
│   │   │   │   ├── utils.ts
│   │   │   │   └── validate-authorization-code.ts
│   │   │   ├── social-providers
│   │   │   │   ├── apple.ts
│   │   │   │   ├── atlassian.ts
│   │   │   │   ├── cognito.ts
│   │   │   │   ├── discord.ts
│   │   │   │   ├── dropbox.ts
│   │   │   │   ├── facebook.ts
│   │   │   │   ├── figma.ts
│   │   │   │   ├── github.ts
│   │   │   │   ├── gitlab.ts
│   │   │   │   ├── google.ts
│   │   │   │   ├── huggingface.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── kakao.ts
│   │   │   │   ├── kick.ts
│   │   │   │   ├── line.ts
│   │   │   │   ├── linear.ts
│   │   │   │   ├── linkedin.ts
│   │   │   │   ├── microsoft-entra-id.ts
│   │   │   │   ├── naver.ts
│   │   │   │   ├── notion.ts
│   │   │   │   ├── paypal.ts
│   │   │   │   ├── reddit.ts
│   │   │   │   ├── roblox.ts
│   │   │   │   ├── salesforce.ts
│   │   │   │   ├── slack.ts
│   │   │   │   ├── spotify.ts
│   │   │   │   ├── tiktok.ts
│   │   │   │   ├── twitch.ts
│   │   │   │   ├── twitter.ts
│   │   │   │   ├── vk.ts
│   │   │   │   └── zoom.ts
│   │   │   ├── types
│   │   │   │   ├── context.ts
│   │   │   │   ├── cookie.ts
│   │   │   │   ├── helper.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── init-options.ts
│   │   │   │   ├── plugin-client.ts
│   │   │   │   └── plugin.ts
│   │   │   └── utils
│   │   │       ├── error-codes.ts
│   │   │       └── index.ts
│   │   ├── tsconfig.json
│   │   └── tsdown.config.ts
│   ├── expo
│   │   ├── CHANGELOG.md
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── src
│   │   │   ├── client.ts
│   │   │   ├── expo.test.ts
│   │   │   └── index.ts
│   │   ├── tsconfig.json
│   │   └── tsdown.config.ts
│   ├── sso
│   │   ├── package.json
│   │   ├── src
│   │   │   ├── client.ts
│   │   │   ├── index.ts
│   │   │   ├── oidc.test.ts
│   │   │   └── saml.test.ts
│   │   ├── tsconfig.json
│   │   └── tsdown.config.ts
│   ├── stripe
│   │   ├── CHANGELOG.md
│   │   ├── package.json
│   │   ├── src
│   │   │   ├── client.ts
│   │   │   ├── hooks.ts
│   │   │   ├── index.ts
│   │   │   ├── schema.ts
│   │   │   ├── stripe.test.ts
│   │   │   ├── types.ts
│   │   │   └── utils.ts
│   │   ├── tsconfig.json
│   │   ├── tsdown.config.ts
│   │   └── vitest.config.ts
│   └── telemetry
│       ├── package.json
│       ├── src
│       │   ├── detectors
│       │   │   ├── detect-auth-config.ts
│       │   │   ├── detect-database.ts
│       │   │   ├── detect-framework.ts
│       │   │   ├── detect-project-info.ts
│       │   │   ├── detect-runtime.ts
│       │   │   └── detect-system-info.ts
│       │   ├── index.ts
│       │   ├── project-id.ts
│       │   ├── telemetry.test.ts
│       │   ├── types.ts
│       │   └── utils
│       │       ├── hash.ts
│       │       ├── id.ts
│       │       ├── import-util.ts
│       │       └── package-json.ts
│       ├── tsconfig.json
│       └── tsdown.config.ts
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── README.md
├── SECURITY.md
├── tsconfig.json
└── turbo.json
```

# Files

--------------------------------------------------------------------------------
/docs/content/docs/plugins/organization.mdx:
--------------------------------------------------------------------------------

```markdown
   1 | ---
   2 | title: Organization
   3 | description: The organization plugin allows you to manage your organization's members and teams.
   4 | ---
   5 | 
   6 | Organizations simplifies user access and permissions management. Assign roles and permissions to streamline project management, team coordination, and partnerships.
   7 | 
   8 | ## Installation
   9 | 
  10 | <Steps>
  11 | <Step>
  12 | ### Add the plugin to your **auth** config
  13 | ```ts title="auth.ts"
  14 | import { betterAuth } from "better-auth"
  15 | import { organization } from "better-auth/plugins"
  16 | 
  17 | export const auth = betterAuth({
  18 |     plugins: [ // [!code highlight]
  19 |         organization() // [!code highlight]
  20 |     ] // [!code highlight]
  21 | })
  22 | ```
  23 | </Step>
  24 | 
  25 | <Step>
  26 |     ### Migrate the database
  27 | 
  28 |     Run the migration or generate the schema to add the necessary fields and tables to the database.
  29 | 
  30 |     <Tabs items={["migrate", "generate"]}>
  31 |         <Tab value="migrate">
  32 |         ```bash
  33 |         npx @better-auth/cli migrate
  34 |         ```
  35 |         </Tab>
  36 |         <Tab value="generate">
  37 |         ```bash
  38 |         npx @better-auth/cli generate
  39 |         ```
  40 |         </Tab>
  41 |     </Tabs>
  42 |     See the [Schema](#schema) section to add the fields manually.
  43 | 
  44 | </Step>
  45 | 
  46 | <Step>
  47 |     ### Add the client plugin
  48 | 
  49 |     ```ts title="auth-client.ts"
  50 |     import { createAuthClient } from "better-auth/client"
  51 |     import { organizationClient } from "better-auth/client/plugins"
  52 | 
  53 |     export const authClient = createAuthClient({
  54 |         plugins: [ // [!code highlight]
  55 |             organizationClient() // [!code highlight]
  56 |         ] // [!code highlight]
  57 |     })
  58 |     ```
  59 | 
  60 | </Step>
  61 | </Steps>
  62 | 
  63 | ## Usage
  64 | 
  65 | Once you've installed the plugin, you can start using the organization plugin to manage your organization's members and teams. The client plugin will provide you with methods under the `organization` namespace, and the server `api` will provide you with the necessary endpoints to manage your organization and give you an easier way to call the functions on your own backend.
  66 | 
  67 | ## Organization
  68 | 
  69 | ### Create an organization
  70 | 
  71 | <APIMethod path="/organization/create" method="POST" requireSession>
  72 | 
  73 | ```ts
  74 | const metadata = { someKey: "someValue" };
  75 | 
  76 | type createOrganization = {
  77 |   /**
  78 |   * The organization name.
  79 |   */
  80 |   name: string = "My Organization"
  81 |   /**
  82 |   * The organization slug.
  83 |   */
  84 |   slug: string = "my-org"
  85 |   /**
  86 |   * The organization logo.
  87 |   */
  88 |   logo?: string = "https://example.com/logo.png"
  89 |   /**
  90 |   * The metadata of the organization.
  91 |   */
  92 |   metadata?: Record<string, any>
  93 |   /**
  94 |   * The user ID of the organization creator. If not provided, the current user will be used. Should only be used by admins or when called by the server.
  95 |   * @serverOnly
  96 |   */
  97 |   userId?: string = "some_user_id"
  98 |   /**
  99 |   * Whether to keep the current active organization active after creating a new one.
 100 |   */
 101 |   keepCurrentActiveOrganization?: boolean = false
 102 | }
 103 | ```
 104 | 
 105 | </APIMethod>
 106 | 
 107 | #### Restrict who can create an organization
 108 | 
 109 | By default, any user can create an organization. To restrict this, set the `allowUserToCreateOrganization` option to a function that returns a boolean, or directly to `true` or `false`.
 110 | 
 111 | ```ts title="auth.ts"
 112 | import { betterAuth } from "better-auth";
 113 | import { organization } from "better-auth/plugins";
 114 | 
 115 | const auth = betterAuth({
 116 |   //...
 117 |   plugins: [
 118 |     organization({
 119 |       allowUserToCreateOrganization: async (user) => {
 120 |         // [!code highlight]
 121 |         const subscription = await getSubscription(user.id); // [!code highlight]
 122 |         return subscription.plan === "pro"; // [!code highlight]
 123 |       }, // [!code highlight]
 124 |     }),
 125 |   ],
 126 | });
 127 | ```
 128 | 
 129 | #### Check if organization slug is taken
 130 | 
 131 | To check if an organization slug is taken or not you can use the `checkSlug` function provided by the client. The function takes an object with the following properties:
 132 | 
 133 | <APIMethod path="/organization/check-slug" method="POST">
 134 | ```ts
 135 | type checkOrganizationSlug = {
 136 |     /**
 137 |      * The organization slug to check.  
 138 |      */
 139 |     slug: string = "my-org"
 140 | }
 141 | ```
 142 | </APIMethod>
 143 | 
 144 | ### Organization Hooks
 145 | 
 146 | You can customize organization operations using hooks that run before and after various organization-related activities. Better Auth provides two ways to configure hooks:
 147 | 
 148 | 1. **Legacy organizationCreation hooks** (deprecated, use `organizationHooks` instead)
 149 | 2. **Modern organizationHooks** (recommended) - provides comprehensive control over all organization-related activities
 150 | 
 151 | #### Organization Creation and Management Hooks
 152 | 
 153 | Control organization lifecycle operations:
 154 | 
 155 | ```ts title="auth.ts"
 156 | import { betterAuth } from "better-auth";
 157 | import { organization } from "better-auth/plugins";
 158 | 
 159 | export const auth = betterAuth({
 160 |   plugins: [
 161 |     organization({
 162 |       organizationHooks: {
 163 |         // Organization creation hooks
 164 |         beforeCreateOrganization: async ({ organization, user }) => {
 165 |           // Run custom logic before organization is created
 166 |           // Optionally modify the organization data
 167 |           return {
 168 |             data: {
 169 |               ...organization,
 170 |               metadata: {
 171 |                 customField: "value",
 172 |               },
 173 |             },
 174 |           };
 175 |         },
 176 | 
 177 |         afterCreateOrganization: async ({ organization, member, user }) => {
 178 |           // Run custom logic after organization is created
 179 |           // e.g., create default resources, send notifications
 180 |           await setupDefaultResources(organization.id);
 181 |         },
 182 | 
 183 |         // Organization update hooks
 184 |         beforeUpdateOrganization: async ({ organization, user, member }) => {
 185 |           // Validate updates, apply business rules
 186 |           return {
 187 |             data: {
 188 |               ...organization,
 189 |               name: organization.name?.toLowerCase(),
 190 |             },
 191 |           };
 192 |         },
 193 | 
 194 |         afterUpdateOrganization: async ({ organization, user, member }) => {
 195 |           // Sync changes to external systems
 196 |           await syncOrganizationToExternalSystems(organization);
 197 |         },
 198 |       },
 199 |     }),
 200 |   ],
 201 | });
 202 | ```
 203 | 
 204 | <Callout type="info">
 205 |   The legacy `organizationCreation` hooks are still supported but deprecated.
 206 |   Use `organizationHooks.beforeCreateOrganization` and
 207 |   `organizationHooks.afterCreateOrganization` instead for new projects.
 208 | </Callout>
 209 | 
 210 | #### Member Hooks
 211 | 
 212 | Control member operations within organizations:
 213 | 
 214 | ```ts title="auth.ts"
 215 | import { betterAuth } from "better-auth";
 216 | import { organization } from "better-auth/plugins";
 217 | 
 218 | export const auth = betterAuth({
 219 |   plugins: [
 220 |     organization({
 221 |       organizationHooks: {
 222 |         // Before a member is added to an organization
 223 |         beforeAddMember: async ({ member, user, organization }) => {
 224 |           // Custom validation or modification
 225 |           console.log(`Adding ${user.email} to ${organization.name}`);
 226 | 
 227 |           // Optionally modify member data
 228 |           return {
 229 |             data: {
 230 |               ...member,
 231 |               role: "custom-role", // Override the role
 232 |             },
 233 |           };
 234 |         },
 235 | 
 236 |         // After a member is added
 237 |         afterAddMember: async ({ member, user, organization }) => {
 238 |           // Send welcome email, create default resources, etc.
 239 |           await sendWelcomeEmail(user.email, organization.name);
 240 |         },
 241 | 
 242 |         // Before a member is removed
 243 |         beforeRemoveMember: async ({ member, user, organization }) => {
 244 |           // Cleanup user's resources, send notification, etc.
 245 |           await cleanupUserResources(user.id, organization.id);
 246 |         },
 247 | 
 248 |         // After a member is removed
 249 |         afterRemoveMember: async ({ member, user, organization }) => {
 250 |           await logMemberRemoval(user.id, organization.id);
 251 |         },
 252 | 
 253 |         // Before updating a member's role
 254 |         beforeUpdateMemberRole: async ({
 255 |           member,
 256 |           newRole,
 257 |           user,
 258 |           organization,
 259 |         }) => {
 260 |           // Validate role change permissions
 261 |           if (newRole === "owner" && !hasOwnerUpgradePermission(user)) {
 262 |             throw new Error("Cannot upgrade to owner role");
 263 |           }
 264 | 
 265 |           // Optionally modify the role
 266 |           return {
 267 |             data: {
 268 |               role: newRole,
 269 |             },
 270 |           };
 271 |         },
 272 | 
 273 |         // After updating a member's role
 274 |         afterUpdateMemberRole: async ({
 275 |           member,
 276 |           previousRole,
 277 |           user,
 278 |           organization,
 279 |         }) => {
 280 |           await logRoleChange(user.id, previousRole, member.role);
 281 |         },
 282 |       },
 283 |     }),
 284 |   ],
 285 | });
 286 | ```
 287 | 
 288 | #### Invitation Hooks
 289 | 
 290 | Control invitation lifecycle:
 291 | 
 292 | ```ts title="auth.ts"
 293 | export const auth = betterAuth({
 294 |   plugins: [
 295 |     organization({
 296 |       organizationHooks: {
 297 |         // Before creating an invitation
 298 |         beforeCreateInvitation: async ({
 299 |           invitation,
 300 |           inviter,
 301 |           organization,
 302 |         }) => {
 303 |           // Custom validation or expiration logic
 304 |           const customExpiration = new Date(
 305 |             Date.now() + 1000 * 60 * 60 * 24 * 7
 306 |           ); // 7 days
 307 | 
 308 |           return {
 309 |             data: {
 310 |               ...invitation,
 311 |               expiresAt: customExpiration,
 312 |             },
 313 |           };
 314 |         },
 315 | 
 316 |         // After creating an invitation
 317 |         afterCreateInvitation: async ({
 318 |           invitation,
 319 |           inviter,
 320 |           organization,
 321 |         }) => {
 322 |           // Send custom invitation email, track metrics, etc.
 323 |           await sendCustomInvitationEmail(invitation, organization);
 324 |         },
 325 | 
 326 |         // Before accepting an invitation
 327 |         beforeAcceptInvitation: async ({ invitation, user, organization }) => {
 328 |           // Additional validation before acceptance
 329 |           await validateUserEligibility(user, organization);
 330 |         },
 331 | 
 332 |         // After accepting an invitation
 333 |         afterAcceptInvitation: async ({
 334 |           invitation,
 335 |           member,
 336 |           user,
 337 |           organization,
 338 |         }) => {
 339 |           // Setup user account, assign default resources
 340 |           await setupNewMemberResources(user, organization);
 341 |         },
 342 | 
 343 |         // Before/after rejecting invitations
 344 |         beforeRejectInvitation: async ({ invitation, user, organization }) => {
 345 |           // Log rejection reason, send notification to inviter
 346 |         },
 347 | 
 348 |         afterRejectInvitation: async ({ invitation, user, organization }) => {
 349 |           await notifyInviterOfRejection(invitation.inviterId, user.email);
 350 |         },
 351 | 
 352 |         // Before/after cancelling invitations
 353 |         beforeCancelInvitation: async ({
 354 |           invitation,
 355 |           cancelledBy,
 356 |           organization,
 357 |         }) => {
 358 |           // Verify cancellation permissions
 359 |         },
 360 | 
 361 |         afterCancelInvitation: async ({
 362 |           invitation,
 363 |           cancelledBy,
 364 |           organization,
 365 |         }) => {
 366 |           await logInvitationCancellation(invitation.id, cancelledBy.id);
 367 |         },
 368 |       },
 369 |     }),
 370 |   ],
 371 | });
 372 | ```
 373 | 
 374 | #### Team Hooks
 375 | 
 376 | Control team operations (when teams are enabled):
 377 | 
 378 | ```ts title="auth.ts"
 379 | export const auth = betterAuth({
 380 |   plugins: [
 381 |     organization({
 382 |       teams: { enabled: true },
 383 |       organizationHooks: {
 384 |         // Before creating a team
 385 |         beforeCreateTeam: async ({ team, user, organization }) => {
 386 |           // Validate team name, apply naming conventions
 387 |           return {
 388 |             data: {
 389 |               ...team,
 390 |               name: team.name.toLowerCase().replace(/\s+/g, "-"),
 391 |             },
 392 |           };
 393 |         },
 394 | 
 395 |         // After creating a team
 396 |         afterCreateTeam: async ({ team, user, organization }) => {
 397 |           // Create default team resources, channels, etc.
 398 |           await createDefaultTeamResources(team.id);
 399 |         },
 400 | 
 401 |         // Before updating a team
 402 |         beforeUpdateTeam: async ({ team, updates, user, organization }) => {
 403 |           // Validate updates, apply business rules
 404 |           return {
 405 |             data: {
 406 |               ...updates,
 407 |               name: updates.name?.toLowerCase(),
 408 |             },
 409 |           };
 410 |         },
 411 | 
 412 |         // After updating a team
 413 |         afterUpdateTeam: async ({ team, user, organization }) => {
 414 |           await syncTeamChangesToExternalSystems(team);
 415 |         },
 416 | 
 417 |         // Before deleting a team
 418 |         beforeDeleteTeam: async ({ team, user, organization }) => {
 419 |           // Backup team data, notify members
 420 |           await backupTeamData(team.id);
 421 |         },
 422 | 
 423 |         // After deleting a team
 424 |         afterDeleteTeam: async ({ team, user, organization }) => {
 425 |           await cleanupTeamResources(team.id);
 426 |         },
 427 | 
 428 |         // Team member operations
 429 |         beforeAddTeamMember: async ({
 430 |           teamMember,
 431 |           team,
 432 |           user,
 433 |           organization,
 434 |         }) => {
 435 |           // Validate team membership limits, permissions
 436 |           const memberCount = await getTeamMemberCount(team.id);
 437 |           if (memberCount >= 10) {
 438 |             throw new Error("Team is full");
 439 |           }
 440 |         },
 441 | 
 442 |         afterAddTeamMember: async ({
 443 |           teamMember,
 444 |           team,
 445 |           user,
 446 |           organization,
 447 |         }) => {
 448 |           await grantTeamAccess(user.id, team.id);
 449 |         },
 450 | 
 451 |         beforeRemoveTeamMember: async ({
 452 |           teamMember,
 453 |           team,
 454 |           user,
 455 |           organization,
 456 |         }) => {
 457 |           // Backup user's team-specific data
 458 |           await backupTeamMemberData(user.id, team.id);
 459 |         },
 460 | 
 461 |         afterRemoveTeamMember: async ({
 462 |           teamMember,
 463 |           team,
 464 |           user,
 465 |           organization,
 466 |         }) => {
 467 |           await revokeTeamAccess(user.id, team.id);
 468 |         },
 469 |       },
 470 |     }),
 471 |   ],
 472 | });
 473 | ```
 474 | 
 475 | #### Hook Error Handling
 476 | 
 477 | All hooks support error handling. Throwing an error in a `before` hook will prevent the operation from proceeding:
 478 | 
 479 | ```ts title="auth.ts"
 480 | import { APIError } from "better-auth/api";
 481 | 
 482 | export const auth = betterAuth({
 483 |   plugins: [
 484 |     organization({
 485 |       organizationHooks: {
 486 |         beforeAddMember: async ({ member, user, organization }) => {
 487 |           // Check if user has pending violations
 488 |           const violations = await checkUserViolations(user.id);
 489 |           if (violations.length > 0) {
 490 |             throw new APIError("BAD_REQUEST", {
 491 |               message:
 492 |                 "User has pending violations and cannot join organizations",
 493 |             });
 494 |           }
 495 |         },
 496 | 
 497 |         beforeCreateTeam: async ({ team, user, organization }) => {
 498 |           // Validate team name uniqueness
 499 |           const existingTeam = await findTeamByName(team.name, organization.id);
 500 |           if (existingTeam) {
 501 |             throw new APIError("BAD_REQUEST", {
 502 |               message: "Team name already exists in this organization",
 503 |             });
 504 |           }
 505 |         },
 506 |       },
 507 |     }),
 508 |   ],
 509 | });
 510 | ```
 511 | 
 512 | ### List User's Organizations
 513 | 
 514 | To list the organizations that a user is a member of, you can use `useListOrganizations` hook. It implements a reactive way to get the organizations that the user is a member of.
 515 | 
 516 | <Tabs items={["React", "Vue", "Svelte"]} default="React">
 517 | <Tab value="React">
 518 | ```tsx title="client.tsx"
 519 | import { authClient } from "@/lib/auth-client"
 520 | 
 521 | function App(){
 522 | const { data: organizations } = authClient.useListOrganizations()
 523 | return (
 524 |   <div>
 525 |     {organizations.map((org) => (
 526 |       <p>{org.name}</p>
 527 |     ))}
 528 |   </div>)
 529 | }
 530 | ```
 531 | </Tab>
 532 | 
 533 | <Tab value="Svelte">
 534 | ```svelte title="page.svelte"
 535 | <script lang="ts">
 536 |   import { authClient } from "$lib/auth-client";
 537 |   const organizations = authClient.useListOrganizations();
 538 | </script>
 539 | 
 540 | <h1>Organizations</h1>
 541 | 
 542 | {#if $organizations.isPending}
 543 | 
 544 |   <p>Loading...</p>
 545 | {:else if !$organizations.data?.length}
 546 |   <p>No organizations found.</p>
 547 | {:else}
 548 |   <ul>
 549 |     {#each $organizations.data as organization}
 550 |       <li>{organization.name}</li>
 551 |     {/each}
 552 |   </ul>
 553 | {/if}
 554 | ```
 555 | 
 556 | </Tab>
 557 | 
 558 | <Tab value="Vue">
 559 | ```vue title="organization.vue"
 560 | <script lang="ts">;
 561 | export default {
 562 |     setup() {
 563 |         const organizations = authClient.useListOrganizations()
 564 |         return { organizations };
 565 |     }
 566 | };
 567 | </script>
 568 | 
 569 | <template>
 570 |     <div>
 571 |         <h1>Organizations</h1>
 572 |         <div v-if="organizations.isPending">Loading...</div>
 573 |         <div v-else-if="organizations.data === null">No organizations found.</div>
 574 |         <ul v-else>
 575 |             <li v-for="organization in organizations.data" :key="organization.id">
 576 |                 {{ organization.name }}
 577 |             </li>
 578 |         </ul>
 579 |     </div>
 580 | </template>
 581 | ```
 582 | </Tab>
 583 | </Tabs>
 584 | 
 585 | Or alternatively, you can call `organization.list` if you don't want to use a hook.
 586 | 
 587 | <APIMethod path="/organization/list" method="GET" requireSession>
 588 |   ```ts
 589 |   type listOrganizations = {
 590 |   }
 591 |   ```
 592 | </APIMethod>
 593 | 
 594 | 
 595 | ### Active Organization
 596 | 
 597 | Active organization is the workspace the user is currently working on. By default when the user is signed in the active organization is set to `null`. You can set the active organization to the user session.
 598 | 
 599 | <Callout type="info">
 600 |   It's not always you want to persist the active organization in the session.
 601 |   You can manage the active organization in the client side only. For example,
 602 |   multiple tabs can have different active organizations.
 603 | </Callout>
 604 | 
 605 | #### Set Active Organization
 606 | 
 607 | You can set the active organization by calling the `organization.setActive` function. It'll set the active organization for the user session.
 608 | 
 609 | <Callout>
 610 |   In some applications, you may want the ability to unset an active
 611 |   organization. In this case, you can call this endpoint with `organizationId`
 612 |   set to `null`.
 613 | </Callout>
 614 | 
 615 | <APIMethod path="/organization/set-active" method="POST">
 616 | ```ts
 617 | type setActiveOrganization = {
 618 |     /**
 619 |      * The organization ID to set as active. It can be null to unset the active organization.  
 620 |      */
 621 |     organizationId?: string | null = "org-id"
 622 |     /**
 623 |      * The organization slug to set as active. It can be null to unset the active organization if organizationId is not provided.  
 624 |      */
 625 |     organizationSlug?: string = "org-slug"
 626 | }
 627 | ```
 628 | </APIMethod>
 629 | 
 630 | To set active organization when a session is created you can use [database hooks](/docs/concepts/database#database-hooks).
 631 | 
 632 | ```ts title="auth.ts"
 633 | export const auth = betterAuth({
 634 |   databaseHooks: {
 635 |     session: {
 636 |       create: {
 637 |         before: async (session) => {
 638 |           const organization = await getActiveOrganization(session.userId);
 639 |           return {
 640 |             data: {
 641 |               ...session,
 642 |               activeOrganizationId: organization.id,
 643 |             },
 644 |           };
 645 |         },
 646 |       },
 647 |     },
 648 |   },
 649 | });
 650 | ```
 651 | 
 652 | #### Use Active Organization
 653 | 
 654 | To retrieve the active organization for the user, you can call the `useActiveOrganization` hook. It returns the active organization for the user. Whenever the active organization changes, the hook will re-evaluate and return the new active organization.
 655 | 
 656 | <Tabs items={['React', 'Vue', 'Svelte']}>
 657 |     <Tab value="React">
 658 |     ```tsx title="client.tsx"
 659 |     import { authClient } from "@/lib/auth-client"
 660 | 
 661 |     function App(){
 662 |         const { data: activeOrganization } = authClient.useActiveOrganization()
 663 |         return (
 664 |             <div>
 665 |                 {activeOrganization ? <p>{activeOrganization.name}</p> : null}
 666 |             </div>
 667 |         )
 668 |     }
 669 |     ```
 670 |     </Tab>
 671 |     <Tab value="Svelte">
 672 |     ```tsx title="client.tsx"
 673 |     <script lang="ts">
 674 |     import { authClient } from "$lib/auth-client";
 675 |     const activeOrganization = authClient.useActiveOrganization();
 676 |     </script>
 677 | 
 678 |     <h2>Active Organization</h2>
 679 | 
 680 |     {#if $activeOrganization.isPending}
 681 |     <p>Loading...</p>
 682 |     {:else if $activeOrganization.data === null}
 683 |     <p>No active organization found.</p>
 684 |     {:else}
 685 |     <p>{$activeOrganization.data.name}</p>
 686 |     {/if}
 687 |     ```
 688 |     </Tab>
 689 |     <Tab value="Vue">
 690 |     ```vue title="organization.vue"
 691 |     <script lang="ts">;
 692 |     export default {
 693 |         setup() {
 694 |             const activeOrganization = authClient.useActiveOrganization();
 695 |             return { activeOrganization };
 696 |         }
 697 |     };
 698 |     </script>
 699 | 
 700 |     <template>
 701 |         <div>
 702 |             <h2>Active organization</h2>
 703 |             <div v-if="activeOrganization.isPending">Loading...</div>
 704 |             <div v-else-if="activeOrganization.data === null">No active organization.</div>
 705 |             <div v-else>
 706 |                 {{ activeOrganization.data.name }}
 707 |             </div>
 708 |         </div>
 709 |     </template>
 710 |     ```
 711 |     </Tab>
 712 | 
 713 | </Tabs>
 714 | 
 715 | ### Get Full Organization
 716 | 
 717 | To get the full details of an organization, you can use the `getFullOrganization` function.
 718 | By default, if you don't pass any properties, it will use the active organization.
 719 | 
 720 | <APIMethod
 721 |   path="/organization/get-full-organization"
 722 |   method="GET"
 723 |   requireSession
 724 | >
 725 | ```ts
 726 | type getFullOrganization = {
 727 |     /**
 728 |      * The organization ID to get. By default, it will use the active organization.  
 729 |      */
 730 |     organizationId?: string = "org-id"
 731 |     /**
 732 |      * The organization slug to get.  
 733 |      */
 734 |     organizationSlug?: string = "org-slug"
 735 |     /**
 736 |      * The limit of members to get. By default, it uses the membershipLimit option which defaults to 100.
 737 |      */
 738 |     membersLimit?: number = 100
 739 | }
 740 | ```
 741 | </APIMethod>
 742 | 
 743 | 
 744 | ### Update Organization
 745 | 
 746 | To update organization info, you can use `organization.update`
 747 | 
 748 | <APIMethod
 749 |   path="/organization/update"
 750 |   method="POST"
 751 |   requireSession
 752 | >
 753 | ```ts
 754 | type updateOrganization = {
 755 |     /**
 756 |      * A partial list of data to update the organization. 
 757 |      */
 758 |     data: {
 759 |         /**
 760 |          * The name of the organization. 
 761 |          */
 762 |         name?: string = "updated-name"
 763 |         /**
 764 |          * The slug of the organization. 
 765 |          */
 766 |         slug?: string = "updated-slug"
 767 |         /**
 768 |          * The logo of the organization. 
 769 |          */
 770 |         logo?: string = "new-logo.url"
 771 |         /**
 772 |          * The metadata of the organization. 
 773 |          */
 774 |         metadata?: Record<string, any> | null = { customerId: "test" }
 775 |     }
 776 |     /**
 777 |      * The organization ID. to update.
 778 |      */
 779 |     organizationId?: string = "org-id"
 780 | }
 781 | ```
 782 | </APIMethod>
 783 | 
 784 | ### Delete Organization
 785 | 
 786 | To remove user owned organization, you can use `organization.delete`
 787 | 
 788 | <APIMethod
 789 |   path="/organization/delete"
 790 |   method="POST"
 791 |   requireSession
 792 | >
 793 | ```ts
 794 | type deleteOrganization = {
 795 |     /*
 796 |     * The organization ID to delete.
 797 |     */
 798 |     organizationId: string = "org-id"
 799 | }
 800 | ```
 801 | </APIMethod>
 802 | 
 803 | If the user has the necessary permissions (by default: role is owner) in the specified organization, all members, invitations and organization information will be removed.
 804 | 
 805 | You can configure how organization deletion is handled through `organizationDeletion` option:
 806 | 
 807 | ```ts
 808 | const auth = betterAuth({
 809 |   plugins: [
 810 |     organization({
 811 |       disableOrganizationDeletion: true, //to disable it altogether
 812 |       organizationHooks: {
 813 |         beforeDeleteOrganization: async (data, request) => {
 814 |           // a callback to run before deleting org
 815 |         },
 816 |         afterDeleteOrganization: async (data, request) => {
 817 |           // a callback to run after deleting org
 818 |         },
 819 |       },
 820 |     }),
 821 |   ],
 822 | });
 823 | ```
 824 | 
 825 | ## Invitations
 826 | 
 827 | To add a member to an organization, we first need to send an invitation to the user. The user will receive an email/sms with the invitation link. Once the user accepts the invitation, they will be added to the organization.
 828 | 
 829 | ### Setup Invitation Email
 830 | 
 831 | For member invitation to work we first need to provide `sendInvitationEmail` to the `better-auth` instance. This function is responsible for sending the invitation email to the user.
 832 | 
 833 | You'll need to construct and send the invitation link to the user. The link should include the invitation ID, which will be used with the acceptInvitation function when the user clicks on it.
 834 | 
 835 | ```ts title="auth.ts"
 836 | import { betterAuth } from "better-auth";
 837 | import { organization } from "better-auth/plugins";
 838 | import { sendOrganizationInvitation } from "./email";
 839 | export const auth = betterAuth({
 840 |   plugins: [
 841 |     organization({
 842 |       async sendInvitationEmail(data) {
 843 |         const inviteLink = `https://example.com/accept-invitation/${data.id}`;
 844 |         sendOrganizationInvitation({
 845 |           email: data.email,
 846 |           invitedByUsername: data.inviter.user.name,
 847 |           invitedByEmail: data.inviter.user.email,
 848 |           teamName: data.organization.name,
 849 |           inviteLink,
 850 |         });
 851 |       },
 852 |     }),
 853 |   ],
 854 | });
 855 | ```
 856 | 
 857 | ### Send Invitation
 858 | 
 859 | To invite users to an organization, you can use the `invite` function provided by the client. The `invite` function takes an object with the following properties:
 860 | 
 861 | <APIMethod path="/organization/invite-member" method="POST">
 862 | ```ts
 863 | type createInvitation = {
 864 |     /**
 865 |      * The email address of the user to invite.  
 866 |      */
 867 |     email: string = "[email protected]"
 868 |     /**
 869 |      * The role(s) to assign to the user. It can be `admin`, `member`, or `guest`.  
 870 |      */
 871 |     role: string | string[] = "member"
 872 |     /**
 873 |      * The organization ID to invite the user to. Defaults to the active organization.  
 874 |      */
 875 |     organizationId?: string = "org-id"
 876 |     /**
 877 |      * Resend the invitation email, if the user is already invited.  
 878 |      */
 879 |     resend?: boolean = true
 880 |     /**
 881 |      * The team ID to invite the user to.  
 882 |      */
 883 |     teamId?: string = "team-id"
 884 | }
 885 | ```
 886 | </APIMethod>
 887 | 
 888 | <Callout>
 889 |   - If the user is already a member of the organization, the invitation will be
 890 |   canceled. - If the user is already invited to the organization, unless
 891 |   `resend` is set to `true`, the invitation will not be sent again. - If
 892 |   `cancelPendingInvitationsOnReInvite` is set to `true`, the invitation will be
 893 |   canceled if the user is already invited to the organization and a new
 894 |   invitation is sent.
 895 | </Callout>
 896 | 
 897 | ### Accept Invitation
 898 | 
 899 | When a user receives an invitation email, they can click on the invitation link to accept the invitation. The invitation link should include the invitation ID, which will be used to accept the invitation.
 900 | 
 901 | Make sure to call the `acceptInvitation` function after the user is logged in.
 902 | 
 903 | <APIMethod path="/organization/accept-invitation" method="POST">
 904 | ```ts
 905 | type acceptInvitation = {
 906 |     /**
 907 |      * The ID of the invitation to accept.  
 908 |      */
 909 |     invitationId: string = "invitation-id"
 910 | }
 911 | ```
 912 | </APIMethod>
 913 | 
 914 | #### Email Verification Requirement
 915 | 
 916 | If the `requireEmailVerificationOnInvitation` option is enabled in your organization configuration, users must verify their email address before they can accept invitations. This adds an extra security layer to ensure that only verified users can join your organization.
 917 | 
 918 | ```ts title="auth.ts"
 919 | import { betterAuth } from "better-auth";
 920 | import { organization } from "better-auth/plugins";
 921 | 
 922 | export const auth = betterAuth({
 923 |   plugins: [
 924 |     organization({
 925 |       requireEmailVerificationOnInvitation: true, // [!code highlight]
 926 |       async sendInvitationEmail(data) {
 927 |         // ... your email sending logic
 928 |       },
 929 |     }),
 930 |   ],
 931 | });
 932 | ```
 933 | 
 934 | ### Invitation Accepted Callback
 935 | 
 936 | You can configure Better Auth to execute a callback function when an invitation is accepted. This is useful for logging events, updating analytics, sending notifications, or any other custom logic you need to run when someone joins your organization.
 937 | 
 938 | ```ts title="auth.ts"
 939 | import { betterAuth } from "better-auth";
 940 | import { organization } from "better-auth/plugins";
 941 | 
 942 | export const auth = betterAuth({
 943 |   plugins: [
 944 |     organization({
 945 |       async sendInvitationEmail(data) {
 946 |         // ... your invitation email logic
 947 |       },
 948 |       async onInvitationAccepted(data) {
 949 |         // This callback gets triggered when an invitation is accepted
 950 |       },
 951 |     }),
 952 |   ],
 953 | });
 954 | ```
 955 | 
 956 | The callback receives the following data:
 957 | 
 958 | - `id`: The invitation ID
 959 | - `role`: The role assigned to the user
 960 | - `organization`: The organization the user joined
 961 | - `invitation`: The invitation object
 962 | - `inviter`: The member who sent the invitation (including user details)
 963 | - `acceptedUser`: The user who accepted the invitation
 964 | 
 965 | ### Cancel Invitation
 966 | 
 967 | If a user has sent out an invitation, you can use this method to cancel it.
 968 | 
 969 | If you're looking for how a user can reject an invitation, you can find that [here](#reject-invitation).
 970 | 
 971 | <APIMethod path="/organization/cancel-invitation" method="POST" noResult>
 972 | ```ts
 973 | type cancelInvitation = {
 974 |     /**
 975 |      * The ID of the invitation to cancel.  
 976 |      */
 977 |     invitationId: string = "invitation-id"
 978 | }
 979 | ```
 980 | </APIMethod>
 981 | 
 982 | ### Reject Invitation
 983 | 
 984 | If this user has received an invitation, but wants to decline it, this method will allow you to do so by rejecting it.
 985 | 
 986 | <APIMethod path="/organization/reject-invitation" method="POST" noResult>
 987 | ```ts
 988 | type rejectInvitation = {
 989 |     /**
 990 |      * The ID of the invitation to reject.  
 991 |      */
 992 |     invitationId: string = "invitation-id"
 993 | }
 994 | ```
 995 | </APIMethod>
 996 | 
 997 | <Callout type="info">
 998 |   Like accepting invitations, rejecting invitations also requires email
 999 |   verification when the `requireEmailVerificationOnInvitation` option is
1000 |   enabled. Users with unverified emails will receive an error when attempting to
1001 |   reject invitations.
1002 | </Callout>
1003 | 
1004 | ### Get Invitation
1005 | 
1006 | To get an invitation you can use the `organization.getInvitation` function provided by the client. You need to provide the invitation id as a query parameter.
1007 | 
1008 | <APIMethod
1009 |   path="/organization/get-invitation"
1010 |   method="GET"
1011 |   requireSession
1012 | >
1013 | ```ts
1014 | type getInvitation = {
1015 |     /**
1016 |      * The ID of the invitation to get.  
1017 |      */
1018 |     id: string = "invitation-id"
1019 | }
1020 | ```
1021 | </APIMethod>
1022 | 
1023 | ### List Invitations
1024 | 
1025 | To list all invitations for a given organization you can use the `listInvitations` function provided by the client.
1026 | 
1027 | <APIMethod path="/organization/list-invitations" method="GET">
1028 | ```ts
1029 | type listInvitations = {
1030 |     /**
1031 |      * An optional ID of the organization to list invitations for. If not provided, will default to the user's active organization. 
1032 |      */
1033 |     organizationId?: string = "organization-id"
1034 | }
1035 | ```
1036 | </APIMethod>
1037 | 
1038 | ### List user invitations
1039 | 
1040 | To list all invitations for a given user you can use the `listUserInvitations` function provided by the client.
1041 | 
1042 | ```ts title="auth-client.ts"
1043 | const invitations = await authClient.organization.listUserInvitations();
1044 | ```
1045 | 
1046 | On the server, you can pass the user ID as a query parameter.
1047 | 
1048 | ```ts title="api.ts"
1049 | const invitations = await auth.api.listUserInvitations({
1050 |   query: {
1051 |     email: "[email protected]",
1052 |   },
1053 | });
1054 | ```
1055 | 
1056 | <Callout type="warn">
1057 |   The `email` query parameter is only available on the server to query for
1058 |   invitations for a specific user.
1059 | </Callout>
1060 | 
1061 | ## Members
1062 | 
1063 | ### List Members
1064 | 
1065 | To list all members of an organization you can use the `listMembers` function.
1066 | 
1067 | <APIMethod path="/organization/list-members" method="GET">
1068 | 
1069 | ```ts
1070 | type listMembers = {
1071 |     /**
1072 |      * An optional organization ID to list members for. If not provided, will default to the user's active organization.
1073 |      */
1074 |     organizationId?: string = "organization-id"
1075 |     /**
1076 |      * The limit of members to return.
1077 |      */
1078 |     limit?: number = 100
1079 |     /**
1080 |      * The offset to start from.
1081 |      */
1082 |     offset?: number = 0
1083 |     /**
1084 |      * The field to sort by.
1085 |      */
1086 |     sortBy?: string = "createdAt"
1087 |     /**
1088 |      * The direction to sort by.
1089 |      */
1090 |     sortDirection?: "asc" | "desc" = "desc"
1091 |     /**
1092 |      * The field to filter by.
1093 |      */
1094 |     filterField?: string = "createdAt"
1095 |     /**
1096 |      * The operator to filter by.
1097 |      */
1098 |     filterOperator?: "eq" | "ne" | "gt" | "gte" | "lt" | "lte" | "in" | "nin" | "contains" = "eq"
1099 |     /**
1100 |      * The value to filter by.
1101 |      */
1102 |     filterValue?: string = "value"
1103 | }
1104 | ```
1105 | </APIMethod>
1106 | 
1107 | ### Remove Member
1108 | 
1109 | To remove you can use `organization.removeMember`
1110 | 
1111 | <APIMethod path="/organization/remove-member" method="POST">
1112 | ```ts
1113 | type removeMember = {
1114 |     /**
1115 |      * The ID or email of the member to remove. 
1116 |      */
1117 |     memberIdOrEmail: string = "[email protected]"
1118 |     /**
1119 |      * The ID of the organization to remove the member from. If not provided, the active organization will be used. 
1120 |      */
1121 |     organizationId?: string = "org-id"
1122 | }
1123 | ```
1124 | </APIMethod>
1125 | 
1126 | ### Update Member Role
1127 | 
1128 | To update the role of a member in an organization, you can use the `organization.updateMemberRole`. If the user has the permission to update the role of the member, the role will be updated.
1129 | 
1130 | <APIMethod path="/organization/update-member-role" method="POST" noResult>
1131 | ```ts
1132 | type updateMemberRole = {
1133 |     /**
1134 |      * The new role to be applied. This can be a string or array of strings representing the roles. 
1135 |      */
1136 |     role: string | string[] = ["admin", "sale"]
1137 |     /**
1138 |      * The member id to apply the role update to. 
1139 |      */
1140 |     memberId: string = "member-id"
1141 |     /**
1142 |      * An optional organization ID which the member is a part of to apply the role update. If not provided, you must provide session headers to get the active organization. 
1143 |      */
1144 |     organizationId?: string = "organization-id"
1145 | }
1146 | ```
1147 | </APIMethod>
1148 | 
1149 | ### Get Active Member
1150 | 
1151 | To get the current member of the active organization you can use the `organization.getActiveMember` function. This function will return the user's member details in their active organization.
1152 | 
1153 | <APIMethod
1154 |   path="/organization/get-active-member"
1155 |   method="GET"
1156 |   requireSession
1157 |   resultVariable="member"
1158 | >
1159 |   ```ts 
1160 |   type getActiveMember = {
1161 |   }
1162 |   ```
1163 | </APIMethod>
1164 | 
1165 | ### Get Active Member Role
1166 | To get the current role member of the active organization you can use the `organization.getActiveMemberRole` function. This function will return the user's member role in their active organization.
1167 | 
1168 | <APIMethod
1169 |   path="/organization/get-active-member-role"
1170 |   method="GET"
1171 |   requireSession
1172 |   resultVariable="{ role }"
1173 | >
1174 |   ```ts
1175 |   type getActiveMemberRole = {
1176 |   }
1177 |   ```
1178 | </APIMethod>
1179 | 
1180 | ### Add Member
1181 | 
1182 | If you want to add a member directly to an organization without sending an invitation, you can use the `addMember` function which can only be invoked on the server.
1183 | 
1184 | <APIMethod
1185 |   path="/organization/add-member"
1186 |   method="POST"
1187 |   isServerOnly
1188 | >
1189 | ```ts
1190 | type addMember = {
1191 |     /**
1192 |      * The user ID which represents the user to be added as a member. If `null` is provided, then it's expected to provide session headers. 
1193 |      */
1194 |     userId?: string | null = "user-id"
1195 |     /**
1196 |      * The role(s) to assign to the new member. 
1197 |      */
1198 |     role: string | string[] = ["admin", "sale"]
1199 |     /**
1200 |      * An optional organization ID to pass. If not provided, will default to the user's active organization. 
1201 |      */
1202 |     organizationId?: string = "org-id"
1203 |     /**
1204 |      * An optional team ID to add the member to. 
1205 |      */
1206 |     teamId?: string = "team-id"
1207 | }
1208 | ```
1209 | </APIMethod>
1210 | 
1211 | ### Leave Organization
1212 | 
1213 | To leave organization you can use `organization.leave` function. This function will remove the current user from the organization.
1214 | 
1215 | <APIMethod
1216 |   path="/organization/leave"
1217 |   method="POST"
1218 |   requireSession
1219 |   noResult
1220 | >
1221 | ```ts
1222 | type leaveOrganization = {
1223 |     /**
1224 |      * The organization ID for the member to leave. 
1225 |      */
1226 |     organizationId: string = "organization-id"
1227 | }
1228 | ```
1229 | </APIMethod>
1230 | 
1231 | ## Access Control
1232 | 
1233 | The organization plugin provides a very flexible access control system. You can control the access of the user based on the role they have in the organization. You can define your own set of permissions based on the role of the user.
1234 | 
1235 | ### Roles
1236 | 
1237 | By default, there are three roles in the organization:
1238 | 
1239 | `owner`: The user who created the organization by default. The owner has full control over the organization and can perform any action.
1240 | 
1241 | `admin`: Users with the admin role have full control over the organization except for deleting the organization or changing the owner.
1242 | 
1243 | `member`: Users with the member role have limited control over the organization. They can create projects, invite users, and manage projects they have created.
1244 | 
1245 | <Callout>
1246 |   A user can have multiple roles. Multiple roles are stored as string separated
1247 |   by comma (",").
1248 | </Callout>
1249 | 
1250 | ### Permissions
1251 | 
1252 | By default, there are three resources, and these have two to three actions.
1253 | 
1254 | **organization**:
1255 | 
1256 |     `update` `delete`
1257 | 
1258 | **member**:
1259 | 
1260 |     `create` `update` `delete`
1261 | 
1262 | **invitation**:
1263 | 
1264 |     `create` `cancel`
1265 | 
1266 | The owner has full control over all the resources and actions. The admin has full control over all the resources except for deleting the organization or changing the owner. The member has no control over any of those actions other than reading the data.
1267 | 
1268 | ### Custom Permissions
1269 | 
1270 | The plugin provides an easy way to define your own set of permissions for each role.
1271 | 
1272 | <Steps>
1273 |     <Step>
1274 |     #### Create Access Control
1275 | 
1276 |     You first need to create access controller by calling `createAccessControl` function and passing the statement object. The statement object should have the resource name as the key and the array of actions as the value.
1277 |     ```ts title="permissions.ts"
1278 |     import { createAccessControl } from "better-auth/plugins/access";
1279 | 
1280 |     /**
1281 |      * make sure to use `as const` so typescript can infer the type correctly
1282 |      */
1283 |     const statement = { // [!code highlight]
1284 |         project: ["create", "share", "update", "delete"], // [!code highlight]
1285 |     } as const; // [!code highlight]
1286 | 
1287 |     const ac = createAccessControl(statement); // [!code highlight]
1288 |     ```
1289 |     </Step>
1290 | 
1291 |     <Step>
1292 |     #### Create Roles
1293 | 
1294 |     Once you have created the access controller you can create roles with the permissions you have defined.
1295 | 
1296 |     ```ts title="permissions.ts"
1297 |     import { createAccessControl } from "better-auth/plugins/access";
1298 | 
1299 |     const statement = {
1300 |         project: ["create", "share", "update", "delete"],
1301 |     } as const;
1302 | 
1303 |     const ac = createAccessControl(statement);
1304 | 
1305 |     const member = ac.newRole({ // [!code highlight]
1306 |         project: ["create"], // [!code highlight]
1307 |     }); // [!code highlight]
1308 | 
1309 |     const admin = ac.newRole({ // [!code highlight]
1310 |         project: ["create", "update"], // [!code highlight]
1311 |     }); // [!code highlight]
1312 | 
1313 |     const owner = ac.newRole({ // [!code highlight]
1314 |         project: ["create", "update", "delete"], // [!code highlight]
1315 |     }); // [!code highlight]
1316 | 
1317 |     const myCustomRole = ac.newRole({ // [!code highlight]
1318 |         project: ["create", "update", "delete"], // [!code highlight]
1319 |         organization: ["update"], // [!code highlight]
1320 |     }); // [!code highlight]
1321 |     ```
1322 | 
1323 |       When you create custom roles for existing roles, the predefined permissions for those roles will be overridden. To add the existing permissions to the custom role, you need to import `defaultStatements` and merge it with your new statement, plus merge the roles' permissions set with the default roles.
1324 | 
1325 |     ```ts title="permissions.ts"
1326 |     import { createAccessControl } from "better-auth/plugins/access";
1327 |     import { defaultStatements, adminAc } from 'better-auth/plugins/organization/access'
1328 | 
1329 |     const statement = {
1330 |         ...defaultStatements, // [!code highlight]
1331 |         project: ["create", "share", "update", "delete"],
1332 |     } as const;
1333 | 
1334 |     const ac = createAccessControl(statement);
1335 | 
1336 |     const admin = ac.newRole({
1337 |         project: ["create", "update"],
1338 |         ...adminAc.statements, // [!code highlight]
1339 |     });
1340 |     ```
1341 | 
1342 |     </Step>
1343 | 
1344 |     <Step>
1345 |         #### Pass Roles to the Plugin
1346 | 
1347 |         Once you have created the roles you can pass them to the organization plugin both on the client and the server.
1348 | 
1349 |         ```ts title="auth.ts"
1350 |         import { betterAuth } from "better-auth"
1351 |         import { organization } from "better-auth/plugins"
1352 |         import { ac, owner, admin, member } from "@/auth/permissions"
1353 | 
1354 |         export const auth = betterAuth({
1355 |             plugins: [
1356 |                 organization({
1357 |                     ac,
1358 |                     roles: {
1359 |                         owner,
1360 |                         admin,
1361 |                         member,
1362 |                         myCustomRole
1363 |                     }
1364 |                 }),
1365 |             ],
1366 |         });
1367 |         ```
1368 | 
1369 |         You also need to pass the access controller and the roles to the client plugin.
1370 | 
1371 |         ```ts title="auth-client"
1372 |         import { createAuthClient } from "better-auth/client"
1373 |         import { organizationClient } from "better-auth/client/plugins"
1374 |         import { ac, owner, admin, member, myCustomRole } from "@/auth/permissions"
1375 | 
1376 |         export const authClient = createAuthClient({
1377 |             plugins: [
1378 |                 organizationClient({
1379 |                     ac,
1380 |                     roles: {
1381 |                         owner,
1382 |                         admin,
1383 |                         member,
1384 |                         myCustomRole
1385 |                     }
1386 |                 })
1387 |           ]
1388 |         })
1389 |         ```
1390 |     </Step>
1391 | 
1392 | </Steps>
1393 | 
1394 | ### Access Control Usage
1395 | 
1396 | **Has Permission**:
1397 | 
1398 | You can use the `hasPermission` action provided by the `api` to check the permission of the user.
1399 | 
1400 | ```ts title="api.ts"
1401 | import { auth } from "@/auth";
1402 | 
1403 | await auth.api.hasPermission({
1404 |   headers: await headers(),
1405 |   body: {
1406 |     permissions: {
1407 |       project: ["create"], // This must match the structure in your access control
1408 |     },
1409 |   },
1410 | });
1411 | 
1412 | // You can also check multiple resource permissions at the same time
1413 | await auth.api.hasPermission({
1414 |   headers: await headers(),
1415 |   body: {
1416 |     permissions: {
1417 |       project: ["create"], // This must match the structure in your access control
1418 |       sale: ["create"],
1419 |     },
1420 |   },
1421 | });
1422 | ```
1423 | 
1424 | If you want to check the permission of the user on the client from the server you can use the `hasPermission` function provided by the client.
1425 | 
1426 | ```ts title="auth-client.ts"
1427 | const canCreateProject = await authClient.organization.hasPermission({
1428 |   permissions: {
1429 |     project: ["create"],
1430 |   },
1431 | });
1432 | 
1433 | // You can also check multiple resource permissions at the same time
1434 | const canCreateProjectAndCreateSale =
1435 |   await authClient.organization.hasPermission({
1436 |     permissions: {
1437 |       project: ["create"],
1438 |       sale: ["create"],
1439 |     },
1440 |   });
1441 | ```
1442 | 
1443 | **Check Role Permission**:
1444 | 
1445 | Once you have defined the roles and permissions to avoid checking the permission from the server you can use the `checkRolePermission` function provided by the client.
1446 | 
1447 | ```ts title="auth-client.ts"
1448 | const canCreateProject = authClient.organization.checkRolePermission({
1449 |   permissions: {
1450 |     organization: ["delete"],
1451 |   },
1452 |   role: "admin",
1453 | });
1454 | 
1455 | // You can also check multiple resource permissions at the same time
1456 | const canCreateProjectAndCreateSale =
1457 |   authClient.organization.checkRolePermission({
1458 |     permissions: {
1459 |       organization: ["delete"],
1460 |       member: ["delete"],
1461 |     },
1462 |     role: "admin",
1463 |   });
1464 | ```
1465 | 
1466 | <Callout type="warn">
1467 |   This will not include any dynamic roles as everything is ran syncronously on the client side.
1468 |   Please use the [hasPermission](#access-control-usage) APIs to include checks for any dynamic roles & permissions.
1469 | </Callout>
1470 | 
1471 | ---
1472 | 
1473 | ## Dynamic Access Control 
1474 | 
1475 | Dynamic access control allows you to create roles at runtime for organizations. This is achieved by storing the
1476 | created roles and permissions associated with an organization in a database table.
1477 | 
1478 | ### Enabling Dynamic Access Control
1479 | 
1480 | To enable dynamic access control, pass the `dynamicAccessControl` configuration option with `enabled` set to `true` to both server and client plugins.
1481 | 
1482 | Ensure you have pre-defined an `ac` instance on the server auth plugin.
1483 | This is important as this is how we can infer the permissions that are available for use.
1484 | 
1485 | ```ts title="auth.ts"
1486 | import { betterAuth } from "better-auth";
1487 | import { organization } from "better-auth/plugins";
1488 | import { ac } from "@/auth/permissions";
1489 | 
1490 | export const auth = betterAuth({
1491 |     plugins: [ // [!code highlight]
1492 |         organization({ // [!code highlight]
1493 |             ac, // Must be defined in order for dynamic access control to work // [!code highlight]
1494 |             dynamicAccessControl: { // [!code highlight]
1495 |               enabled: true, // [!code highlight]
1496 |             }, // [!code highlight]
1497 |         }) // [!code highlight]
1498 |     ] // [!code highlight]
1499 | })
1500 | ```
1501 | 
1502 | ```ts title="auth-client.ts"
1503 | import { createAuthClient } from "better-auth/client";
1504 | import { organizationClient } from "better-auth/client/plugins";
1505 | 
1506 | export const authClient = createAuthClient({
1507 |     plugins: [ // [!code highlight]
1508 |         organizationClient({ // [!code highlight]
1509 |             dynamicAccessControl: { // [!code highlight]
1510 |               enabled: true, // [!code highlight]
1511 |             }, // [!code highlight]
1512 |         }) // [!code highlight]
1513 |     ] // [!code highlight]
1514 | })
1515 | ```
1516 | 
1517 | <Callout>
1518 |   This will require you to run migrations to add the new `organizationRole` table to the database.
1519 | </Callout>
1520 | 
1521 | 
1522 | <Callout type="warn">
1523 |   The `authClient.organization.checkRolePermission` function will not include any dynamic roles as everything is ran syncronously on the client side.
1524 |   Please use the [hasPermission](#access-control-usage) APIs to include checks for any dynamic roles.
1525 | </Callout>
1526 | 
1527 | ### Creating a role
1528 | 
1529 | To create a new role for an organization at runtime, you can use the `createRole` function.
1530 | 
1531 | Only users with roles which contain the `ac` resource with the `create` permission can create a new role.
1532 | By default, only the `admin` and `owner` roles have this permission. You also cannot add permissions that your
1533 | current role in that organization can't already access.
1534 | 
1535 | <Callout>
1536 |   TIP: You can validate role names by using the `dynamicAccessControl.validateRoleName` option in the organization plugin config.
1537 |   Learn more [here](#validaterolename).
1538 | </Callout>
1539 | 
1540 | 
1541 | <APIMethod
1542 |   path="/organization/create-role"
1543 |   method="POST"
1544 |   requireSession
1545 |   noResult
1546 | >
1547 | ```ts
1548 | // To use custom resources or permissions,
1549 | // make sure they are defined in the `ac` instance of your organization config.
1550 | const permission = {
1551 |   project: ["create", "update", "delete"]
1552 | }
1553 | 
1554 | type createOrgRole = {
1555 |     /**
1556 |      * A unique name of the role to create.
1557 |      */
1558 |     role: string = "my-unique-role"
1559 |     /**
1560 |      * The permissions to assign to the role.
1561 |      */
1562 |     permission?: Record<string, string[]> = permission,
1563 |     /**
1564 |      * The organization ID which the role will be created in. Defaults to the active organization.
1565 |      */
1566 |     organizationId?: string = "organization-id"
1567 | }
1568 | ```
1569 | </APIMethod>
1570 | 
1571 | Now you can freely call [`updateMemberRole`](#updating-a-member-role) to update the role of a member with your newly created role!
1572 | 
1573 | ### Deleting a role
1574 | 
1575 | To delete a role, you can use the `deleteRole` function, then provide either a `roleName` or `roleId` parameter along
1576 | with the `organizationId` parameter.
1577 | 
1578 | <APIMethod
1579 |   path="/organization/delete-role"
1580 |   method="POST"
1581 |   requireSession
1582 |   noResult
1583 | >
1584 | ```ts
1585 | type deleteOrgRole = {
1586 |     /**
1587 |      * The name of the role to delete. Alternatively, you can pass a `roleId` parameter instead.
1588 |      */
1589 |     roleName?: string = "my-role"
1590 |     /**
1591 |      * The id of the role to delete. Alternatively, you can pass a `roleName` parameter instead.
1592 |      */
1593 |     roleId?: string = "role-id"
1594 |     /**
1595 |      * The organization ID which the role will be deleted in. Defaults to the active organization.
1596 |      */
1597 |     organizationId?: string = "organization-id"
1598 | }
1599 | ```
1600 | </APIMethod>
1601 | 
1602 | ### Listing roles
1603 | 
1604 | To list roles, you can use the `listOrgRoles` function.
1605 | This requires the `ac` resource with the `read` permission for the member to be able to list roles.
1606 | 
1607 | <APIMethod
1608 |   path="/organization/list-roles"
1609 |   method="GET"
1610 |   requireSession
1611 |   resultVariable="roles"
1612 | >
1613 | ```ts
1614 | type listOrgRoles = {
1615 |     /**
1616 |      * The organization ID which the roles are under to list. Defaults to the user's active organization. 
1617 |      */
1618 |     organizationId?: string = "organization-id"
1619 | }
1620 | ```
1621 | </APIMethod>
1622 | 
1623 | ### Getting a specific role
1624 | 
1625 | To get a specific role, you can use the `getOrgRole` function and pass either a `roleName` or `roleId` parameter.
1626 | This requires the `ac` resource with the `read` permission for the member to be able to get a role.
1627 | 
1628 | 
1629 | <APIMethod
1630 |   path="/organization/get-role"
1631 |   method="GET"
1632 |   requireSession
1633 |   resultVariable="role"
1634 | >
1635 | ```ts
1636 | type getOrgRole = {
1637 |     /**
1638 |      * The name of the role to get. Alternatively, you can pass a `roleId` parameter instead.
1639 |      */
1640 |     roleName?: string = "my-role"
1641 |     /**
1642 |      * The id of the role to get. Alternatively, you can pass a `roleName` parameter instead.
1643 |      */
1644 |     roleId?: string = "role-id"
1645 |     /**
1646 |      * The organization ID which the role will be deleted in. Defaults to the active organization.
1647 |      */
1648 |     organizationId?: string = "organization-id"
1649 | }
1650 | ```
1651 | </APIMethod>
1652 | 
1653 | ### Updating a role
1654 | 
1655 | To update a role, you can use the `updateOrgRole` function and pass either a `roleName` or `roleId` parameter.
1656 | 
1657 | 
1658 | <APIMethod
1659 |   path="/organization/update-role"
1660 |   method="POST"
1661 |   requireSession
1662 |   resultVariable="updatedRole"
1663 | >
1664 | ```ts
1665 | type updateOrgRole = {
1666 |     /**
1667 |      * The name of the role to update. Alternatively, you can pass a `roleId` parameter instead.
1668 |      */
1669 |     roleName?: string = "my-role"
1670 |     /**
1671 |      * The id of the role to update. Alternatively, you can pass a `roleName` parameter instead.
1672 |      */
1673 |     roleId?: string = "role-id"
1674 |     /**
1675 |      * The organization ID which the role will be updated in. Defaults to the active organization.
1676 |      */
1677 |     organizationId?: string = "organization-id"
1678 |     /**
1679 |      * The data which will be updated
1680 |     */
1681 |     data: {
1682 |       /**
1683 |        * Optionally update the permissions of the role.
1684 |        */
1685 |       permission?: Record<string, string[]> = { project: ["create", "update", "delete"] }
1686 |       /**
1687 |        * Optionally update the name of the role.
1688 |        */
1689 |       roleName?: string = "my-new-role"
1690 |     }
1691 | }
1692 | ```
1693 | </APIMethod>
1694 | 
1695 | 
1696 | ### Configuration Options
1697 | 
1698 | Below is a list of options that can be passed to the `dynamicAccessControl` object.
1699 | 
1700 | #### `enabled`
1701 | 
1702 | This option is used to enable or disable dynamic access control. By default, it is disabled.
1703 | 
1704 | ```ts
1705 | organization({
1706 |   dynamicAccessControl: {
1707 |     enabled: true // [!code highlight]
1708 |   }
1709 | })
1710 | ```
1711 | 
1712 | #### `maximumRolesPerOrganization`
1713 | 
1714 | This option is used to limit the number of roles that can be created for an organization.
1715 | 
1716 | By default, the maximum number of roles that can be created for an organization is infinite.
1717 | 
1718 | ```ts
1719 | organization({
1720 |   dynamicAccessControl: {
1721 |     maximumRolesPerOrganization: 10 // [!code highlight]
1722 |   }
1723 | })
1724 | ```
1725 | 
1726 | You can also pass a function that returns a number.
1727 | 
1728 | ```ts
1729 | organization({
1730 |   dynamicAccessControl: {
1731 |     maximumRolesPerOrganization: async (organizationId) => { // [!code highlight]
1732 |       const organization = await getOrganization(organizationId); // [!code highlight]
1733 |       return organization.plan === "pro" ? 100 : 10; // [!code highlight]
1734 |     } // [!code highlight]
1735 |   }
1736 | })
1737 | ```
1738 | 
1739 | ### Additional Fields
1740 | 
1741 | To add additional fields to the `organizationRole` table, you can pass the `additionalFields` configuration option to the `organization` plugin.
1742 | 
1743 | ```ts
1744 | organization({
1745 |   schema: {
1746 |     organizationRole: {
1747 |       additionalFields: {
1748 |         // Role colors!
1749 |         color: {
1750 |           type: "string",
1751 |           defaultValue: "#ffffff",
1752 |         },
1753 |         //... other fields
1754 |       },
1755 |     },
1756 |   },
1757 | })
1758 | ```
1759 | 
1760 | Then, if you don't already use `inferOrgAdditionalFields` to infer the additional fields, you can use it to infer the additional fields.
1761 | 
1762 | ```ts title="auth-client.ts"
1763 | import { createAuthClient } from "better-auth/client"
1764 | import { organizationClient, inferOrgAdditionalFields } from "better-auth/client/plugins"
1765 | import type { auth } from "./auth"
1766 | 
1767 | export const authClient = createAuthClient({
1768 |     plugins: [
1769 |         organizationClient({
1770 |             schema: inferOrgAdditionalFields<typeof auth>()
1771 |         })
1772 |     ]
1773 | })
1774 | ```
1775 | Otherwise, you can pass the schema values directly, the same way you do on the org plugin in the server.
1776 | 
1777 | ```ts title="auth-client.ts"
1778 | import { createAuthClient } from "better-auth/client"
1779 | import { organizationClient } from "better-auth/client/plugins"
1780 | 
1781 | export const authClient = createAuthClient({
1782 |     plugins: [
1783 |         organizationClient({
1784 |             schema: {
1785 |                 organizationRole: {
1786 |                     additionalFields: {
1787 |                         color: {
1788 |                             type: "string",
1789 |                             defaultValue: "#ffffff",
1790 |                         }
1791 |                     }
1792 |                 }
1793 |             }
1794 |         })
1795 |     ]
1796 | })
1797 | ```
1798 | 
1799 | 
1800 | ---
1801 | 
1802 | ## Teams
1803 | 
1804 | Teams allow you to group members within an organization. The teams feature provides additional organization structure and can be used to manage permissions at a more granular level.
1805 | 
1806 | ### Enabling Teams
1807 | 
1808 | To enable teams, pass the `teams` configuration option to both server and client plugins:
1809 | 
1810 | ```ts title="auth.ts"
1811 | import { betterAuth } from "better-auth";
1812 | import { organization } from "better-auth/plugins";
1813 | 
1814 | export const auth = betterAuth({
1815 |   plugins: [
1816 |     organization({
1817 |       teams: {
1818 |         enabled: true,
1819 |         maximumTeams: 10, // Optional: limit teams per organization
1820 |         allowRemovingAllTeams: false, // Optional: prevent removing the last team
1821 |       },
1822 |     }),
1823 |   ],
1824 | });
1825 | ```
1826 | 
1827 | ```ts title="auth-client.ts"
1828 | import { createAuthClient } from "better-auth/client";
1829 | import { organizationClient } from "better-auth/client/plugins";
1830 | 
1831 | export const authClient = createAuthClient({
1832 |   plugins: [
1833 |     organizationClient({
1834 |       teams: {
1835 |         enabled: true,
1836 |       },
1837 |     }),
1838 |   ],
1839 | });
1840 | ```
1841 | 
1842 | ### Managing Teams
1843 | 
1844 | #### Create Team
1845 | 
1846 | Create a new team within an organization:
1847 | 
1848 | <APIMethod path="/organization/create-team" method="POST">
1849 | ```ts
1850 | type createTeam = {
1851 |     /**
1852 |      * The name of the team. 
1853 |      */
1854 |     name: string = "my-team"
1855 |     /**
1856 |      * The organization ID which the team will be created in. Defaults to the active organization. 
1857 |      */
1858 |     organizationId?: string = "organization-id"
1859 | }
1860 | ```
1861 | </APIMethod>
1862 | 
1863 | #### List Teams
1864 | 
1865 | Get all teams in an organization:
1866 | 
1867 | <APIMethod
1868 |   path="/organization/list-teams"
1869 |   method="GET"
1870 |   requireSession
1871 | >
1872 | ```ts
1873 | type listOrganizationTeams = {
1874 |     /**
1875 |     * The organization ID which the teams are under to list. Defaults to the user's active organization. 
1876 |     */
1877 |     organizationId?: string = "organization-id"
1878 | }
1879 | ```
1880 | </APIMethod>
1881 | 
1882 | #### Update Team
1883 | 
1884 | Update a team's details:
1885 | 
1886 | <APIMethod
1887 |   path="/organization/update-team"
1888 |   method="POST"
1889 |   requireSession
1890 | >
1891 | ```ts
1892 | type updateTeam = {
1893 |     /**
1894 |      * The ID of the team to be updated. 
1895 |      */
1896 |     teamId: string = "team-id"
1897 |     /**
1898 |      * A partial object containing options for you to update.
1899 |      */
1900 |     data: {
1901 |         /**
1902 |          * The name of the team to be updated.
1903 |          */
1904 |         name?: string = "My new team name"
1905 |         /**
1906 |          * The organization ID which the team falls under.
1907 |          */
1908 |         organizationId?: string = "My new organization ID for this team"
1909 |         /**
1910 |          * The timestamp of when the team was created.
1911 |          */
1912 |         createdAt?: Date = new Date()
1913 |         /**
1914 |          * The timestamp of when the team was last updated.
1915 |          */
1916 |         updatedAt?: Date = new Date()
1917 |     }
1918 | }
1919 | ```
1920 | </APIMethod>
1921 | 
1922 | #### Remove Team
1923 | 
1924 | Delete a team from an organization:
1925 | 
1926 | <APIMethod path="/organization/remove-team" method="POST">
1927 | ```ts
1928 | type removeTeam = {
1929 |     /**
1930 |      * The team ID of the team to remove. 
1931 |      */
1932 |     teamId: string = "team-id"
1933 |     /**
1934 |      * The organization ID which the team falls under. If not provided, it will default to the user's active organization. 
1935 |      */
1936 |     organizationId?: string = "organization-id"
1937 | }
1938 | ```
1939 | </APIMethod>
1940 | 
1941 | #### Set Active Team
1942 | 
1943 | Sets the given team as the current active team. If `teamId` is `null` the current active team is unset.
1944 | 
1945 | <APIMethod path="/organization/set-active-team" method="POST">
1946 | ```ts
1947 | type setActiveTeam = {
1948 |     /**
1949 |      * The team ID of the team to set as the current active team.
1950 |      */
1951 |     teamId?: string = "team-id"
1952 | }
1953 | ```
1954 | </APIMethod>
1955 | 
1956 | #### List User Teams
1957 | 
1958 | List all teams that the current user is a part of.
1959 | 
1960 | <APIMethod path="/organization/list-user-teams" method="GET">
1961 |   ```ts 
1962 |   type listUserTeams = {
1963 |   }
1964 |   ```
1965 | </APIMethod>
1966 | 
1967 | #### List Team Members
1968 | 
1969 | List the members of the given team.
1970 | 
1971 | <APIMethod path="/organization/list-team-members" method="POST">
1972 | ```ts
1973 | type listTeamMembers = {
1974 |     /**
1975 |      * The team whose members we should return. If this is not provided the members of the current active team get returned.
1976 |      */
1977 |     teamId?: string = "team-id"
1978 | }
1979 | ```
1980 | </APIMethod>
1981 | 
1982 | #### Add Team Member
1983 | 
1984 | Add a member to a team.
1985 | 
1986 | <APIMethod path="/organization/add-team-member" method="POST">
1987 | ```ts
1988 | type addTeamMember = {
1989 |     /**
1990 |      * The team the user should be a member of.
1991 |      */
1992 |     teamId: string = "team-id"
1993 |     /**
1994 |      * The user ID which represents the user to be added as a member.
1995 |      */
1996 |     userId: string = "user-id"
1997 | }
1998 | ```
1999 | </APIMethod>
2000 | 
2001 | #### Remove Team Member
2002 | 
2003 | Remove a member from a team.
2004 | 
2005 | <APIMethod path="/organization/remove-team-member" method="POST">
2006 | ```ts
2007 | type removeTeamMember = {
2008 |     /**
2009 |      * The team the user should be removed from.
2010 |      */
2011 |     teamId: string = "team-id"
2012 |     /**
2013 |      * The user which should be removed from the team.
2014 |      */
2015 |     userId: string = "user-id"
2016 | }
2017 | ```
2018 | </APIMethod>
2019 | 
2020 | ### Team Permissions
2021 | 
2022 | Teams follow the organization's permission system. To manage teams, users need the following permissions:
2023 | 
2024 | - `team:create` - Create new teams
2025 | - `team:update` - Update team details
2026 | - `team:delete` - Remove teams
2027 | 
2028 | By default:
2029 | 
2030 | - Organization owners and admins can manage teams
2031 | - Regular members cannot create, update, or delete teams
2032 | 
2033 | ### Team Configuration Options
2034 | 
2035 | The teams feature supports several configuration options:
2036 | 
2037 | - `maximumTeams`: Limit the number of teams per organization
2038 | 
2039 |   ```ts
2040 |   teams: {
2041 |     enabled: true,
2042 |     maximumTeams: 10 // Fixed number
2043 |     // OR
2044 |     maximumTeams: async ({ organizationId, session }, request) => {
2045 |       // Dynamic limit based on organization plan
2046 |       const plan = await getPlan(organizationId)
2047 |       return plan === 'pro' ? 20 : 5
2048 |     },
2049 |     maximumMembersPerTeam: 10 // Fixed number
2050 |     // OR
2051 |     maximumMembersPerTeam: async ({ teamId, session, organizationId }, request) => {
2052 |       // Dynamic limit based on team plan
2053 |       const plan = await getPlan(organizationId, teamId)
2054 |       return plan === 'pro' ? 50 : 10
2055 |     },
2056 |   }
2057 |   ```
2058 | 
2059 | - `allowRemovingAllTeams`: Control whether the last team can be removed
2060 |   ```ts
2061 |   teams: {
2062 |     enabled: true,
2063 |     allowRemovingAllTeams: false // Prevent removing the last team
2064 |   }
2065 |   ```
2066 | 
2067 | ### Team Members
2068 | 
2069 | When inviting members to an organization, you can specify a team:
2070 | 
2071 | ```ts
2072 | await authClient.organization.inviteMember({
2073 |   email: "[email protected]",
2074 |   role: "member",
2075 |   teamId: "team-id",
2076 | });
2077 | ```
2078 | 
2079 | The invited member will be added to the specified team upon accepting the invitation.
2080 | 
2081 | ### Database Schema
2082 | 
2083 | When teams are enabled, new `team` and `teamMember` tables are added to the database.
2084 | 
2085 | Table Name: `team`
2086 | 
2087 | <DatabaseTable
2088 |   fields={[
2089 |     {
2090 |       name: "id",
2091 |       type: "string",
2092 |       description: "Unique identifier for each team",
2093 |       isPrimaryKey: true,
2094 |     },
2095 |     {
2096 |       name: "name",
2097 |       type: "string",
2098 |       description: "The name of the team",
2099 |     },
2100 |     {
2101 |       name: "organizationId",
2102 |       type: "string",
2103 |       description: "The ID of the organization",
2104 |       isForeignKey: true,
2105 |     },
2106 |     {
2107 |       name: "createdAt",
2108 |       type: "Date",
2109 |       description: "Timestamp of when the team was created",
2110 |     },
2111 |     {
2112 |       name: "updatedAt",
2113 |       type: "Date",
2114 |       isOptional: true,
2115 |       description: "Timestamp of when the team was created",
2116 |     },
2117 |   ]}
2118 | />
2119 | 
2120 | Table Name: `teamMember`
2121 | 
2122 | <DatabaseTable
2123 |   fields={[
2124 |     {
2125 |       name: "id",
2126 |       type: "string",
2127 |       description: "Unique identifier for each team member",
2128 |       isPrimaryKey: true,
2129 |     },
2130 |     {
2131 |       name: "teamId",
2132 |       type: "string",
2133 |       description: "Unique identifier for each team",
2134 |       isForeignKey: true,
2135 |     },
2136 |     {
2137 |       name: "userId",
2138 |       type: "string",
2139 |       description: "The ID of the user",
2140 |       isForeignKey: true,
2141 |     },
2142 |     {
2143 |       name: "createdAt",
2144 |       type: "Date",
2145 |       description: "Timestamp of when the team member was created",
2146 |     },
2147 |   ]}
2148 | />
2149 | 
2150 | ## Schema
2151 | 
2152 | The organization plugin adds the following tables to the database:
2153 | 
2154 | ### Organization
2155 | 
2156 | Table Name: `organization`
2157 | 
2158 | <DatabaseTable
2159 |   fields={[
2160 |     {
2161 |       name: "id",
2162 |       type: "string",
2163 |       description: "Unique identifier for each organization",
2164 |       isPrimaryKey: true,
2165 |     },
2166 |     {
2167 |       name: "name",
2168 |       type: "string",
2169 |       description: "The name of the organization",
2170 |     },
2171 |     {
2172 |       name: "slug",
2173 |       type: "string",
2174 |       description: "The slug of the organization",
2175 |     },
2176 |     {
2177 |       name: "logo",
2178 |       type: "string",
2179 |       description: "The logo of the organization",
2180 |       isOptional: true,
2181 |     },
2182 |     {
2183 |       name: "metadata",
2184 |       type: "string",
2185 |       description: "Additional metadata for the organization",
2186 |       isOptional: true,
2187 |     },
2188 |     {
2189 |       name: "createdAt",
2190 |       type: "Date",
2191 |       description: "Timestamp of when the organization was created",
2192 |     },
2193 |   ]}
2194 | />
2195 | 
2196 | ### Member
2197 | 
2198 | Table Name: `member`
2199 | 
2200 | <DatabaseTable
2201 |   fields={[
2202 |     {
2203 |       name: "id",
2204 |       type: "string",
2205 |       description: "Unique identifier for each member",
2206 |       isPrimaryKey: true,
2207 |     },
2208 |     {
2209 |       name: "userId",
2210 |       type: "string",
2211 |       description: "The ID of the user",
2212 |       isForeignKey: true,
2213 |     },
2214 |     {
2215 |       name: "organizationId",
2216 |       type: "string",
2217 |       description: "The ID of the organization",
2218 |       isForeignKey: true,
2219 |     },
2220 |     {
2221 |       name: "role",
2222 |       type: "string",
2223 |       description: "The role of the user in the organization",
2224 |     },
2225 |     {
2226 |       name: "createdAt",
2227 |       type: "Date",
2228 |       description: "Timestamp of when the member was added to the organization",
2229 |     },
2230 |   ]}
2231 | />
2232 | 
2233 | ### Invitation
2234 | 
2235 | Table Name: `invitation`
2236 | 
2237 | <DatabaseTable
2238 |   fields={[
2239 |     {
2240 |       name: "id",
2241 |       type: "string",
2242 |       description: "Unique identifier for each invitation",
2243 |       isPrimaryKey: true,
2244 |     },
2245 |     {
2246 |       name: "email",
2247 |       type: "string",
2248 |       description: "The email address of the user",
2249 |     },
2250 |     {
2251 |       name: "inviterId",
2252 |       type: "string",
2253 |       description: "The ID of the inviter",
2254 |       isForeignKey: true,
2255 |     },
2256 |     {
2257 |       name: "organizationId",
2258 |       type: "string",
2259 |       description: "The ID of the organization",
2260 |       isForeignKey: true,
2261 |     },
2262 |     {
2263 |       name: "role",
2264 |       type: "string",
2265 |       description: "The role of the user in the organization",
2266 |     },
2267 |     {
2268 |       name: "status",
2269 |       type: "string",
2270 |       description: "The status of the invitation",
2271 |     },
2272 |     {
2273 |       name: "createdAt",
2274 |       type: "Date",
2275 |       description: "Timestamp of when the invitation was created"
2276 |     },
2277 |     {
2278 |       name: "expiresAt",
2279 |       type: "Date",
2280 |       description: "Timestamp of when the invitation expires",
2281 |     },
2282 |   ]}
2283 | />
2284 | 
2285 | If teams are enabled, you need to add the following fields to the invitation table:
2286 | 
2287 | <DatabaseTable
2288 |   fields={[
2289 |     {
2290 |       name: "teamId",
2291 |       type: "string",
2292 |       description: "The ID of the team",
2293 |       isOptional: true,
2294 |     },
2295 |   ]}
2296 | />
2297 | 
2298 | ### Session
2299 | 
2300 | Table Name: `session`
2301 | 
2302 | You need to add two more fields to the session table to store the active organization ID and the active team ID.
2303 | 
2304 | <DatabaseTable
2305 |   fields={[
2306 |     {
2307 |       name: "activeOrganizationId",
2308 |       type: "string",
2309 |       description: "The ID of the active organization",
2310 |       isOptional: true,
2311 |     },
2312 |     {
2313 |       name: "activeTeamId",
2314 |       type: "string",
2315 |       description: "The ID of the active team",
2316 |       isOptional: true,
2317 |     },
2318 |   ]}
2319 | />
2320 | 
2321 | ### Teams (optional)
2322 | 
2323 | Table Name: `team`
2324 | 
2325 | <DatabaseTable
2326 |   fields={[
2327 |     {
2328 |       name: "id",
2329 |       type: "string",
2330 |       description: "Unique identifier for each team",
2331 |       isPrimaryKey: true,
2332 |     },
2333 |     {
2334 |       name: "name",
2335 |       type: "string",
2336 |       description: "The name of the team",
2337 |     },
2338 |     {
2339 |       name: "organizationId",
2340 |       type: "string",
2341 |       description: "The ID of the organization",
2342 |       isForeignKey: true,
2343 |     },
2344 |     {
2345 |       name: "createdAt",
2346 |       type: "Date",
2347 |       description: "Timestamp of when the team was created",
2348 |     },
2349 |     {
2350 |       name: "updatedAt",
2351 |       type: "Date",
2352 |       isOptional: true,
2353 |       description: "Timestamp of when the team was created",
2354 |     },
2355 |   ]}
2356 | />
2357 | 
2358 | Table Name: `teamMember`
2359 | 
2360 | <DatabaseTable
2361 |   fields={[
2362 |     {
2363 |       name: "id",
2364 |       type: "string",
2365 |       description: "Unique identifier for each team member",
2366 |       isPrimaryKey: true,
2367 |     },
2368 |     {
2369 |       name: "teamId",
2370 |       type: "string",
2371 |       description: "Unique identifier for each team",
2372 |       isForeignKey: true,
2373 |     },
2374 |     {
2375 |       name: "userId",
2376 |       type: "string",
2377 |       description: "The ID of the user",
2378 |       isForeignKey: true,
2379 |     },
2380 |     {
2381 |       name: "createdAt",
2382 |       type: "Date",
2383 |       description: "Timestamp of when the team member was created",
2384 |     },
2385 |   ]}
2386 | />
2387 | 
2388 | Table Name: `invitation`
2389 | 
2390 | <DatabaseTable
2391 |   fields={[
2392 |     {
2393 |       name: "teamId",
2394 |       type: "string",
2395 |       description: "The ID of the team",
2396 |       isOptional: true,
2397 |     },
2398 |   ]}
2399 | />
2400 | 
2401 | ### Customizing the Schema
2402 | 
2403 | To change the schema table name or fields, you can pass `schema` option to the organization plugin.
2404 | 
2405 | ```ts title="auth.ts"
2406 | const auth = betterAuth({
2407 |   plugins: [
2408 |     organization({
2409 |       schema: {
2410 |         organization: {
2411 |           modelName: "organizations", //map the organization table to organizations
2412 |           fields: {
2413 |             name: "title", //map the name field to title
2414 |           },
2415 |           additionalFields: {
2416 |             // Add a new field to the organization table
2417 |             myCustomField: {
2418 |               type: "string",
2419 |               input: true,
2420 |               required: false,
2421 |             },
2422 |           },
2423 |         },
2424 |       },
2425 |     }),
2426 |   ],
2427 | });
2428 | ```
2429 | 
2430 | #### Additional Fields
2431 | 
2432 | Starting with [Better Auth v1.3](https://github.com/better-auth/better-auth/releases/tag/v1.3.0), you can easily add custom fields to the `organization`, `invitation`, `member`, and `team` tables.
2433 | 
2434 | When you add extra fields to a model, the relevant API endpoints will automatically accept and return these new properties. For instance, if you add a custom field to the `organization` table, the `createOrganization` endpoint will include this field in its request and response payloads as needed.
2435 | 
2436 | ```ts title="auth.ts"
2437 | const auth = betterAuth({
2438 |   plugins: [
2439 |     organization({
2440 |       schema: {
2441 |         organization: {
2442 |           additionalFields: {
2443 |             myCustomField: {
2444 |               // [!code highlight]
2445 |               type: "string", // [!code highlight]
2446 |               input: true, // [!code highlight]
2447 |               required: false, // [!code highlight]
2448 |             }, // [!code highlight]
2449 |           },
2450 |         },
2451 |       },
2452 |     }),
2453 |   ],
2454 | });
2455 | ```
2456 | 
2457 | For inferring the additional fields, you can use the `inferOrgAdditionalFields` function. This function will infer the additional fields from the auth object type.
2458 | 
2459 | ```ts title="auth-client.ts"
2460 | import { createAuthClient } from "better-auth/client";
2461 | import {
2462 |   inferOrgAdditionalFields,
2463 |   organizationClient,
2464 | } from "better-auth/client/plugins";
2465 | import type { auth } from "@/auth"; // import the auth object type only
2466 | 
2467 | const client = createAuthClient({
2468 |   plugins: [
2469 |     organizationClient({
2470 |       schema: inferOrgAdditionalFields<typeof auth>(),
2471 |     }),
2472 |   ],
2473 | });
2474 | ```
2475 | 
2476 | if you can't import the auth object type, you can use the `inferOrgAdditionalFields` function without the generic. This function will infer the additional fields from the schema object.
2477 | 
2478 | ```ts title="auth-client.ts"
2479 | const client = createAuthClient({
2480 |   plugins: [
2481 |     organizationClient({
2482 |       schema: inferOrgAdditionalFields({
2483 |         organization: {
2484 |           // [!code highlight]
2485 |           additionalFields: {
2486 |             newField: {
2487 |               // [!code highlight]
2488 |               type: "string", // [!code highlight]
2489 |             }, // [!code highlight]
2490 |           },
2491 |         },
2492 |       }),
2493 |     }),
2494 |   ],
2495 | });
2496 | 
2497 | //example usage
2498 | await client.organization.create({
2499 |   name: "Test",
2500 |   slug: "test",
2501 |   newField: "123", //this should be allowed
2502 |   //@ts-expect-error - this field is not available
2503 |   unavalibleField: "123", //this should be not allowed
2504 | });
2505 | ```
2506 | 
2507 | ## Options
2508 | 
2509 | **allowUserToCreateOrganization**: `boolean` | `((user: User) => Promise<boolean> | boolean)` - A function that determines whether a user can create an organization. By default, it's `true`. You can set it to `false` to restrict users from creating organizations.
2510 | 
2511 | **organizationLimit**: `number` | `((user: User) => Promise<boolean> | boolean)` - The maximum number of organizations allowed for a user. By default, it's `5`. You can set it to any number you want or a function that returns a boolean.
2512 | 
2513 | **creatorRole**: `admin | owner` - The role of the user who creates the organization. By default, it's `owner`. You can set it to `admin`.
2514 | 
2515 | **membershipLimit**: `number` - The maximum number of members allowed in an organization. By default, it's `100`. You can set it to any number you want.
2516 | 
2517 | **sendInvitationEmail**: `async (data) => Promise<void>` - A function that sends an invitation email to the user.
2518 | 
2519 | **invitationExpiresIn** : `number` - How long the invitation link is valid for in seconds. By default, it's 48 hours (2 days).
2520 | 
2521 | **cancelPendingInvitationsOnReInvite**: `boolean` - Whether to cancel pending invitations if the user is already invited to the organization. By default, it's `false`.
2522 | 
2523 | **invitationLimit**: `number` | `((user: User) => Promise<boolean> | boolean)` - The maximum number of invitations allowed for a user. By default, it's `100`. You can set it to any number you want or a function that returns a boolean.
2524 | 
2525 | **requireEmailVerificationOnInvitation**: `boolean` - Whether to require email verification before accepting or rejecting invitations. By default, it's `false`. When enabled, users must have verified their email address before they can accept or reject organization invitations.
2526 | 
```
Page 65/67FirstPrevNextLast