Skip to content

Organization Data Models

Multi-tenant organization system models managed by Better Auth's organization plugin.

Overview

Organizations provide multi-tenant workspace functionality, allowing users to belong to multiple organizations with different roles. All organization data is managed through Better Auth's organization plugin integrated with AdonisJS and PostgreSQL.

Models

Organization

Multi-tenant workspaces that group users, events, and resources.

Fields:

  • id - UUID primary key
  • owner_id - Reference to User who created the organization
  • name - Organization display name
  • slug - URL-safe identifier (unique)
  • logo - Optional logo URL
  • metadata - Additional custom data (JSONB)
  • created_at - Creation timestamp

Indexes:

  • Primary key on id
  • Index on owner_id for owner lookups
  • Unique index on slug for URL routing

Member

Organization membership records with role-based access.

Fields:

  • id - Membership record ID
  • user_id - Reference to User
  • organization_id - Reference to Organization
  • role - Role within organization (owner, admin, member)
  • team_id - Optional team assignment
  • created_at - When user joined organization

Roles:

  • owner - Organization creator, full permissions
  • admin - Administrative permissions
  • member - Standard member permissions

Indexes:

  • Primary key on id
  • Composite index on (user_id, organization_id) for membership lookups
  • Index on organization_id for listing members
  • Index on team_id for team-specific queries

Team

Sub-groups within organizations for grouping members.

Fields:

  • id - Team ID
  • organization_id - Parent organization
  • name - Team name
  • created_at - Creation timestamp
  • updated_at - Last modification timestamp

Indexes:

  • Primary key on id
  • Index on organization_id for listing teams
  • Composite unique index on (organization_id, name)

Invitation

Pending organization invitations with expiry.

Fields:

  • id - Invitation ID
  • organization_id - Target organization
  • team_id - Optional team ID for team-specific invitations
  • email - Invitee email address
  • role - Role to assign upon acceptance
  • invited_by - User ID of member who sent invitation
  • status - Invitation status (pending, accepted, declined)
  • expires_at - When invitation expires
  • created_at - When invitation was sent

Indexes:

  • Primary key on id
  • Index on organization_id for listing invitations
  • Index on email for user invitation lookups
  • Index on status for filtering by status
  • Index on expires_at for expiring invitations

Relationships

Key Relationships:

  • User → Organization (as owner, one-to-many)
  • User → Member (user can be member of many organizations)
  • Organization → Member (organization has many members)
  • Organization → Team (organization contains teams)
  • Member → Team (members can be assigned to teams)
  • Organization → Invitation (organization can send invitations)

Limits & Constraints

These limits are enforced by Better Auth in app/lib/auth.ts:

ConstraintLimitSource
Max organizations per user5organizationLimit in app/lib/auth.ts
Max members per organization100membershipLimit in app/lib/auth.ts

Owner Protection

The Member model has a @beforeDelete hook that prevents removing the organization owner:

  • Loads the organization to check ownerId
  • Throws if member.userId === member.organization.ownerId
  • Error message: "Cannot remove the organization owner... Transfer ownership first."

To remove an owner, transfer ownership to another member first, then remove the old owner's membership.

Invitation Emails

Invitation emails are not implemented

Invitation records are created and stored in the invitations table when an owner invites a user. However, the sendInvitationEmail function in app/lib/auth.ts has an empty body (TODO comment only). Invited users receive no email notification. This is a known TODO.

See Also

For actual migration files, see apps/server/database/migrations/

Built with ❤️ by the Jubiloop team