Beta
The Neon Auth with Better Auth is in Beta. Share your feedback on Discord or via the Neon Console.
Neon Auth is built on Better Auth and provides support for Organization plugin APIs through the Neon SDK. You do not need to manually install or configure the Better Auth Organization plugin.
The Organization plugin allows you to build multi-tenant applications where users can create workspaces, invite other members, and manage permissions using roles.
Preview Feature
The Organization plugin is currently in Beta. Support for invitation emails and JWT token claims is currently in progress.
Prerequisites
- A Neon project with Auth enabled
- A signed-in user (organizations are associated with users)
Organizations
Use the following methods to manage the organization lifecycle.
Create an organization
Creates a new organization. The user creating it automatically becomes the Owner.
View parameters
| Parameter | Type | Required | Notes |
|---|---|---|---|
| name | string | ✓ | The display name of the organization |
| slug | string | ✓ | A URL-friendly identifier |
| logo | string | undefined | Optional URL to a logo image for the organization | |
| metadata | Record<string, any> | undefined | Optional JSON metadata for the organization | |
| userId | string | undefined | The user ID of the organization creator | |
| keepCurrentActiveOrganization | boolean | undefined | If true, does not switch the active session to the new organization |
const { data, error } = await authClient.organization.create({
name: 'My Organization',
slug: 'my-org',
logo: 'https://example.com/logo.png',
metadata: { plan: 'pro' },
keepCurrentActiveOrganization: false,
});Check organization slug
Checks if an organization slug is available.
View parameters
| Parameter | Type | Required | Notes |
|---|---|---|---|
| slug | string | ✓ | The slug to check |
const { data, error } = await authClient.organization.checkSlug({
slug: 'my-org',
});List organizations
Lists all organizations the current user is a member of.
View parameters
This method does not take any parameters.
const { data, error } = await authClient.organization.list();In a React component, you can use the useListOrganizations hook to fetch and display organizations:
import { authClient } from './auth';
export default function OrganizationList() {
const { data: organizations } = authClient.useListOrganizations();
return (
<div>
{organizations?.map((org) => (
<p>{org.name}</p>
))}
</div>
);
}Set active organization
Switches the user's active context to a specific organization.
View parameters
| Parameter | Type | Required | Notes |
|---|---|---|---|
| organizationId | string | null | The ID to set as active. Pass null to unset. | |
| organizationSlug | string | undefined | Alternatively, pass the slug to set as active. |
const { data, error } = await authClient.organization.setActive({
organizationId: 'org_12345678',
});Get active organization
Retrieves full details of the currently active organization.
View parameters
| Parameter | Type | Required | Notes |
|---|---|---|---|
| organizationId | string | undefined | Optional ID to get details for (defaults to active org) | |
| organizationSlug | string | undefined | Optional slug to get details for | |
| membersLimit | number | undefined | Limit members returned in the response (default: 100) |
const { data, error } = await authClient.organization.getFullOrganization({
query: {
organizationId: 'org-id',
organizationSlug: 'org-slug',
membersLimit: 10,
},
});In a React component, you can use the useActiveOrganization hook to fetch and display the active organization:
import { authClient } from './auth';
export default function ActiveOrganization() {
const { data: organization } = authClient.useActiveOrganization();
return (
<div>
<h1>{organization?.name}</h1>
<p>Members: {organization?.members.length}</p>
</div>
);
}Update organization
Updates organization details. Requires Owner or Admin permissions.
View parameters
| Parameter | Type | Required | Notes |
|---|---|---|---|
| data | object | ✓ | Object containing fields to update (name, slug, logo, metadata) |
| organizationId | string | The ID of the organization to update |
await authClient.organization.update({
data: {
name: 'New Name',
metadata: { plan: 'enterprise' },
},
organizationId: 'org-id',
});Delete organization
Deletes the organization and all associated data. Requires Owner permission.
View parameters
| Parameter | Type | Required | Notes |
|---|---|---|---|
| organizationId | string | ✓ | The ID of the organization to delete |
const { data, error } = await authClient.organization.delete({
organizationId: 'org-id',
});Invitations
Manage invitations to join an organization.
Invitation Emails
Invitation emails are not sent during the Beta phase. They will be supported in a future release. In the meantime, users can accept invitations using the invitation ID or by viewing them in their invitation list.
Invite member
Sends an invitation to a user.
View parameters
| Parameter | Type | Required | Notes |
|---|---|---|---|
| string | ✓ | Email address to invite | |
| role | string | ✓ | Role to assign (owner, admin, member) |
| organizationId | string | undefined | ID of the organization (defaults to active org) | |
| resend | boolean | undefined | If true, resends email if invitation already exists |
const { data, error } = await authClient.organization.inviteMember({
email: 'new-user@example.com',
role: 'member',
resend: true,
});Accept invitation
Accepts an invitation using the invitation ID.
View parameters
| Parameter | Type | Required | Notes |
|---|---|---|---|
| invitationId | string | ✓ | The ID from the invitation link |
const { data, error } = await authClient.organization.acceptInvitation({
invitationId: 'invitation-id',
});Reject invitation
Declines an invitation that the user has received and chooses not to accept.
View parameters
| Parameter | Type | Required | Notes |
|---|---|---|---|
| invitationId | string | ✓ | The ID of the invitation to reject |
const { data, error } = await authClient.organization.rejectInvitation({
invitationId: 'invitation-id',
});Cancel invitation
Cancel a pending invitation that has been sent to a user.
View parameters
| Parameter | Type | Required | Notes |
|---|---|---|---|
| invitationId | string | ✓ | The ID of the invitation to cancel |
const { data, error } = await authClient.organization.cancelInvitation({
invitationId: 'invitation-id',
});Get invitation
Retrieves details of a specific invitation.
View parameters
| Parameter | Type | Required | Notes |
|---|---|---|---|
| query.id | string | ✓ | The ID of the invitation to retrieve |
const { data, error } = await authClient.organization.getInvitation({
query: {
id: 'invitation-id',
},
});List invitations
Lists all pending invitations for an organization.
View parameters
| Parameter | Type | Required | Notes |
|---|---|---|---|
| query.organizationId | string | undefined | Defaults to the active organization |
const { data, error } = await authClient.organization.listInvitations({
query: {
organizationId: 'org-id',
},
});List user invitations
Lists all invitations received by the current user.
View parameters
This method does not take any parameters.
const { data, error } = await authClient.organization.listUserInvitations();Members
Manage users within the organization.
List members
Lists members with support for pagination, sorting, and filtering.
View parameters
| Parameter | Type | Required | Notes |
|---|---|---|---|
| query.organizationId | string | undefined | Defaults to the active organization | |
| query.limit | number | undefined | Items per page (default: 100) | |
| query.offset | number | undefined | Items to skip | |
| query.sortBy | string | undefined | Field to sort by (e.g., createdAt) | |
| query.sortDirection | "asc" | "desc" | undefined | Sort direction | |
| query.filterField | string | undefined | Field to filter by | |
| query.filterOperator | "eq" | "ne" | "gt" | "contains" etc. | Operator for filtering | |
| query.filterValue | string | undefined | Value to filter for |
const { data, error } = await authClient.organization.listMembers({
query: {
limit: 20,
offset: 0,
sortBy: 'createdAt',
sortDirection: 'desc',
filterField: 'role',
filterOperator: 'eq',
filterValue: 'admin',
},
});Update member role
Updates a member's role.
View parameters
| Parameter | Type | Required | Notes |
|---|---|---|---|
| memberId | string | ✓ | The ID of the member to update |
| role | string| string[] | ✓ | New role(s) to assign (owner, admin, member) |
| organizationId | string | undefined | Defaults to active organization |
const { data, error } = await authClient.organization.updateMemberRole({
memberId: 'member-id',
role: 'admin',
});Remove member
Removes a member from the organization.
View parameters
| Parameter | Type | Required | Notes |
|---|---|---|---|
| memberIdOrEmail | string | ✓ | Member ID or Email address |
| organizationId | string | undefined | Defaults to active organization |
const { data, error } = await authClient.organization.removeMember({
memberIdOrEmail: 'member-id-or-email',
});Get active member
Gets the current user's membership details for the active organization.
View parameters
This method does not take any parameters.
const { data, error } = await authClient.organization.getActiveMember();Get Active Member Role
Gets the current user's role(s) in the active organization.
View parameters
This method does not take any parameters.
const { data, error } = await authClient.organization.getActiveMemberRole();Leave organization
Removes the current user from an organization.
View parameters
| Parameter | Type | Required | Notes |
|---|---|---|---|
| organizationId | string | ✓ | The ID of the organization to leave |
const { data, error } = await authClient.organization.leave({
organizationId: 'org-id',
});Access Control
The Organization plugin includes a Role-Based Access Control (RBAC) system.
| Role | Permissions |
|---|---|
| Owner | Full control. Can delete the organization and manage all roles. The user who creates the organization is automatically assigned this role. |
| Admin | Can invite members, update roles, and manage organization settings. Cannot delete the organization. |
| Member | Read-only access to organization data. Cannot manage other members. |
You can check permissions on the client side using checkRolePermission:
const canDelete = authClient.organization.checkRolePermission({
permission: {
organization: ['delete'],
},
role: 'admin', // returns false, admins cannot delete orgs
});
// console.log(canDelete); // falseLimitations
Because Neon Auth is a managed service, some Better Auth features are not currently supported:
- Teams: The Teams sub-feature is not currently enabled.
- Hooks: Server-side hooks (e.g.,
beforeCreateOrganization) are not supported. - Custom Permissions: You cannot currently define custom roles or modify default permissions.
- Dynamic Access Control: Dynamic creation of roles via API is not enabled.
Check the Neon Auth roadmap for updates on these features.
Need help?
Join our Discord Server to ask questions or see what others are doing with Neon. For paid plan support options, see Support.








