Invitations
Organization owners and admins can invite new members by email. Invitations work for both existing users and people who haven't signed up yet.
API Routes
GET /api/organizations/{org}/invitations # list pending invitations
POST /api/organizations/{org}/invitations # send an invitation
DELETE /api/organizations/{org}/invitations/{id} # cancel an invitation
GET /api/invitations/{token} # public preview (no auth)
POST /api/invitations/{token}/accept # accept an invitation
Sending an Invitation
POST /api/organizations/{org}/invitations
{
"email": "jane@example.com",
"role": "member"
}
The system generates a unique 64-character token and sends an invitation email with a link.
Invitation Model
Key fields on the Invitation model:
organization_id— the org being joinedemail— invitee's email addressrole— the org role assigned on accept (e.g.member,admin)token— 64-character unique tokeninvited_by— user ID of the person who sent the inviteaccepted_at— timestamp when accepted (null while pending)expires_at— expiration timestamp
Public Preview
GET /api/invitations/{token}
This endpoint requires no authentication. It returns a preview of the invitation including the organization name, so the invitee knows which team they're joining before logging in or registering.
Accepting an Invitation
POST /api/invitations/{token}/accept
- Existing users: Added to the org immediately and receive an in-app notification
- New users: Guided through the registration flow first, then auto-added to the org
Per-Seat Billing
When an invitation is accepted, the SeatSyncService is called automatically. If the org's plan uses per-seat billing, the subscription quantity is updated in Stripe to reflect the new member count.