Team Invitations
In B2B mode, workspace owners and admins can invite new members via email.
Invitation Flow
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Admin sends │ │ Invitee gets │ │ Invitee clicks │
│ invitation │────▶│ email with │────▶│ accept link │
│ (email + role) │ │ magic link │ │ │
└──────────────────┘ └──────────────────┘ └──────────────────┘
│
▼
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Member added │ │ Membership │ │ Token validated │
│ to workspace │◀────│ created with │◀────│ (7-day expiry) │
│ │ │ assigned role │ │ │
└──────────────────┘ └──────────────────┘ └──────────────────┘Sending Invitations
Admins and owners can invite new members from the org dashboard's Members page. The invitation form collects the invitee's email and assigns a role. The system generates a unique token, stores the invitation in the invitations table with a 7-day expiry, and sends the invite email through the active email provider with a locale-appropriate template.
Invitation API
Invitations are created by a server action from the workspace members page (not a public REST endpoint). The HTTP API surface is POST /api/invitations/accept (accept an invite), POST /api/invitations/resend (owner/admin re-send), and POST /api/invitations/revoke (owner/admin revoke). Each path validates the user's permission, checks for duplicate or pending invitations where relevant, and enforces the workspace's member limit based on the current plan.
Accepting Invitations
When a user clicks the invitation link, they are taken to /invite/accept which validates the token and expiry. If the user is already authenticated, the membership is created immediately. If not, they are prompted to log in or create an account first, then redirected back to complete the acceptance.
Invitation Database Schema
The invitations table stores the account ID, invited email, role slug, invitation token (unique), expiry date, status (pending / accepted / expired / revoked), and who sent the invitation. RLS scopes reads to the invitation's account; writes go through server actions / SECURITY DEFINER paths, not user-level INSERT policies.
Managing Pending Invitations
The Members page in the org dashboard shows all pending invitations alongside current members. Admins can resend an expired invitation or cancel a pending one. The pending-invitations.tsx component displays the invitee's email, assigned role, expiry status, and action buttons.
Invitation Email Templates
Invitation emails are rendered by a single shared builder — buildInvitationEmail() in lib/email/templates.ts — wrapped in the universal email chrome from lib/email/layout.ts. Locale strings come from the email.invitation.* i18n keys (FR/EN), so there is no separate workspace-invitation-fr / -en file pair.
| Builder | Source | Parameters |
|---|---|---|
buildInvitationEmail |
lib/email/templates.ts |
workspaceName, role, inviteLink, inviterName, appName, locale |
- Invitation tokens are single-use and expire after 7 days
- Only owners and admins can send invitations
- Users cannot invite themselves
- Duplicate invitations to the same email are prevented
B2B Subscription Flow
Invitations sit on top of an already-existing workspace, so the workspace bootstrap, pending-checkout cookie, Stripe round-trip, and onboarding gate live in the parent page. See Multi-Tenancy Overview for the full B2B subscription flow (five auto-bootstrap call sites, single onboarding gate, workspace-manager redirect rule).
Invited User Flow
When an invited user accepts an invitation:
- Click invitation link → Accept invitation page
- API-based acceptance via
/api/invitations/accept - Pending invitation revocation via
/api/invitations/revokewith authenticated CSRF-protected server validation - Redirect to success page → Onboarding (if needed)
- Subscription check: If workspace has active subscription, redirect via the workspace-manager rule (
isWorkspaceManager(user.id)) —/org-dashboardfor owners/admins, else/private-dashboard(not pricing) - Dashboard prioritizes workspace account for data display
Organization Member Limits
Organizations can have configurable member limits:
| Feature | Description |
|---|---|
max_members |
Configurable per organization (null = unlimited) |
| Enforcement | Limit checked on member invitation |
| Admin Update | Can be updated via admin dashboard |
| Visual Indicator | Warning shown when limit is reached |