Organizations
Multi-tenant organization management for your SaaS
Organizations
Turbocamp includes a comprehensive multi-tenant organization system built with Better Auth's organization plugin. This allows your users to create teams, invite members, and manage permissions.
Overview
The organization system provides:
- Multi-tenant Architecture - Users can belong to multiple organizations
- Role-Based Access Control - Owner, admin, and member roles
- Team Management - Invite, remove, and manage team members
- Organization Switching - Seamless context switching
- Scoped Resources - Data isolation between organizations
Organizations are limited to 5 per user and 100 members per organization by default. These limits can be adjusted in the auth configuration.
Key Features
Organization Creation
Users can create organizations with:
- Custom organization name
- URL-friendly slug
- Logo upload (coming soon)
- Metadata storage for custom fields
Member Management
Organization owners and admins can:
- Invite new members via email
- Assign roles (admin or member)
- Remove members
- Update member permissions
Role Hierarchy
- Full control over organization
- Can delete the organization
- Can manage all members and roles
- Cannot be removed by others
- Can invite and remove members
- Can change member roles (except owner)
- Can update organization settings
- Cannot delete the organization
- Basic access to organization resources
- Cannot manage other members
- Cannot change organization settings
- Can leave the organization
Implementation Guide
Creating an Organization
Navigate to the dashboard and click "Create Organization" in the sidebar
Enter organization details:
const result = await organization.create({
name: "Acme Inc",
slug: "acme-inc", // Optional, auto-generated if not provided
});The new organization is automatically set as active:
await organization.setActive({
organizationId: result.data.id,
});Managing Members
Inviting Members
await organization.inviteMember({
organizationId: activeOrg.id,
email: "colleague@example.com",
role: "member", // or "admin"
});Email invitations require email service configuration. Until configured, invitations are logged to the console.
Updating Member Roles
await organization.updateMemberRole({
organizationId: activeOrg.id,
memberIdOrEmail: memberId,
role: "admin",
});Removing Members
await organization.removeMember({
organizationId: activeOrg.id,
memberIdOrEmail: memberId,
});Organization Switching
The organization switcher is available in the dashboard sidebar:
// Get list of user's organizations
const { data: organizations } = useListOrganizations();
// Get current active organization
const { data: activeOrg } = useActiveOrganization();
// Switch to a different organization
await organization.setActive({
organizationId: orgId,
});Database Schema
The organization system uses these Prisma models:
model Organization {
id String @id
name String
slug String?
logo String?
createdAt DateTime
metadata String?
members Member[]
invitations Invitation[]
@@unique([slug])
}
model Member {
id String @id
organizationId String
organization Organization @relation(...)
userId String
user User @relation(...)
role String
createdAt DateTime
}
model Invitation {
id String @id
organizationId String
organization Organization @relation(...)
email String
role String?
status String
expiresAt DateTime
inviterId String
user User @relation(...)
}API Endpoints
Better Auth automatically provides these endpoints:
POST /api/auth/organization.create- Create new organizationPOST /api/auth/organization.update- Update organization detailsPOST /api/auth/organization.delete- Delete organizationPOST /api/auth/organization.setActive- Switch active organizationPOST /api/auth/organization.inviteMember- Send invitationPOST /api/auth/organization.removeMember- Remove memberPOST /api/auth/organization.updateMemberRole- Change member roleGET /api/auth/organization.getFullOrganization- Get organization with members
Configuration
Organizations are configured in packages/auth/server.ts:
import { organization } from 'better-auth/plugins';
export const auth = betterAuth({
plugins: [
organization({
allowUserToCreateOrganization: true,
organizationLimit: 5,
membershipLimit: 100,
creatorRole: 'owner',
sendInvitationEmail: async (data) => {
// Implement email sending
await sendEmail({
to: data.email,
subject: `Invitation to join ${data.organization.name}`,
// ... email template
});
},
}),
],
});Best Practices
- Scope All Resources - Always filter data by organization ID
- Check Permissions - Verify user roles before allowing actions
- Handle Edge Cases - Users with no organizations, deleted organizations
- Audit Logging - Track organization changes for compliance
- Rate Limiting - Prevent abuse of invitation system
Troubleshooting
Common Issues
Problem: User sees "No Organization" after sign up
Solution: Redirect new users to /organization/new or auto-create a default organization:
if (!activeOrg && organizations.length === 0) {
router.push('/organization/new');
}Problem: User cannot perform organization actions
Solution: Check user role in the organization:
const member = organization.members.find(m => m.userId === user.id);
if (member.role !== 'owner' && member.role !== 'admin') {
throw new Error('Insufficient permissions');
}Problem: Email invitations not being sent
Solution: Configure email service in auth settings:
sendInvitationEmail: async (data) => {
// Ensure email service is configured
if (!process.env.RESEND_API_KEY) {
console.log('Email invitation:', data);
return;
}
// Send actual email
}Next Steps
- Set up email service for invitations
- Configure billing per organization
- Implement webhooks for organization events
- Add custom roles beyond owner/admin/member