Skip to content

Invitations

Endpoints

POST /api/v1/workspace/members/invite             send (auth, admin, gated, quota-enforced)
GET  /api/v1/workspace/invitations                list pending (auth, admin)
POST /api/v1/workspace/invitations/:id/revoke     revoke (auth, admin)

GET  /api/v1/invitations/:token                   public lookup (no auth — token is secret)
POST /api/v1/invitations/:token/accept            accept (auth)

Invitation fields

typescript
{
  workspace_id: ObjectId,
  email: string,                                  // lowercase, indexed
  role: "owner" | "admin" | "editor" | "author" | "viewer",  // default "author"
  token: string,                                  // base64url-encoded 32-byte secret
  status: "pending" | "accepted" | "revoked" | "expired",
  invited_by: ObjectId,
  expires_at: Date,
  accepted_at: Date | null,
  accepted_by: ObjectId | null,
  revoked_at: Date | null,
}

Flow

  1. Admin sends POST /workspace/members/invite { email, role }. Sosyabot mints a 32-byte token and emails an acceptance link.
  2. Recipient clicks the link, lands on /invitation-accept (frontend), which calls GET /invitations/:token to display invitation details.
  3. If the recipient is logged in (or signs up first), the page calls POST /invitations/:token/accept. The endpoint moves status to accepted, joins the user to the workspace, and sets accepted_by.

The token is the only auth needed for the public lookup; treat it as a secret. Revoking an invite (/revoke) flips status to revoked and the token stops working.