API
Every REST endpoint grouped by resource. All paths are mounted
under /api. Authentication is cookie-based; see
Guide > Authentication. Routes
marked requireAuth reject unauthenticated requests with 401.
Routes marked optionalAuth accept anonymous requests but return
a reduced view.
Auth
POST /api/auth/login body { username, password }
POST /api/auth/logout
POST /api/auth/register body { username, password, email, displayName }
GET /api/auth/me
GET /api/auth/session-status
POST /api/auth/extend-session
POST /api/auth/register returns 403 when ALLOW_REGISTRATION=false.
Users (admin)
GET /api/user/profile
PUT /api/user/profile
GET /api/admin/users admin only
POST /api/admin/users admin only
Sessions
GET /api/sessions requester's sessions
GET /api/admin/sessions admin only, all sessions
API keys
GET /api/api-keys requester's keys
POST /api/api-keys body { provider, keyName, key }
GET /api/admin/api-keys admin only, shared pool
POST /api/admin/api-keys admin only, add to shared pool
Personas
GET /api/personas
POST /api/personas body { name, role, informationNeed, details? }
GET /api/personas/:id
PUT /api/personas/:id
DELETE /api/personas/:id
GET /api/personas/:id/deletion-preview
GET /api/personas/:id/ontology
PUT /api/personas/:id/ontology
GET /api/personas/:personaId/entity-types/:typeId
DELETE /api/personas/:personaId/entity-types/:typeId
GET /api/personas/:personaId/event-types/:typeId
DELETE /api/personas/:personaId/event-types/:typeId
GET /api/personas/:personaId/role-types/:typeId
DELETE /api/personas/:personaId/role-types/:typeId
GET /api/personas/:personaId/relation-types/:typeId
DELETE /api/personas/:personaId/relation-types/:typeId
POST and PUT silently coerce isSystemGenerated to false
for non-admin requesters.
Ontology
GET /api/ontology query personaId
PUT /api/ontology body { personaId, entityTypes, eventTypes,
roleTypes, relationTypes }
POST /api/ontology/augment requireAuth; body { personaId, prompt }
World
GET /api/world
PUT /api/world body { entities, events, times,
entityCollections, eventCollections,
timeCollections, relations }
DELETE /api/admin/world/:userId admin only
Videos
GET /api/videos
GET /api/videos/:videoId
GET /api/videos/:videoId/url
GET /api/videos/:videoId/stream
GET /api/videos/:videoId/thumbnail
POST /api/videos/sync
POST /api/videos/:videoId/detect body { personaId, frame }
Annotations
GET /api/annotations/:videoId
POST /api/annotations body { videoId, type, label, frames,
personaId?, linkType? }
PUT /api/annotations/:id
DELETE /api/annotations/:videoId/:id
Summaries
GET /api/videos/:videoId/summaries
GET /api/videos/:videoId/summaries/:personaId
POST /api/summaries hand-write a summary row
PUT /api/videos/:videoId/summaries/:summaryId
DELETE /api/videos/:videoId/summaries/:personaId
POST /api/videos/summaries/generate enqueue VLM job
GET /api/jobs/:jobId poll the VLM job
Claims
GET /api/summaries/:summaryId/claims
GET /api/summaries/:summaryId/claims/:claimId
POST /api/summaries/:summaryId/claims
PUT /api/summaries/:summaryId/claims/:claimId
DELETE /api/summaries/:summaryId/claims/:claimId
POST /api/summaries/:summaryId/claims/generate
GET /api/jobs/claims/:jobId
POST /api/summaries/:summaryId/synthesize
GET /api/jobs/synthesis/:jobId
GET /api/videos/:videoId/personas/:personaId/claims
Claim relations
POST /api/summaries/:summaryId/claims/:claimId/relations
GET /api/summaries/:summaryId/claims/:claimId/relations
DELETE /api/summaries/:summaryId/claims/relations/:relationId
Projects
GET /api/projects
POST /api/projects body { name, description?, slug,
ownerUserId?, ownerGroupId?,
settings? }
GET /api/projects/:projectId
PUT /api/projects/:projectId
DELETE /api/projects/:projectId
GET /api/projects/:projectId/members
POST /api/projects/:projectId/members body { userId, role }
PUT /api/projects/:projectId/members/:userId body { role }
DELETE /api/projects/:projectId/members/:userId
GET /api/projects/:projectId/personas
GET /api/projects/:projectId/world
PUT /api/projects/:projectId/world
Roles: project_owner | project_manager | annotator | reviewer | viewer.
Groups
GET /api/groups
POST /api/groups body { name, description?, slug }
GET /api/groups/:groupId
PUT /api/groups/:groupId
DELETE /api/groups/:groupId
GET /api/groups/:groupId/members
POST /api/groups/:groupId/members body { userId, role }
PUT /api/groups/:groupId/members/:userId body { role }
DELETE /api/groups/:groupId/members/:userId
GET /api/admin/groups admin only
POST /api/admin/groups
PUT /api/admin/groups/:groupId
DELETE /api/admin/groups/:groupId
GET /api/admin/groups/:groupId/members
Group roles: group_owner | group_admin | group_member.
Video assignments
GET /api/projects/:projectId/videos list assignments
POST /api/projects/:projectId/videos body { videoId, assignedUserId? }
DELETE /api/projects/:projectId/videos/:videoId
POST /api/admin/video-assignments/bulk admin only
GET /api/admin/video-assignments/rules admin only
POST /api/admin/video-assignments/rules admin only
PUT /api/admin/video-assignments/rules/:ruleId admin only
DELETE /api/admin/video-assignments/rules/:ruleId admin only
POST /api/admin/video-assignments/rules/:ruleId/evaluate admin only
POST /api/admin/video-assignments/rules/evaluate-all admin only
Sharing
POST /api/sharing body { resourceType, resourceId,
sharedWithUserId? | sharedWithGroupId?,
permissionLevel, expiresAt? }
GET /api/sharing/received
GET /api/sharing/sent
DELETE /api/sharing/:shareId
POST /api/sharing/:shareId/fork
resourceType is one of annotation | summary | claim | persona | world_state.
permissionLevel is read_only or forkable.
Admin permissions
GET /api/admin/permissions
POST /api/admin/permissions body { scope, role, resourceType, action, ownOnly? }
PATCH /api/admin/permissions/:id body { ownOnly }
DELETE /api/admin/permissions/:id
All require systemRole = 'system_admin'. Mutations invalidate the
per-user ability cache.
Import / Export
GET /api/export full export
GET /api/export/personas
GET /api/export/world
GET /api/export/summaries
GET /api/export/stats
POST /api/import multipart upload; returns 4xx (typically
413) on FST_*_LIMIT
POST /api/import/preview dry-run, returns conflict report
GET /api/import/history requester's prior imports
Models
Backend proxy endpoints (mounted under /api):
GET /api/models/config
GET /api/models/status
POST /api/models/validate
GET /api/models/defaults per-task default knobs
GET /api/models/frameworks framework metadata for UI
Model service (FastAPI, :8000)
The model service is not authenticated at the HTTP layer; the
backend is the only client and gates every call. Routes are
defined under
model-service/src/infrastructure/adapters/inbound/fastapi/routes/.
GET /models/config
GET /models/status
POST /models/select
POST /models/validate
POST /models/load/{task_type}
POST /models/unload/{task_type}
GET /models/task-ready/{task_type}
POST /detection/detect body: DetectionRequest schema
POST /tracking/track body: TrackingRequest schema
POST /summarize body: SummarizationRequest;
response carries optional ReasonedText
POST /ontology/augment body: OntologyAugmentRequest
POST /extract-claims body: ClaimExtractionRequest
POST /synthesize-summary body: SummarySynthesisRequest
POST /thumbnails/generate body: ThumbnailRequest;
writes to THUMBNAIL_OUTPUT_ROOT
POST /admin/reconfigure gated by MODEL_SERVICE_ADMIN_TOKEN
Config and telemetry
GET /api/config
POST /api/telemetry/traces
Status codes
200 ok
201 created
204 no content (deletes)
400 validation failed
401 unauthenticated (requireAuth routes)
403 forbidden (e.g. registration disabled, non-admin admin route)
404 not found OR foreign-owned (ownership helpers throw NotFoundError)
413 request too large (multipart upload)
429 too many requests (LoginAttempt-driven lockout)
500 unhandled error