SaaSyBase
SaaSyBase

Teams

SaaSyBase supports team subscriptions that provision managed organizations, sync with billing status, and give members shared access to features and token pools. This guide covers the full team lifecycle, including personal-versus-team workspace switching.

How Team Plans Work

A team plan is a subscription plan with scope: "TEAM" and supportsOrganizations: true. When a user subscribes to a team plan, the system automatically:

  1. Creates or updates an organization for the subscriber.
  2. Assigns a deterministic slug based on the user's name or email.
  3. Mirrors organization metadata to the active auth provider when that provider supports organization primitives, notably Clerk and Better Auth.
  4. Sets up the token strategy defined in the plan.

This provisioning only happens when the plan has supportsOrganizations: true and the subscription is active (not in a proration-pending state).

When the buyer already has a local workspace, provisioning updates that organization in place instead of deleting and recreating it. That preserves the workspace row, member assignments, and related metadata while bringing the plan-backed fields back into sync.

Tip

Choose team plans when multiple users need a shared billing/workspace context. Stay with individual plans when each customer should manage only their own subscription and balances.

Multiple Workspaces and Switching

Users can operate in more than one workspace context. There is always a personal workspace, and there can also be one or more team workspaces owned by or shared with the user.

Workspace typeWhat it meansHow users switch
Personal workspaceThe user is acting on their own subscription, balances, and featuresSelect the personal context in the sidebar switcher or clear the active org cookie
Team workspaceThe user is acting inside an organization with its own plan context and token strategyUse the sidebar workspace switcher or app-managed active-org API

The dashboard sidebar footer renders the workspace switcher through AuthOrganizationSwitcher. For app-managed switching, the client calls POST /api/user/active-org and stores the active organization in an httpOnly cookie.

Note

Active workspace selection affects plan scope, team dashboard data, checkout metadata, organization-aware billing flows, and token spending behavior.

If you are using Clerk-backed organizations, the relevant upstream docs are Clerk Organizations.

Organization Provisioning

The ensureTeamOrganization helper handles creating and updating organizations when subscriptions activate. It runs automatically during:

  • Checkout completion
  • Subscription activation
  • Webhook events
  • Admin overrides

When a subscription lapses, the companion helper syncOrganizationEligibilityForUserapplies the configured organization-expiry policy and clears member access. The shipped default is to suspend workspace access while preserving the local organization row so reactivation can provision it again cleanly. Admins can switch this to full dismantling through ORGANIZATION_EXPIRY_MODE.

Admins can also manually suspend a workspace from /admin/organizations. That keeps the local workspace row, expires pending invites, and surfaces a notice whenever a user is inside the suspended workspace. The same admin screen can restore that workspace later without creating a brand-new local record.

If provisioning ever drifts from billing state, the dashboard and admin tooling both expose a provisioning refresh path backed by /api/team/provision. Use that before making direct database edits.

Organization expiry modes

ORGANIZATION_EXPIRY_MODE controls what happens when a workspace owner falls outside the configured token-expiry grace window.

ModeWhat happens
SUSPENDKeep the local workspace, expire invites, remove provider-side org linkage when applicable, and show a dashboard notice until access is restored.
DISMANTLERemove the workspace access path entirely instead of leaving a suspended shell behind.

The grace window before either mode is applied is controlled by TOKENS_NATURAL_EXPIRY_GRACE_HOURS.

Token Strategies

Team plans support two distinct approaches to distributing tokens among members. See the Tokens & Features page for detailed field descriptions.

StrategyHow tokens work
SHARED_FOR_ORGOne shared pool for the whole team. Optional per-member caps prevent overuse.
ALLOCATED_PER_MEMBEREach member gets their own balance. Renewals reset individual balances.

The effective strategy is set on the plan (organizationTokenPoolStrategy). The dashboard UI adapts automatically — shared-pool cap controls only appear when the workspace uses SHARED_FOR_ORG.

Tip

Choose SHARED_FOR_ORG when the team should spend from one common pool. Choose ALLOCATED_PER_MEMBER when each member needs their own predictable allowance.

Member Management

Inviting members

Team owners invite members through the /dashboard/team page. Invites generate a unique token and send an email notification. Invited users accept via /invite/[token].

The invite flow works for both existing users and new sign-ups — new users can accept the invite after creating their account.

Member entitlements

When a workspace uses ALLOCATED_PER_MEMBER, joining members receive the plan's token allowance in their membership balance. Renewals reset those balances, and top-ups credit each active member instead of the org pool.

The initial allowance is granted when the invite is accepted and the membership becomes active. Renewal resets happen on the organization subscription lifecycle, not on an individual member anniversary.

Seat limits

Plans can define seat constraints to control team size:

FieldPurpose
organizationSeatLimitMaximum number of members
minSeats / maxSeatsSeat range for seat-based pricing
seatPriceCentsPer-seat price for seat-based billing

Treat the seat-pricing fields as advanced plan metadata. The schema and provider abstractions understand them, but you should verify your checkout and admin flows before treating seat-count selection as a finished end-user feature.

Plan Schema for Teams

Prisma schema (relevant fields)
Plan {
  scope                          String  @default("INDIVIDUAL") // "INDIVIDUAL" or "TEAM"
  supportsOrganizations          Boolean @default(false)
  organizationSeatLimit          Int?
  organizationTokenPoolStrategy  String? @default("SHARED_FOR_ORG")
  minSeats                       Int?
  maxSeats                       Int?
  seatPriceCents                 Int?
}

Dashboard & API

The team management UI is at /dashboard/team and includes:

  • Invite management (send, resend, revoke)
  • Member list with removal
  • Per-member cap override controls for shared token workspaces
  • Provisioning refresh
  • Owner-only organization deletion with active-plan checks in the confirmation modal
  • Strategy-aware balance labels
  • Shared-pool cap controls (only visible for SHARED_FOR_ORG workspaces)

Deleting a workspace safely

Workspace deletion is intentionally owner-only and is triggered from the /dashboard/team header actions beside the refresh control. The dashboard first checks deletion eligibility, then opens a confirmation modal that explains whether an active team plan still blocks deletion.

The confirm action stays disabled while the workspace still has an active team subscription attached. That prevents accidental removal of an organization that billing still considers live.

When deletion succeeds, the client clears the active workspace by posting { orgId: null } to /api/user/active-org so the user returns to their personal workspace cleanly.

Note

If the workspace was created through Clerk, Better Auth, or another provider-backed organization lane, deletion still routes through the auth-provider abstraction first. Local-only workspaces fall back to direct database cleanup when no provider organization id exists.

Team API routes

RoutePurpose
/api/user/active-orgSwitch between the personal workspace and a selected organization workspace
/api/organization/check-deletion-eligibilityCheck whether the current or requested workspace still has an active team plan that blocks deletion
/api/organization/deleteDelete a workspace owned by the current user once no active team plan remains
/api/team/inviteCreate team invitations
/api/team/invite/revokeRevoke a pending invite
/api/team/invite/acceptAccept a team invite
/api/team/invite/declineDecline a team invite
/api/team/invite/resendResend an invite email
/api/team/members/removeRemove a team member
/api/team/members/cap-overrideSet or clear a per-member cap override
/api/team/summaryGet team summary data
/api/team/provisionTrigger organization provisioning
/api/team/settingsUpdate team settings

Support and recovery flows

If a workspace owner needs help with invites, provisioning drift, billing confusion, or member access issues, the built-in support center is already wired into the dashboard.

Users can open tickets at /dashboard/support, and admins handle them from /admin/support. Invite acceptance also tries to switch the user directly into the organization workspace after success so the team dashboard opens in the correct context.

When no custom organization billing email exists, admin tooling falls back to the owner's email so the workspace still has a default billing contact.

If a workspace is suspended manually or by expiry policy, users see a workspace notice in the dashboard so they understand why team actions are blocked.

Clerk Webhook Sync

When using Clerk as the auth provider, organization and membership events are automatically synced:

  • organization.* — upserts organizations from Clerk into the local database.
  • organizationMembership.* — syncs member roles and status.
  • organizationInvitation.* — tracks invite state (pending, accepted, revoked).

Note

When using Better Auth or NextAuth, organizations are managed in the app/self-hosted lane — no Clerk webhook sync is needed.

If a Clerk webhook delivery fails, Clerk retries according to its own webhook policy. For stubborn drift or missed backfills, rerun provisioning from the team dashboard or the organizations admin view rather than assuming the provider retry already repaired everything.

For the auth-provider tradeoffs behind that choice, see Authentication.