REST API
The SessionFS API server exposes a REST API for session management, auditing, sync, and settings.
Base URL
Section titled “Base URL”- Cloud:
https://api.sessionfs.dev - Self-hosted: Your deployment URL
Authentication
Section titled “Authentication”All endpoints (except signup) require a Bearer token:
Authorization: Bearer sk_sfs_...Two key kinds:
- User keys — created via
POST /api/v1/auth/keys. Tied to a single user, inherit all of that user's permissions. Legacy keys are back-filled toscopes=["*"]. - Service keys (v0.10.10+) — created via
POST /api/v1/orgs/{org_id}/service-keys. Org-scoped, expirable, and restricted to an enumeratedscopeslist (e.g.["handoffs:write", "agent_runs:write"]). Deny-by-default: a service key only reaches a route handler that explicitly opted in viarequire_scope(...). The recommended credential for cloud agents (Bedrock, Vertex), CI runners (GitHub Actions, GitLab MR), and integration partners.
Structured 401/403 error codes: api_key_revoked, api_key_expired, service_key_not_allowed, insufficient_scope (with required + current arrays), cross_org_denied, service_key_project_required, service_key_project_not_registered, service_key_project_ambiguous.
Endpoints
Section titled “Endpoints”| Method | Path | Auth | Description |
|--------|------|------|-------------|
| POST | /api/v1/auth/signup | None | Create account |
| GET | /api/v1/auth/me | Bearer | Current user profile |
| POST | /api/v1/auth/keys | Bearer | Create user API key |
| GET | /api/v1/auth/keys | Bearer | List user API keys |
| DELETE | /api/v1/auth/keys/{key_id} | Bearer | Delete a user API key |
| GET | /api/v1/auth/verify/{token} | None | Verify email address |
Service Keys (v0.10.10+)
Section titled “Service Keys (v0.10.10+)”Org-scoped, scope-restricted API keys for cloud agents, CI runners, and integration partners. Org admin role + Team+ tier required for mutations. Raw key returned exactly once on create + rotate; the list/detail endpoints only return key_prefix.
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| POST | /api/v1/orgs/{org_id}/service-keys | Bearer (org admin) | Create a service key (body: name, scopes[], optional expires_at, project_ids[]) |
| GET | /api/v1/orgs/{org_id}/service-keys | Bearer (org member) | List service keys for the org |
| DELETE | /api/v1/orgs/{org_id}/service-keys/{id} | Bearer (org admin) | Revoke a service key (body: optional revoke_reason) |
| POST | /api/v1/orgs/{org_id}/service-keys/{id}/rotate | Bearer (org admin) | Issue a new raw secret for an existing key |
Available scopes (the * wildcard is reserved for legacy user/admin keys; service keys must enumerate explicitly):
| Scope | Status | Routes opted in |
|-------|--------|-----------------|
| handoffs:read, handoffs:write | ✅ live (v0.10.10) | Handoff create/claim/revoke/decline + comments + events |
| agent_runs:write | ✅ live (v0.10.10) | POST /agent-runs, POST /agent-runs/{id}/complete |
| tickets:read, tickets:write | ✅ live (v0.10.18) | Ticket list/get/comments/review-state + start/complete/comment |
| knowledge:read, knowledge:write | ✅ live (v0.10.19) | GET /entries, GET /entries/{id}, POST /entries/add, PUT /entries/{id} (dismiss/update/refresh/promote/supersede) |
| personas:read, personas:write | ✅ live (v0.10.20) | Persona list/get + create/update/delete |
| agent_runs:read | ✅ live (v0.10.21) | GET /agent-runs, GET /agent-runs/{run_id} |
| sessions:read, rules:read, rules:write, retrieval_audit:read, admin:* | Reserved | Not yet opted in on any route |
Sessions
Section titled “Sessions”| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | /api/v1/sessions | Bearer | List sessions (paginated) |
| GET | /api/v1/sessions/{id} | Bearer | Get session detail |
| PUT | /api/v1/sessions/{id}/sync | Bearer | Push session (upload archive) |
| GET | /api/v1/sessions/{id}/sync | Bearer | Pull session (download archive) |
| GET | /api/v1/sessions/{id}/messages | Bearer | Get paginated messages |
| GET | /api/v1/sessions/{id}/summary | Bearer | Get session summary |
| POST | /api/v1/sessions/{id}/summary | Bearer | Generate session summary |
| POST | /api/v1/sessions | Bearer | Upload/create a session |
| DELETE | /api/v1/sessions/{id} | Bearer | Soft-delete a session |
| PUT | /api/v1/sessions/{id}/alias | Bearer | Set session alias |
| DELETE | /api/v1/sessions/{id}/alias | Bearer | Clear session alias |
| POST | /api/v1/sessions/{id}/share | Bearer | Create share link |
| DELETE | /api/v1/sessions/{id}/share/{link_id} | Bearer | Revoke share link |
| GET | /api/v1/sessions/share/{token} | None | Access shared session (public) |
| POST | /api/v1/sessions/share/{token} | None | Access password-protected share link |
| GET | /api/v1/sessions/search | Bearer | Full-text search |
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| POST | /api/v1/sessions/{id}/audit | Bearer | Run LLM Judge audit |
| GET | /api/v1/sessions/{id}/audit | Bearer | Get latest audit report |
| GET | /api/v1/sessions/{id}/audits | Bearer | Audit history |
| GET | /api/v1/sessions/{id}/audit/status | Bearer | Check background audit status |
Handoffs
Section titled “Handoffs”| Method | Path | Auth | Description |
|--------|------|------|-------------|
| POST | /api/v1/handoffs | Bearer | Create handoff |
| GET | /api/v1/handoffs/{id} | Bearer | Get handoff details |
| POST | /api/v1/handoffs/{id}/claim | Bearer | Claim handoff (copies session) |
| GET | /api/v1/handoffs/inbox | Bearer | Handoffs received |
| GET | /api/v1/handoffs/sent | Bearer | Handoffs sent |
| GET | /api/v1/handoffs/{id}/summary | None | Get handoff session summary |
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | /api/v1/sync/settings | Bearer | Get autosync settings (mode, debounce_seconds, max_member_bytes — tier-aware per-file cap; CLI v0.10.27+ reads this to avoid a hardcoded fallback) |
| PUT | /api/v1/sync/settings | Bearer | Update autosync mode (requires autosync feature if not "off"); response echoes the same shape as GET including max_member_bytes |
| GET | /api/v1/sync/watchlist | Bearer | Get watched sessions |
| POST | /api/v1/sync/watch/{id} | Bearer | Add session to watchlist |
| DELETE | /api/v1/sync/watch/{id} | Bearer | Remove session from watchlist |
| PUT | /api/v1/sync/watch/{id}/{status} | Bearer | Update watch status (pending/queued/synced/failed) |
| GET | /api/v1/sync/status | Bearer | Sync status overview (counts, storage usage) |
Settings
Section titled “Settings”| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | /api/v1/settings/judge | Bearer | Get judge settings |
| PUT | /api/v1/settings/judge | Bearer | Save judge settings |
| DELETE | /api/v1/settings/judge | Bearer | Clear judge settings |
| GET | /api/v1/settings/judge/models | Bearer | Discover models from custom endpoint |
| GET | /api/v1/settings/audit-trigger | Bearer | Get auto-audit trigger |
| PUT | /api/v1/settings/audit-trigger | Bearer | Set auto-audit trigger |
| GET | /api/v1/settings/github | Bearer | GitHub installation settings |
| PUT | /api/v1/settings/github | Bearer | Update GitHub settings |
| GET | /api/v1/settings/gitlab | Bearer | GitLab integration settings |
| PUT | /api/v1/settings/gitlab | Bearer | Update GitLab settings |
| DELETE | /api/v1/settings/gitlab | Bearer | Remove GitLab integration |
Projects
Section titled “Projects”| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | /api/v1/projects/ | Bearer | List projects the user has access to |
| POST | /api/v1/projects/ | Bearer | Create project context (requires project_context feature) |
| GET | /api/v1/projects/{remote} | Bearer | Get project context by git remote |
| PUT | /api/v1/projects/{remote}/context | Bearer | Update context document |
| DELETE | /api/v1/projects/{project_id} | Bearer | Delete project (owner or admin only) |
Knowledge Base
Section titled “Knowledge Base”| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | /api/v1/projects/{id}/entries | Bearer | List knowledge entries. Query params: type, pending, search, limit |
| POST | /api/v1/projects/{id}/entries/add | Bearer | Create a knowledge entry (types: decision, pattern, discovery, convention, bug, dependency) |
| PUT | /api/v1/projects/{id}/entries/{entry_id} | Bearer | Dismiss or un-dismiss a knowledge entry |
| POST | /api/v1/projects/{id}/compile | Bearer | Compile pending entries into project context (optional LLM config in body) |
| GET | /api/v1/projects/{id}/compilations | Bearer | List compilation history. Query param: limit |
| GET | /api/v1/projects/{id}/health | Bearer | Knowledge health stats (total/pending/compiled/dismissed entries, staleness) |
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | /api/v1/projects/{id}/pages | Bearer | List all wiki pages for a project |
| GET | /api/v1/projects/{id}/pages/{slug} | Bearer | Get a wiki page with backlinks |
| PUT | /api/v1/projects/{id}/pages/{slug} | Bearer | Create or update a wiki page (requires project_context feature) |
| DELETE | /api/v1/projects/{id}/pages/{slug} | Bearer | Delete a wiki page (requires project_context feature) |
| POST | /api/v1/projects/{id}/pages/{slug}/regenerate | Bearer | Regenerate an auto-generated concept page from latest entries |
| GET | /api/v1/projects/{id}/links/{target_type}/{target_id} | Bearer | Get backlinks for a target |
| PUT | /api/v1/projects/{id}/settings | Bearer | Update project settings (e.g. auto_narrative toggle) |
Tickets
Section titled “Tickets”| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | /api/v1/projects/{id}/tickets | Bearer (tickets:read) | List tickets. Query params: status, assigned_to, kind |
| POST | /api/v1/projects/{id}/tickets | Bearer (tickets:write) | Create a ticket (body: title, description, acceptance_criteria, priority, kind, parent_ticket_id, depends_on) |
| GET | /api/v1/projects/{id}/tickets/{tid} | Bearer (tickets:read) | Get a ticket with dependencies, dependents, and recent comments |
| PUT | /api/v1/projects/{id}/tickets/{tid} | Bearer (tickets:write) | Partial mutation for title / description / priority / acceptance_criteria / context_refs / file_refs / depends_on. Status transitions remain FSM-only. Authz: creator OR project admin. Optional lease_epoch for optimistic concurrency (409 + structured envelope on stale; last-write-wins when omitted; lease_epoch alone rejected with 400 — it's a fence, not a mutation). Side effect: every successful update auto-posts ONE author_persona='system' diff comment + writes per-field rows to the ticket_edits audit table (no-op writes don't pollute the audit). depends_on updates run same-project + cycle + dedup validation before wipe-and-reinsert. (v0.10.28) |
| POST | /api/v1/projects/{id}/tickets/{tid}/start | Bearer (tickets:write) | Atomic claim. Increments and returns lease_epoch, creates a server-side retrieval_audit_id, returns compiled persona + ticket context |
| POST | /api/v1/projects/{id}/tickets/{tid}/complete | Bearer (tickets:write) | in_progress → review. Pass lease_epoch to reject stale workers with 409 |
| POST | /api/v1/projects/{id}/tickets/{tid}/resolve | Bearer (tickets:write) | review → done. Atomic with rowcount-1 guard |
| POST | /api/v1/projects/{id}/tickets/{tid}/close | Bearer (tickets:write) | Issue terminator (rejects kind='task' with 400) |
| POST | /api/v1/projects/{id}/tickets/{tid}/approve | Bearer (tickets:write) | Move suggested → open so an agent-source ticket can be started |
| POST | /api/v1/projects/{id}/tickets/{tid}/escalate | Bearer (tickets:write) | Raise priority and append a non-idempotent audit comment |
| POST | /api/v1/projects/{id}/tickets/{tid}/comments | Bearer (tickets:write) | Append a comment to the ticket thread |
| GET | /api/v1/projects/{id}/tickets/{tid}/comments | Bearer (tickets:read) | Paginated comment thread (query: since, since_id, limit) |
| GET | /api/v1/projects/{id}/tickets/{tid}/review-state | Bearer (tickets:read) | Derived review state — open/closed findings, last verdict, severity counts |
Organizations
Section titled “Organizations”| Method | Path | Auth | Description |
|--------|------|------|-------------|
| POST | /api/v1/org | Bearer | Create organization (Team tier or above required) |
| GET | /api/v1/org | Bearer | Get org info, member list, and current user role |
| POST | /api/v1/org/invite | Bearer | Invite a member by email (admin only). Sends invite email best-effort (v0.10.22). UPSERTs over stale (org_id, email) rows (declined / expired / orphan-accepted) since v0.10.28 — regenerates id to invalidate stale acceptance links and preserves created_at as the audit signal. |
| POST | /api/v1/org/invite/{invite_id}/accept | Bearer | Accept an org invite |
| POST | /api/v1/org/invite/{invite_id}/decline | Bearer | Decline a pending invite (recipient only, optional decline_reason) — v0.10.22 |
| GET | /api/v1/org/invites | Bearer | List pending invites (admin only) |
| GET | /api/v1/org/invites/me | Bearer | List pending invites addressed to the caller (v0.10.22) |
| DELETE | /api/v1/org/invites/{invite_id} | Bearer | Revoke a pending invite (admin only) |
| POST | /api/v1/orgs/{org_id}/invites/{invite_id}/resend | Bearer | Resend an invite email (admin only) — v0.10.22 |
| POST | /api/v1/orgs/{org_id}/invite | Bearer | Org-scoped invite path (admin only). Same UPSERT-over-stale semantics as /api/v1/org/invite since v0.10.28. |
| PUT | /api/v1/org/members/{user_id}/role | Bearer | Change a member's role (admin only) |
| DELETE | /api/v1/org/members/{user_id} | Bearer | Remove a member from the org (admin only) |
| Method | Path | Auth | Description |
|--------|------|------|-------------|
| POST | /api/v1/admin/users/{user_id}/api-keys | Bearer (admin) | Mint a user-kind ApiKey on behalf of any active user as a lost-key recovery path. Raw key returned exactly once; audit row via _log_action(action="mint_api_key_on_behalf", details={key_id, name}). Guards: 404 unknown user, 403 inactive user (no backdoor for disabled accounts), 403 non-admin caller. Service keys are NOT minted here — they remain at /api/v1/orgs/{org_id}/service-keys per v0.10.10. (v0.10.28) |
| POST | /api/v1/admin/projects/{project_id}/restore-from-compilation | Bearer (admin) | Restore project.context_document from a prior ContextCompilation.context_after; restore compiled_at on participating entries. Body: {compilation_id: int, dry_run: bool = true}. Single atomic transaction. Audit-logged. (v0.10.13) |
Billing
Section titled “Billing”| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | /api/v1/billing/status | Bearer | Current subscription status, tier, storage usage |
| POST | /api/v1/billing/checkout | Bearer | Start a SessionFS Cloud plan checkout (body: tier, seats) |
| POST | /api/v1/billing/portal | Bearer | Open the SessionFS Cloud subscription page |
Health
Section titled “Health”| Method | Path | Auth | Description |
|--------|------|------|-------------|
| GET | /health | None | Health check |
OpenAPI
Section titled “OpenAPI”After deployment, interactive API docs are available at:
- Swagger UI:
https://your-domain/api/docs - ReDoc:
https://your-domain/api/redoc