TheShipStack Docs
Features

Teams & Workspaces

Multi-tenant organizations with member invitations, roles, and per-plan limits.

TheShipStack is multi-tenant from the start. Every user belongs to one or more workspaces (organizations). All data — projects, billing, and members — is scoped to a workspace.

Concepts

  • Workspace — an organization that groups users and resources together
  • Member — a user who belongs to a workspace with a specific role
  • Roleowner, admin, or member (see RBAC)
  • Active workspace — the workspace a user is currently acting in

Creating a workspace

Users create a workspace during onboarding. They can create additional workspaces from the dashboard. Each workspace is independent — separate billing, members, and data.

Inviting members

Owners and admins can invite members from /dashboard/team. An invitation email is sent via Resend. When accepted, the user joins the workspace with the assigned role.

import { inviteMember } from '@/actions/team'

await inviteMember('colleague@example.com', 'admin')

Invitations check the plan's member limit before sending — if the workspace is at its limit, UPGRADE_REQUIRED is thrown.

Managing members

import { updateMemberRole, removeMember } from '@/actions/team'

await updateMemberRole(memberId, 'admin')  // promote/demote
await removeMember(memberId)               // remove from workspace

Affected members receive a notification when their role changes or they are removed.

Per-plan member limits

Member limits are defined in lib/plans.ts alongside project limits. Free workspaces have a fixed cap; paid plans raise or remove the limit.

Switching workspaces

The active workspace is stored in the session. Users switch workspaces via the workspace switcher in the sidebar. All data reads use the active organization ID.

Accessing the current workspace in server code

import { requireOrg } from '@/lib/require-org'

const { orgId } = await requireOrg()
// orgId is the active workspace — throws if not authenticated or no org

Schema

Workspace data is managed by Better Auth's organization plugin. The relevant tables are:

  • organization — workspace name, logo, slug
  • member — user↔workspace relationship with role
  • invitation — pending invitations

These are created automatically by Better Auth when you run pnpm db:push.

On this page