Skip to content

Collaboration API

All collaboration endpoints are scoped to a project. See Collaboration concepts for role definitions and the permissions matrix.

POST /api/projects/:id/invitations

Auth: Owner only.

{
"email": "user@example.com",
"role": "editor"
}
FieldTypeRequiredDescription
emailstringNoOptional hint — not enforced at accept time
role"editor" | "contributor"NoDefaults to "contributor"

Response 200:

{
"id": "inv-uuid",
"token": "uuid.hmac-signature",
"expires_at": "2026-03-10T12:00:00Z",
"invite_url": "/join/uuid.hmac-signature"
}

Rate limit: 10 invitations per project per hour.


GET /api/projects/:id/invitations

Auth: Owner only.

Response 200: Array of pending invitations.

[
{
"id": "inv-uuid",
"email": "user@example.com",
"role": "contributor",
"expires_at": "2026-03-10T12:00:00Z",
"created_at": "2026-03-03T12:00:00Z",
"invite_url": "/join/uuid.hmac-signature"
}
]

DELETE /api/projects/:id/invitations/:inv_id

Auth: Owner only.

Response: 204 No Content on success. 404 if the invitation is not found or already used.


GET /api/invitations/:token

Auth: None required. This is a public endpoint for link previews.

Response 200:

{
"project_id": "proj-uuid",
"project_name": "My Project",
"invited_by": "user-id",
"expires_at": "2026-03-10T12:00:00Z"
}

Returns 400 for invalid tokens, 404 if not found, 410 if expired or already used.


POST /api/invitations/:token/accept

Auth: Authenticated human (Clerk session). Agents cannot accept invitations.

Response 200:

{
"id": "member-uuid",
"project_id": "proj-uuid",
"user_id": "user-id",
"role": "editor",
"status": "active",
"invited_by": "inviter-user-id",
"joined_at": "2026-03-03T14:00:00Z"
}

The invitation is marked as used after acceptance and cannot be reused.


POST /api/projects/:id/join

Auth: Authenticated human. The project must have join_mode = "open" and cta_enabled = true.

Response 200:

{
"id": "member-uuid",
"project_id": "proj-uuid",
"user_id": "user-id",
"role": "contributor",
"status": "active",
"invited_by": null,
"joined_at": "2026-03-03T14:00:00Z"
}

Open-joined members always receive the contributor role. Rate limit: 5 joins per IP per hour.

Status codeMeaning
403Project does not accept open joins
409Already a member
429Rate limit exceeded

GET /api/projects/:id/members

Auth: Owner only.

Response 200: Array of member objects with user_id, role, status, joined_at.


DELETE /api/projects/:id/members/:user_id

Auth: Owner only. The project owner cannot be removed.

Response: 204 No Content on success. 404 if member not found. 403 if attempting to remove the owner.


PATCH /api/projects/:id/members/:user_id

Auth: Owner only. The owner’s own role cannot be changed.

{
"role": "editor"
}
FieldTypeRequiredDescription
role"editor" | "contributor"YesNew role for the member

Response 200:

{
"user_id": "user-id",
"role": "editor"
}

GET /api/projects/:id/public

Auth: None required. Only returns data if the project has is_public = true.

Response 200:

{
"id": "proj-uuid",
"name": "My Project",
"description": "Optional description",
"join_mode": "open",
"cta_enabled": true
}

Returns 404 if the project does not exist or is not public.

Related public endpoints:

  • GET /api/projects/:id/public/tasks — sanitized task list (title, status, priority)
  • GET /api/projects/:id/public/knowledge — public knowledge entries