Skip to main content

Projects

A Project groups videos, personas, world states, summaries, claims, and annotations under a shared owner with a member roster and optional video assignment rules. Project-scoped data is visible to project members; the project's role determines what each member can do.

Lifecycle

POST   /api/projects                       create
GET /api/projects list projects the requester can read
GET /api/projects/:projectId read one
PUT /api/projects/:projectId update name, description, settings
DELETE /api/projects/:projectId delete

POST /api/projects accepts:

{
"name": "Field study A",
"description": "Optional",
"slug": "field-study-a",
"ownerUserId": "...",
"ownerGroupId": null,
"settings": {}
}

Exactly one of ownerUserId and ownerGroupId is set. The authenticated user is added as project_owner on creation when they own the project; group-owned projects inherit access through the group's role assignments.

Members

POST   /api/projects/:projectId/members              body { userId, role }
GET /api/projects/:projectId/members
PUT /api/projects/:projectId/members/:userId body { role }
DELETE /api/projects/:projectId/members/:userId

role is one of:

project_owner      full project admin + content
project_manager content + limited project admin (no delete project)
annotator create / read / update / delete own content; read all
reviewer read all; review action on annotation/summary/claim
viewer read-only

The role determines which RolePermission rows match for project scope. See Reference > RBAC permissions for the full action × subject matrix per role.

Adding, updating, or removing a member calls invalidateUserAbilities(userId) on the affected user so their ability is rebuilt on the next request.

Project-scoped resources

GET /api/projects/:projectId/personas
GET /api/projects/:projectId/world
PUT /api/projects/:projectId/world

Personas, world states, summaries, claims, and annotations all carry an optional projectId column. When set, the row is project-scoped and visible to project members per their role; when NULL, the row is personal to its owner.

WorldState has a (userId, projectId) unique constraint, so each user has at most one personal world state and one world state per project they belong to.

Ownership

Project.ownerUserId is the ownership column for ability checks. Owners can do anything inside the project regardless of the RolePermission matrix; transfer ownership via PUT /api/projects/:projectId with a new ownerUserId (or ownerGroupId).

Deleting a project cascades to ProjectMembership and clears projectId on dependent content rows (the FK uses SetNull so data survives).

See also