Skip to content

Teams

TSKit is multi-tenant. Every user belongs to at least one team, and most data (subscriptions, usage, billing) is scoped to the active team.

Teams are built on Better Auth’s organization plugin. The schema includes three tables: organizations, members, and invitations. These are part of the auth schema and managed by Better Auth.

When a user signs up, a personal team is created automatically through a database hook in lib/facades/auth.ts. The team is named “{name}‘s Team” with a slug like personal-abc12345. This happens behind the scenes so the user is ready to go from the start.

Each team member has one of three roles:

RoleCan manage teamCan manage billingCan invite members
OwnerYesYesYes
AdminYesYesYes
MemberNoNoNo

Only owners can delete a team or transfer ownership.

Users can switch between teams using the team switcher in the sidebar. When they switch, the active organization is updated on their session and persisted to user settings so it carries over to their next login.

The setActiveTeam server function handles this. It verifies that the user is a member of the target organization before switching.

Team owners and admins can invite new members by email. The invitation flow:

  1. An invitation is created with a role and sent via email using the team-invitation template.
  2. The invitation expires after 48 hours.
  3. The invited user clicks the link and lands on /invite/{invitationId}.
  4. They see the team name and who invited them, then accept or reject.
  5. On acceptance, they become a member of the team.

Billing, subscriptions, and usage are all scoped to the organization. This means:

  • Each team has its own Stripe customer
  • Each team has its own subscription and plan
  • Usage limits are tracked per team, not per user
  • Only team owners and admins can manage billing

This is handled by orgMiddleware, which fetches the active organization and makes it available as context.organization in server functions.

A user cannot delete their only team or leave their only team. The server functions check membership count before allowing these actions. If a user deletes their account, any team where they are the sole member is also deleted.

FilePurpose
lib/team.tsPersonal team auto-creation on signup
functions/team.tsAll team server functions (CRUD, members, invitations)
middleware/org.tsOrg middleware (fetches active organization)
database/schemas/auth.tsOrganization, member, and invitation tables
components/settings/team-invite-form.tsxInvitation dialog
components/settings/team-members-list.tsxMembers list with role management
routes/invite.$invitationId.tsxInvitation acceptance page