Skip to main content

Team and invitations API

Workspace membership is how sharing actually works in OpenDocs — if a post is workspace visible, every active member of that workspace can read it. These endpoints cover everything about team members and invitations.

Session auth only

Every endpoint on this page requires a browser session cookie. API keys return 401 unauthorized — teams are a dashboard surface. See Authentication.

All routes fall under the general 120 req/min rate bucket.

Roles

Every member has one of three roles:

RoleWhat it grants
ownerEvery permission, plus the right to transfer ownership. One per workspace.
adminFull admin: rename workspace, change slug, manage branding, invite / remove / re-role members. Can't remove the owner.
memberRead/write posts. Cannot change workspace settings or manage members.

The user who creates the workspace becomes its owner. Admins and members are added through the invitation flow below.

Member caps

Member counts are capped per plan:

PlanMembers per workspace
Free3
Pro5
Teamunlimited

Hitting the cap returns 403 member_limit from POST /workspace/team/invite. See Plans and limits for the full breakdown.

GET /api/v1/workspace/team

List the caller's workspace members.

Response

{
"members": [
{
"id": "mem_1",
"userId": "usr_1",
"email": "ada@acme.com",
"fullName": "Ada Lovelace",
"role": "owner",
"joinedAt": "2026-01-10T12:00:00Z"
},
{
"id": "mem_2",
"userId": "usr_2",
"email": "grace@acme.com",
"fullName": "Grace Hopper",
"role": "admin",
"joinedAt": "2026-02-14T09:30:00Z"
}
]
}

Returns 400 no_workspace if the caller hasn't completed onboarding.

GET /api/v1/workspace/team/stats

Compact numeric summary used by the dashboard badge.

{
"total": 3,
"pendingInvitations": 1,
"limit": 5,
"remaining": 2
}

limit and remaining are null on the Team plan (unlimited).

GET /api/v1/workspace/team/invitations

List pending invitations. Owner/admin only — members get 403 forbidden.

{
"invitations": [
{
"id": "inv_1",
"email": "mike@acme.com",
"role": "member",
"status": "pending",
"sentAt": "2026-04-01T10:15:00Z",
"expiresAt": "2026-04-08T10:15:00Z"
}
]
}

POST /api/v1/workspace/team/invite

Send an invitation. Owner/admin only.

Request body

{
"email": "mike@acme.com",
"role": "member",
"workspaceId": "ws_123"
}
FieldRequiredNotes
emailyesValid email, ≤ 320 chars.
roleyes"admin" or "member". You can't invite another owner.
workspaceIdnoOnly needed if the caller belongs to multiple workspaces. Defaults to the current workspace.

Success response

Status 201.

{
"invitation": {
"id": "inv_1",
"email": "mike@acme.com",
"role": "member",
"status": "pending",
"expiresAt": "2026-04-24T10:15:00Z"
}
}

Errors

StatuserrorWhen
403forbiddenCaller isn't owner/admin
403member_limitWorkspace is at its per-plan member cap
409already_memberEmail already belongs to an active member
409already_invitedA pending invitation for this email already exists
400invite_limitPending-invitation backlog is too large

POST /api/v1/workspace/team/invitations/:id/resend

Re-send the invitation email and refresh its expiry.

{ "resent": true, "expiresAt": "2026-04-24T10:15:00Z" }

Errors: 403 forbidden, 404 not_found.

DELETE /api/v1/workspace/team/invitations/:id

Cancel a pending invitation.

{ "cancelled": true }

Errors: 403 forbidden, 404 not_found.

POST /api/v1/workspace/team/accept-invite

Accept an invitation token. Called from the ?token=… landing page — not typically used from automation.

Request body

{ "token": "inv_signed_token_abc123" }

Success response

{
"workspaceId": "ws_123",
"workspaceSlug": "acme",
"role": "member"
}

Errors

StatuserrorWhen
404not_foundToken doesn't match any invitation
410expiredInvitation has expired — ask the inviter to re-send
409already_memberCaller is already a member of this workspace

PATCH /api/v1/workspace/team/members/:id/role

Change a member's role. Owner/admin only.

Request body

{ "role": "admin" }

Role must be "admin" or "member". The owner's role can't be changed through this endpoint — use the ownership-transfer flow in the dashboard.

Success response

{ "id": "mem_2", "role": "admin" }

Errors: 403 forbidden, 404 not_found.

DELETE /api/v1/workspace/team/members/:id

Remove a member. Owner/admin only. The owner cannot be removed.

{ "removed": true }

Errors: 403 forbidden, 404 not_found.

Invitation lifecycle

  1. sendPOST /workspace/team/invite — status pending, email sent, token generated, 7-day expiry.
  2. resendPOST /workspace/team/invitations/:id/resend — re-send email, reset expiry.
  3. cancelDELETE /workspace/team/invitations/:id — invitation is deleted; the token stops working.
  4. acceptPOST /workspace/team/accept-invite — invitee becomes an active member, invitation row is marked consumed.
  5. expire — if no accept within 7 days, the token starts returning 410 expired. Resending refreshes the expiry without issuing a new token URL.