API Reference
REST API of the Express server (port 3000, prefix /api), with an interactive Swagger UI and an OpenAPI spec served by the server itself, documented here by domain with its authentication mechanisms, rate limits, and response conventions.
REST API of the Express server (port 3000, prefix /api), with an interactive Swagger UI and an OpenAPI spec served by the server itself, documented here by domain with its authentication mechanisms, rate limits, and response conventions.
Authentication
| Method | Header | Usage |
|---|---|---|
| JWT | Authorization: Bearer <token> | Internal users (frontend, Tauri) |
| API key | X-Api-Key: sk-... | Widget embed, programmatic integrations |
| Collector secret | X-Collector-Secret | Server↔collector (constant-time comparison) |
| Query token | ?token= | OCR image access |
Middlewares (packages/server/src/middleware/): auth.ts, rbac.ts, license.ts, rateLimit.ts, widgetCors.ts.
Rate limit
| Limiter | Limit (prod/dev) | Scope |
|---|---|---|
| general | 200 / 2000 req/min | IP |
| auth | 10 / 100 req/min | IP |
| probe | 10 / 100 req/min | IP |
| widget chat | 30 / 200 req/min (per-widget override) | widgetId |
| widget daily | 5 / 50 messages per day | widgetId + IP |
| widget session | 50 / 500 creations per day | IP |
| widget lead | 3 / 30 per hour | IP |
There is no dedicated chat rate limiter: the ReAct agent enforces its own limits internally (per-user concurrency, token budgets, wallclock timeouts - agentBudgetService.ts); the global 200/min limiter remains the safety net. Requests carrying an X-Widget-Id header skip the global limiter (the widget service throttles them upstream); widgets use rateLimitPerMinute / sessionLimitPerDay as per-widget overrides.
Response conventions
- Success: JSON entity or
{ ok: true }. - Error:
{ error: string }(+detailsfor Zod validation errors). - License gating:
402 { error, feature, tier, limit?, current? }. - RBAC:
401(no auth),403(insufficient permissions). - SSE:
event: <type>\ndata: <json>\n\n.
Auth - /api/auth (routes/auth.ts)
| Method | Path | Auth | RBAC | Notes |
|---|---|---|---|---|
| POST | /auth/register | public | - | Only if ALLOW_REGISTRATION=true; body registerSchema |
| POST | /auth/admin-register | JWT | requireAdmin | body adminRegisterSchema |
| POST | /auth/login | public | - | body loginSchema (safeParse → 400 when invalid) → { user, token } |
| GET | /auth/me | JWT | - | Current profile |
| GET | /auth/users | JWT | requireAdmin | User list |
| POST | /auth/change-password | JWT | - | changePasswordSchema |
| POST | /auth/set-initial-password | JWT | - | First login (mustChangePassword) |
| POST | /auth/admin-reset-password | JWT | requireAdmin | Reset a user's password |
Users - /api/users (routes/users.ts, authMiddleware)
| Method | Path | RBAC | Notes |
|---|---|---|---|
| GET | /users | requireAdmin | Paginated list |
| GET | /users/:id | self/admin | |
| PUT/PATCH | /users/:id | self/admin | updateUserSchema |
| DELETE | /users/:id | requireAdmin | |
| POST/DELETE | /users/:id/avatar | JWT | Avatar upload/removal (multipart) |
Roles - /api/roles (routes/roles.ts, authMiddleware)
| Method | Path | RBAC | Notes |
|---|---|---|---|
| GET | /roles/me/menu-sections | JWT | Visible sidebar sections |
| GET | /roles | requireAdmin | Role list |
| GET | /roles/:roleId | requireAdmin | Detail (UUID) |
| POST | /roles | requireAdmin | Create role (createRoleSchema, isDefault=false) |
| PUT | /roles/:roleId | requireAdmin | Update (atomic permission replace) |
| PUT | /roles/:roleId/menu-sections | requireAdmin | Menu sections |
| DELETE | /roles/:roleId | requireAdmin | |
| POST | /roles/assign | requireAdmin | Assign a role to a user |
| POST | /roles/revoke | requireAdmin | Revoke a role |
Projects - /api/projects (routes/projects.ts, authMiddleware)
| Method | Path | RBAC | License | Notes |
|---|---|---|---|---|
| GET | /projects | JWT | - | Paginated list |
| POST | /projects | project:create | max_projects | Creation |
| GET | /projects/:projectId | requireProjectAccess | - | |
| GET | /projects/:projectId/usage | requireProjectAccess | - | Usage statistics |
| GET | /projects/:projectId/export | requireProjectAccess | - | Archive stream |
| PUT | /projects/:projectId | requireProjectAccess | - | |
| DELETE | /projects/:projectId | requireProjectAccess | - | Soft delete |
| POST/DELETE | /projects/:projectId/access | requireProjectAccess | - | ProjectAccess management |
Workspaces - /api/workspaces (routes/workspaces.ts, authMiddleware)
| Method | Path | RBAC | License | Notes |
|---|---|---|---|---|
| GET | /workspaces | JWT | - | List |
| POST | /workspaces | workspace:create | max_workspaces | Creation |
| GET/PUT/DELETE | /workspaces/:workspaceId | requireWorkspaceAccess | - | Soft delete |
| PUT | /workspaces/:workspaceId/restore | requireWorkspaceAccess | - | Restore soft-deleted |
| POST | /workspaces/:workspaceId/access | requireWorkspaceAccess | - | WorkspaceAccess |
| GET/POST/PUT/DELETE | /workspaces/:workspaceId/folders[/:folderId] | requireWorkspaceAccess | - | Chat folders |
| PUT | /workspaces/:workspaceId/folders/:folderId/restore | requireWorkspaceAccess | - |
Chat - mounted under /api/workspaces (routes/chat*.ts, authMiddleware)
| Method | Path | RBAC | Notes |
|---|---|---|---|
| POST | /workspaces/:id/chat | requireWorkspaceAccess | Non-streaming → { chat, message } |
| POST | /workspaces/:id/chat/stream | requireWorkspaceAccess | SSE |
| GET | /workspaces/:id/chats | requireWorkspaceAccess | Paginated list (chatList.ts) |
| GET | /workspaces/:id/chats/:chatId/messages | requireWorkspaceAccess | Paginated messages |
| PUT | /workspaces/:id/chats/:chatId | requireWorkspaceAccess | Rename (chatCrud.ts) |
| PATCH | /workspaces/:id/chats/:chatId/model | requireWorkspaceAccess | Per-chat model { providerId?, model? } |
| PATCH | /workspaces/:id/chats/:chatId/folder | requireWorkspaceAccess | Move to folder |
| DELETE | /workspaces/:id/chats/:chatId | requireWorkspaceAccess | |
| PUT | /workspaces/:id/chats/:chatId/move | requireWorkspaceAccess | Reorder |
| POST/DELETE | /workspaces/:id/chats/:chatId/pin | requireWorkspaceAccess | Pin chat |
| DELETE/PUT | /workspaces/:id/chats/:chatId/messages/:messageId | requireWorkspaceAccess | Delete/edit message |
| GET | /workspaces/:id/agent-config | requireWorkspaceAccess | Workspace agent config (chatAgentConfig.ts) |
| PUT | /workspaces/:id/agent-config | requireWorkspaceAccess | |
| GET | /workspaces/:id/chats/export | requireWorkspaceAccess | Export (json/md/pdf) (chatExport.ts) |
| GET | /workspaces/:id/chats/:chatId/export | requireWorkspaceAccess | |
| POST | /workspaces/:id/chats/import/preview | requireWorkspaceAccess | Import preview (chatImport.ts) |
| POST | /workspaces/:id/chats/import/confirm | requireWorkspaceAccess | Confirm import |
| GET | /workspaces/:id/chats/:chatId/tokens | requireWorkspaceAccess | Tokens used (chatTokens.ts) |
| GET | /workspaces/:id/tokens/today | requireWorkspaceAccess |
SSE - /workspaces/:id/chat/stream
Events: token, status, citations ({ sources }), done ({ chatId, messageId, modelUsed, providerUsed, mcpSources }), error ({ error }), plus plan and wiki_edit.
Connectors (Enterprise) - /api/connectors (routes/connectors.ts, connectorOAuthCallback.ts) - F-CONN-2
Gating: authMiddleware → tenantContextMiddleware → requireAdmin → requireFeature("connectors_enabled") (Community: fail-closed, F-CONN-1); per-route connector:manage/connector:view. The platform webhooks run on a SEPARATE PUBLIC router (connectorsWebhookRouter, mounted on the same prefix - NO RBAC, per-platform signature/secret) and the OAuth callback on connectorOAuthCallback.ts.
| Method | Path | Permission | Notes |
|---|---|---|---|
| POST | /connectors | connector:manage | Create the connector (name/archive binding/welcome-fallback/fallback locale/limits) |
| GET | /connectors | connector:view | List |
| GET | /connectors/:id | connector:view | Detail |
| PUT | /connectors/:id | connector:manage | EDIT (v0.28.1, F-CONN-2): name/binding/messages/limits editable - platform/token immutable; emits the chat_connector.updated audit event |
| DELETE | /connectors/:id | connector:manage | Delete |
| GET | /connectors/oauth/providers | connector:manage | Available OAuth providers |
See 13 - Connectors.
Documents & Uploads (routes/documents.ts, routes/uploads.ts)
| Method | Path | RBAC | Notes |
|---|---|---|---|
| GET | /documents | JWT | List |
| GET | /documents/:id | JWT | Detail |
| GET | /documents/:id/text | JWT | Extracted text |
| POST | /documents/upload | document:write | Multipart upload |
| PUT | /documents/:id/status | (pre-middleware) | Collector callback |
| DELETE | /documents/:id | document:delete | Soft delete |
| POST | /uploads | document:write | Draft staging (uploads.ts) |
| POST | /uploads/:id/assign | document:write | Assign draft to a workspace |
| GET | /uploads/pending | document:write | Pending drafts |
| DELETE/PATCH | /uploads/:id | document:write |
API Keys - /api/api-keys (routes/apiKeys.ts, authMiddleware)
GET lists your own keys, POST creates a new one (the raw sk-... key is shown once only), DELETE /:keyId revokes.
Since v0.20.1 API keys are HMAC-SHA256: the sk- key is no longer bcrypt-hashed but hashed with a dedicated HMAC-SHA256 digest (API_KEY_HMAC_SECRET, a dedicated env var decoupled from JWT/encryption key rotation) and verification is an indexed O(1) lookup on api_keys.key_hash (Postgres unique index), not a bcrypt-loop comparison. Breaking change in v0.20.1: operators must re-issue widget/collector keys (see docs/API_KEY_MIGRATION.md). Since v0.22.0 secret comparisons (X-Api-Key cache-bust, X-Collector-Secret) are timing-safe (crypto.timingSafeEqual with a length guard). A missing/invalid key → 401; a server-side HMAC misconfiguration → 500 (fail-loud).
Agent Skills - /api/agent + /api/skills (routes/skills.ts, authMiddleware)
GET /api/agent/skills → builtin skills (rag_search, workspace_memory, document_temp_process).
Custom skills (v0.28.1, F-SKILL-1): CRUD GET/POST/PUT/DELETE /api/skills[...] with the skill:read/skill:create/skill:write/skill:delete permissions; create is gated by the max_skills license limit (Community 3, Enterprise Infinity). Scopes personal/global/workspace with server-side arbitration. POST /api/skills/:id/test → compiled-prompt preview (no LLM call, no DLP masking). See 15 - Custom Skills.
MCP Connections - /api/mcp-connections (routes/mcp.ts, authMiddleware + requireAdmin)
GET list, GET /statuses (live runtime), POST create, PUT /:id (auto-disconnect/reconnect when enabled), DELETE /:id, POST /:id/toggle, POST /:id/test (10s timeout, toolCount).
MCP Pins - /api/chats/:chatId/pins (routes/mcpPins.ts)
GET, POST (mcpPinCreateSchema), DELETE /:pinId.
MCP Server inbound (for IDEs) - agent/mcpServer.ts
GET /api/mcp/sse (SSE transport), POST /api/mcp/message (JSON-RPC).
Marketplace - /api/mcp-marketplace (routes/marketplace.ts, authMiddleware + requireAdmin)
GET list (query ?search, ?category, ?workspaceId for isInstalled), GET /:entryId, POST create entry, POST /:entryId/install, POST /:entryId/uninstall. See 07 - MCP Marketplace.
Providers / Models - /api/providers (routes/providers.ts, authMiddleware)
GET /models/available (frontend selector), GET list, GET/:id, POST create, PUT/:id, DELETE/:id, PUT /:id/set-default, GET /:id/models, POST /:id/models/refresh (discovery), POST /:providerId/models/pull, PUT/DELETE /:providerId/models/:modelId, PUT /:providerId/models/:modelId/set-default.
Provider Presets - /api/provider-presets (routes/providerPresets.ts)
GET list, GET /:presetId, POST /:presetId/install.
Widget (admin) - /api/widgets (routes/widgets.ts, authMiddleware + requireAdmin)
GET list, POST (license widget_enabled + max_widgets), GET/PUT/DELETE /:id, PUT /:id/workspaces (whitelist), GET /:id/workspaces, GET /:id/leads (paginated), GET /:id/leads/export (CSV, license lead_export), GET /:id/leads/:leadId. Analytics: GET /analytics/daily, /analytics/topics, /analytics/summary.
Internal Widget (embed) - /api/internal/widget (routes/internalWidget.ts, apiKeyMiddleware + widgetCors)
Authentication via API key (X-Api-Key). POST /search (RAG, IDOR-safe via widgetId), POST /lead, GET /:id/config, GET /session/:token, POST /session, PATCH /session/:token/increment, PATCH /session/:token/chat/archive. See 06 - Widget Integration.
Backup (routes/backups.ts, backupDestinations.ts, backupJobs.ts, restore.ts)
| Group | Path | Permissions | License |
|---|---|---|---|
| Logs | /api/system/backups | backup:log:read | - |
| Destinations | /api/backup-destinations | backup:destination:read/write | max_backup_destinations |
| Jobs | /api/backup-jobs | backup:job:read/write | - |
| Restore | /api/backups, /api/backups/restore/:logId[/dry-run] | backup:log:read / backup:restore:write | - |
POST /backup-jobs/:id/run (manual trigger), POST /backup-destinations/:id/test.
Webhooks - /api/webhooks (routes/webhooks.ts, authMiddleware + requireAdmin + requireFeature("webhooks"))
CRUD + POST /:id/test. HMAC-SHA256, exponential backoff retry (5 attempts), auto-disable after 5 failures.
Push - /api/system/push (routes/push.ts)
GET /vapid-key (public key), POST/DELETE /subscribe (license push_notifications), POST /test (admin).
License - /api/license (routes/license.ts)
GET /license/info (public) → { license, features }.
The instance-license slot (v0.28.1, F-LIC-1): GET /api/license/instance (Admin, metadata-only), PUT /api/license/instance ({ licenseKey } - paste + verify + persist + apply WITHOUT a restart; rejects are 400 { error, reason } with the closed reason invalid/expired/plugin_bound/community_tier), DELETE /api/license/instance (clears the paste - the instance reverts to the next-boot state). See 08 - Administration.
SSO - /api/sso (routes/sso.ts, authMiddleware + requireAdmin + requireFeature("sso_enabled"))
GET /config, PUT /config, POST /test.
Templates - /api/templates (routes/templates.ts, authMiddleware)
GET list, GET /:id, POST create, PUT/DELETE /:id (requireAdmin). {{var}} interpolation.
Settings - /api/system/settings (routes/settings.ts, authMiddleware + requireAdmin)
GET /embedding-config, GET /vector-db-config, GET settings, PUT settings ({ updated, rejected }), POST/DELETE /branding/icon (license white_label). See 03 - Configuration.
System - /api/system (routes/system.ts)
GET /is-initialized (public), POST /initialize (setup wizard, only when uninitialized), POST /reset-db (admin), POST /reindex-documents (admin, RAG reindex), POST /ocr/prewarm (admin).
Quotas & Costs - /api/quota + the cost surfaces (routes/quota.ts, Phases 203/207/209) - F-QUOTA-1
Token quotas (Phase 207): per-user/group, hourly reset (the quotaResetJob sweep).
| Method | Path | Permission | Notes |
|---|---|---|---|
| GET | /quota/me | JWT | Own quota state (window usage vs limit) |
| GET | /quota/rollup | agency:users:manage | Rollup across managed users |
| GET | /quota/:userId | agency:users:manage | A user's quota state |
| PUT | /quota/:userId | requireAdmin | Set/edit a user's token quotas |
| POST | /quota/:userId/reset | agency:users:manage | Manual reset (the hourly sweep also resets) |
Costs (Phase 203, server-side per-currency accounting):
| Method | Path | Permission |
|---|---|---|
| GET | /workspaces/:id/chats/:chatId/cost | requireWorkspaceAccess |
| GET | /workspaces/:id/cost/today | requireWorkspaceAccess |
| GET | /system/analytics/cost?days=N | requireAdmin |
| GET | /system/analytics/cost-by-model | requireAdmin |
Per-user model policy & BYOK (Phase 209): GET /me/provider-keys (own keys, encrypted at rest), PUT/DELETE /me/provider-keys/:providerId, PUT/DELETE /users/:userId/models/:providerId/:modelName/pricing, PUT /users/:userId/cloud-model-mode. See 17 - Quotas & Costs.
Audit / Event Log - /api/event-logs (routes/eventLogs.ts, authMiddleware + requireAdmin + requireFeature("audit_log_immutable"))
GET with filters (type/user/workspace/date/action), cursor pagination.
Analytics - /api/system/analytics (routes/analytics.ts, authMiddleware + requireAdmin)
GET /tokens, /models, /top-users.
Archives - /api/archives (routes/archives.ts + archivePages.ts, archiveConfig.ts, archiveSearch.ts, archiveGraph.ts, archiveExport.ts, archiveIndex.ts, archiveImport.ts, archiveSchemaTemplates.ts)
GET list, POST (archive:write), GET/PUT/DELETE /:archiveId (archive:delete for delete), POST /from-template, POST /:archiveId/reindex. Pages: GET/POST /:archiveId/pages, GET/PUT/DELETE /:archiveId/pages/:slug. Config, search (hybrid RRF), graph, export, index, import (copy-from-doc, import/:jobId, import/:jobId/callback), schema templates.
OCR - /api/ocr + /api/archives/:id/jobs (routes/ocr.ts, authMiddleware)
OCR catalog at /api/ocr. Archive jobs: GET /archives/:id/jobs, GET /:jobId, POST /:jobId/approve, POST /:jobId/reject, DELETE /:jobId, GET /:jobId/pages/:pageNumber/image (query token auth).
Synthesis - /api/synthesis (routes/synthesis.ts, authMiddleware)
GET /status, GET /pending/count, POST /trigger (archive:write + feature limit), GET /:runId, POST /:runId/approve, POST /:runId/reject, DELETE /:runId, PATCH /:runId/rename.
Wiki - /api/wiki-write, /api/wikilinks, /api/wiki-edits (routes/wikiChat.ts, wikilinks.ts)
POST /wiki-write/preview, POST /wiki-write/:runId/approve|reject|undo, GET /wiki-write/history/:archiveId, POST /wiki-write/distill. Links: POST /wikilinks/resolve, GET /wikilinks/:archiveId[/:pageSlug], maintenance GET/POST /wikilinks/maintenance/:archiveId[/merge].
Chat retention - /api/system/chat-retention (routes/chatRetention.ts)
PUT (system:write, confirmDataLoss: true, audited). See 03 - Configuration.
Health - / (routes/health.ts)
GET / (public, { uptime }), GET /api/health (public, { status, checks: { database, collector, disk } }), GET /api/health/rag.
Widget Service - porta 3211 (packages/widget)
GET /health, GET /widget/:id.js (loader), GET /widget/:id (HTML iframe), GET /api/config/:id (branding+trigger+lead), POST /api/config/:id/cache-bust, POST /api/sessions (anonymous session), POST /api/chat/:id/stream (SSE, x-session-token), POST /api/lead/:id (lead capture).
Collector Service - porta 3210 (packages/collector)
GET /api/health, GET /api/ingest/chunks/:documentId, POST /api/ingest (X-Collector-Secret, multipart), POST /api/ingest/query (vector search), DELETE /api/ingest/:documentId, POST /api/ingest/reembed, POST /api/ingest/youtube, POST /api/ingest/wiki-pages, DELETE /api/ingest/wiki-pages/:pageId, POST /api/ingest/archive-page.
v0.28.1 Provenance
| Statement | Source |
|---|---|
| The Connectors group + the EDIT arm + OAuth callback + the public webhook router | packages/server/src/routes/connectors.ts (row-time inventory, 35-02 probe) + CHANGELOG.md v0.28.1 (Added) - F-CONN-2 |
| Quota/costs/BYOK/per-user model policy (Phases 203/207/209) | docs/API.md:377-405 (row-time) - F-QUOTA-1 |
| Custom-skill CRUD + the test preview | docs/API.md:358-360 + docs/CUSTOM_SKILLS.md - F-SKILL-1 |
The instance-slot routes (GET/PUT/DELETE /api/license/instance) | docs/API.md:157-159 + docs/ADMIN.md - F-LIC-1 |
Operational reference - Simmetric Chat v0.28.1 (docs updated 2026-10-02, census SHA `aca5b8ff`).
Cross-link
- Auth/RBAC detail: 08 - Administration
- Widget embed how-to: 06 - Widget Integration
- MCP lifecycle: 07 - MCP Marketplace