API Reference
All endpoints are prefixed with /api/v1 unless noted otherwise. Authentication is via JWT Bearer token.
Interactive documentation is available at /docs (Swagger UI) when the server is running. Swagger is generated from the Fastify route schemas registered in src/app.ts, the OpenAPI server URL is /api/v1, and the generated spec supports English, Russian, and Ukrainian via /docs/json?lang=en|ru|uk.
Error Responses
Error responses use a single envelope:
{
"error": "ValidationError",
"message": "Request validation failed",
"statusCode": 422,
"details": {}
}
details is optional and appears only when the route or middleware has structured diagnostics.
Authentication
Register
POST /api/v1/auth/register
Body: { "email": string, "password": string, "name": string, "departmentId": string }
Response: { "token": string, "user": { id, email, name, role, departmentId } }
New registrations always use the default employee role. Client-supplied role is not accepted.
Login
POST /api/v1/auth/login
Body: { "email": string, "password": string }
Response: { "token": string, "user": { id, email, name, role, departmentId } }
Refresh Token
POST /api/v1/auth/refresh
Header: Authorization: Bearer <token>
Response: { "token": string }
Forgot Password
POST /api/v1/auth/forgot-password
Body: { "email": string }
Response is always { "ok": true } to avoid account enumeration. The current implementation logs the reset token and leaves mail delivery as a mailer integration point.
Reset Password
POST /api/v1/auth/reset-password
Body: { "token": string, "newPassword": string }
newPassword must be at least 8 characters and include at least one letter and one digit.
Response: { "ok": true }
Knowledge Base Management
List Knowledge Bases
GET /api/v1/knowledge/bases
Query: ?departmentId=<id> (optional; allowed only within the caller's effective department scope)
Auth: any authenticated user
Get Knowledge Base
GET /api/v1/knowledge/bases/:id
Auth: any authenticated user with department access
Create Knowledge Base
POST /api/v1/knowledge/bases
Body: { "name": string, "departmentId": string, "description"?: string }
Auth: any authenticated user with department access
Update Knowledge Base
PATCH /api/v1/knowledge/bases/:id
Body: { "name"?: string, "description"?: string|null }
Auth: any authenticated user with department access
Update Knowledge Base RAG Config
PATCH /api/v1/knowledge/bases/:id/config
Body: { "ragWeights": { "vector": number, "keyword": number } }
Auth: permission canUploadDocuments
Delete Knowledge Base
DELETE /api/v1/knowledge/bases/:id
Auth: permission canDeleteDocuments
List Documents
GET /api/v1/knowledge/documents
Query: ?knowledgeBaseId=<id>&status=<status>&type=<type>
Auth: any authenticated user
Upload Document (file)
POST /api/v1/knowledge/upload
Content-Type: multipart/form-data
Fields: file (binary), knowledgeBaseId (string), title (string)
Auth: any authenticated user with department access
Accepted formats: PDF, DOCX, TXT, images (PNG, JPG)
Create Document (URL)
POST /api/v1/knowledge/documents
Body: { "knowledgeBaseId": string, "title": string, "type": "url", "originalUrl": string }
Auth: any authenticated user with department access
Update Document
PATCH /api/v1/knowledge/documents/:id
Body: { "title"?: string, "originalUrl"?: string|null, "metadata"?: object }
Auth: any authenticated user with department access
Get Document
GET /api/v1/knowledge/documents/:id
Response includes document metadata and chunks when authorized. Auth: any authenticated user with department access
Reprocess Document
POST /api/v1/knowledge/documents/:id/reprocess
Requeues the document for ingestion. Auth: any authenticated user with department access
Delete Document
DELETE /api/v1/knowledge/documents/:id
Auth: permission canDeleteDocuments
Approvals (HITL)
List Pending Approvals
GET /api/v1/approvals
Query: ?departmentId=<id>&limit=<n>&offset=<n>
Auth: permission canApprove (department-scoped for non-global users)
Get Approval
GET /api/v1/approvals/:id
Response includes RAG sources used for the response.
Auth: permission canApprove
Approve Message
POST /api/v1/approvals/:id/approve
Body: { "editedContent"?: string } (optional edit before approval)
Auth: permission canApprove
Reject Message
POST /api/v1/approvals/:id/reject
Body: { "reason"?: string }
Auth: permission canApprove
Batch Approve/Reject
POST /api/v1/approvals/batch
Body: { "decisions": [{ "approvalId": string, "action": "approve"|"reject", "editedContent"?: string, "reason"?: string }] }
Max 50 decisions per request.
Auth: permission canApprove
Escalate
POST /api/v1/approvals/:id/escalate
Body: { "escalatedToId": string, "reason"?: string }
Auth: permission canEscalate
Conversations
List Conversations
GET /api/v1/conversations
Query: ?departmentId=<id>&channel=<channel>&status=<status>&limit=<n>&offset=<n>
Auth: permission canApprove (scoped to department for non-global users)
Get Messages
GET /api/v1/conversations/:id/messages
Query: ?cursor=<message-id>&limit=<n>
Auth: permission canApprove (scoped to department for non-global users)
Employee Profiles
List Profiles
GET /api/v1/employee-profiles
Auth: any authenticated user. Results are limited to the caller's effective department scope; global users may filter by departmentId.
Get Profile
GET /api/v1/employees/:userId/profile
Auth: own profile or a profile inside the caller's effective department scope.
Update Profile
PATCH /api/v1/employees/:userId/profile
Body: { "summary"?: string, "currentProjects"?: string[], "preferences"?: object, "frequentIntents"?: string[], "accessLevel"?: string, "metadata"?: object }
Auth: users may update their own profile; permission canManageEmployeeProfiles is required for other visible profiles.
Namespaces
List Namespaces
GET /api/v1/namespaces
Auth: any authenticated user (scoped to department)
Get Namespace
GET /api/v1/namespaces/:id
Auth: any authenticated user (scoped to department)
Create Namespace
POST /api/v1/namespaces
Body: { "name": string, "departmentId": string, "systemPrompt"?: string, "persona"?: object, "config"?: object }
Optional config.agentRunner selects the namespace adapter.
Auth: permission canManageNamespaces
Update Namespace
PATCH /api/v1/namespaces/:id
Body: { "name"?: string, "systemPrompt"?: string|null, "persona"?: object, "config"?: object }
Auth: permission canManageNamespaces
Delete Namespace
DELETE /api/v1/namespaces/:id
Auth: permission canManageNamespaces
Intents
List Intent Names
GET /api/v1/intents
Query: ?namespaceId=<id>&limit=<n>&offset=<n>
Auth: any authenticated user with namespace department access
Get Trust Matrix
GET /api/v1/intents/trust
Query: ?namespaceId=<id>
Auth: permission canManageNamespaces
Add Intent Example
POST /api/v1/intents/examples
Body: { "namespaceId": string, "intentName": string, "exampleText": string }
Auth: permission canManageNamespaces
Classify Text
POST /api/v1/intents/classify
Body: { "text": string, "namespaceId": string }
Auth: any authenticated user
RAG Testing
Draft Query
POST /api/v1/rag/draft
Body: { "departmentId": string, "text": string }
Response: { "answer": string, "sources": array, "injectionDetected": boolean, "injectionReason"?: string }
Auth: permission canViewKnowledge
Departments
List Departments
GET /api/v1/departments
Query: ?search=<text>&includeArchived=<bool>&limit=<n>&offset=<n>
Auth: any authenticated user; results are scoped to the user's effective departments.
Get Department
GET /api/v1/departments/:id
Auth: any authenticated user with access to that department
Create Department
POST /api/v1/departments
Body: { "name": string, "slug"?: string, "color"?: string, "description"?: string|null, "settings"?: object }
Auth: permission canManageDepartments
Update Department
PATCH /api/v1/departments/:id
Body: { "name"?: string, "color"?: string, "description"?: string|null, "settings"?: object }
Auth: permission canManageDepartments
Archive Department
POST /api/v1/departments/:id/archive
Soft-archives a department.
Auth: permission canArchiveDepartments
Users
List Users
GET /api/v1/users
Query: ?role=<role>&departmentId=<id>&search=<text>&limit=<n>&offset=<n>
Auth: permission canViewAllUsers
Create User
POST /api/v1/users
Body: { "email": string, "password": string, "name": string, "phone"?: string|null, "role"?: Role, "departmentId": string }
Auth: permission canCreateUsers
Get User
GET /api/v1/users/:id
Auth: self, or permission canViewAllUsers within effective department scope
Update User
PATCH /api/v1/users/:id
Body: { "name"?: string, "phone"?: string|null, "role"?: Role, "roleId"?: string, "departmentId"?: string, "isActive"?: boolean }
Auth: users may update their own profile fields; permission canManageUsers is required for role, department, or active-state changes.
Deactivate User
DELETE /api/v1/users/:id
Soft-deactivates the user and invalidates sessions.
Auth: permission canManageUsers
Effective Access
GET /api/v1/users/:id/effective-access
Returns role object, permission overrides, and computed effective permissions/departments.
Auth: self, or permission canViewAllUsers within effective department scope
User Audit
GET /api/v1/users/:id/audit
Auth: permission canViewAudit
Permission Overrides
POST /api/v1/users/:id/permissions/grant
POST /api/v1/users/:id/permissions/revoke
DELETE /api/v1/users/:id/permissions/grant/:permission
DELETE /api/v1/users/:id/permissions/revoke/:permission
Grant/revoke accepts either { "permission": string } or { "departmentId": string }.
Auth: permission canManageUsers
Roles & Permissions
Permission Catalog
GET /api/v1/permissions
Returns grouped permission metadata for role editors. Auth: any authenticated user
List Roles
GET /api/v1/roles
Query: ?search=<text>&includeSystem=<bool>&limit=<n>&offset=<n>
Auth: permission canViewRoles
Create Role
POST /api/v1/roles
Body: { "name": string, "slug"?: string, "allDepartments"?: boolean, "departmentIds"?: string[], "permissions"?: string[] }
Auth: permission canManageRoles
Get Role
GET /api/v1/roles/:id
Auth: permission canViewRoles
Update Role
PATCH /api/v1/roles/:id
Body: { "name"?: string, "slug"?: string, "allDepartments"?: boolean, "departmentIds"?: string[], "permissions"?: string[] }
Auth: permission canManageRoles
Delete Role
DELETE /api/v1/roles/:id
System roles and roles assigned to users cannot be deleted.
Auth: permission canManageRoles
Audit Logs
List Audit Logs
GET /api/v1/audit
Query: ?action=<action>&entityType=<type>&userId=<id>&limit=<n>&offset=<n>
Auth: permission canViewAudit
Traces
Get Trace
GET /api/v1/traces/:trace_id
Response: AgentRun with retrieval hits. Auth: any authenticated user (scoped to department for non-global users)
Analytics Dashboard
GET /api/v1/analytics/dashboard
Query: ?days=<1-90>&departmentId=<id>&from=<datetime>&to=<datetime>
Auth: any authenticated user (scoped to department for non-global users)
Current User
Get Current User
GET /api/v1/me
Returns the current user, department, profile summary, role object, effective permissions/departments, and UI capability booleans. Auth: any authenticated user
Change Password
POST /api/v1/me/change-password
Body: { "currentPassword": string, "newPassword": string }
Returns a fresh JWT after incrementing tokenVersion.
Auth: any authenticated user
Logout All Sessions
POST /api/v1/me/logout-all
Invalidates existing tokens and disconnects active WebSocket sessions. Auth: any authenticated user
Avatar
POST /api/v1/me/avatar
DELETE /api/v1/me/avatar
Upload accepts one multipart image file (PNG, JPG, or WebP, max 2 MB) and stores /static/avatars/<user-id>.<ext>.
Auth: any authenticated user
Health
Health Check
GET /api/v1/health
No auth required.
Agent Tasks
List Agent Tasks
GET /api/v1/agent-tasks
Query: ?namespaceId=<id>&departmentId=<id>&status=<status>&adapterType=<type>&from=<datetime>&to=<datetime>&limit=<n>&offset=<n>
Status values: queued, running, done, failed, timeout
Response: { items: AgentTask[], total: number, limit: number, offset: number }
Auth: any authenticated user
Get Agent Task Detail
GET /api/v1/agent-tasks/:id
Response includes nested toolCalls array with step-by-step execution trace (stepName, status, inputData, outputData, durationMs, errorMessage).
Auth: any authenticated user
Agent Task Stats
GET /api/v1/agent-tasks/stats
Query: ?namespaceId=<id>&departmentId=<id>&adapterType=<type>&from=<datetime>&to=<datetime>
Response: { stats: [{ adapterType, namespaceId, total, successRate, avgDurationMs, avgCostUsd, byStatus }] }
Auth: any authenticated user
Integration Plugins
List Available Plugins
GET /api/v1/plugins
Returns built-in and registered plugin metadata. Auth: any authenticated user
List Namespace Plugins
GET /api/v1/namespaces/:id/plugins
Auth: permission canManagePlugins or canManageNamespaces
Enable Namespace Plugin
POST /api/v1/namespaces/:id/plugins/:pluginId
Body: { "config"?: object, "enabled"?: boolean }
Config is validated against the plugin's Zod schema.
Auth: permission canManagePlugins or canManageNamespaces
Update Namespace Plugin
PATCH /api/v1/namespaces/:id/plugins/:pluginId
Body: { "config"?: object, "enabled"?: boolean }
Auth: permission canManagePlugins or canManageNamespaces
Disable Namespace Plugin
DELETE /api/v1/namespaces/:id/plugins/:pluginId
Marks the namespace plugin disabled and removes it from the runtime registry cache.
Auth: permission canManagePlugins or canManageNamespaces
Document Templates
List Templates
GET /api/v1/document-templates
Query: ?category=<category>&departmentId=<id>&limit=<n>&offset=<n>
Auth: any authenticated user, department-scoped
Create Template
POST /api/v1/document-templates
Body: { "namespaceId": string, "departmentId"?: string|null, "name": string, "description"?: string|null, "category"?: "contract"|"proposal"|"letter"|"invoice"|"custom", "templateBody": string, "variables"?: array, "outputFormat"?: "md"|"html"|"docx"|"pdf" }
Auth: permission canManageTemplates
Update Template
PATCH /api/v1/document-templates/:id
Body accepts the same editable fields as create except namespaceId.
Auth: permission canManageTemplates
Delete Template
DELETE /api/v1/document-templates/:id
Auth: permission canManageTemplates
Generate Document
POST /api/v1/document-templates/:id/generate
Body: { "input"?: object, "preview"?: boolean, "outputFormat"?: "md"|"html"|"docx"|"pdf", "saveToKnowledgeBaseId"?: string|null }
md, html, and docx exports are implemented. pdf is reserved and currently returns 501.
Auth: permission canGenerateDocuments
Generation History
GET /api/v1/document-generations
GET /api/v1/document-generations/:id
Query filters: ?userId=<id>&templateId=<id>&limit=<n>&offset=<n>.
Users see their own generation history unless they have template-management permission for the department.
Notifications
List Notifications
GET /api/v1/notifications
Query: ?unreadOnly=<bool>&limit=<n>&offset=<n>
Response includes items, pagination, and unreadCount.
Auth: any authenticated user
Mark Read
PATCH /api/v1/notifications/:id/read
Auth: owner only
Mark All Read
POST /api/v1/notifications/read-all
Auth: any authenticated user
Delete Notification
DELETE /api/v1/notifications/:id
Auth: owner only
WebSocket
Agent Task Events
ws://<host>/ws/agent-tasks
Subscribe to real-time agent task lifecycle events per namespace.
Authentication: First message must be a JSON auth handshake:
{ "action": "auth", "token": "<jwt>" }
Server responds with { "event": "authenticated", "payload": {} } on success. All other messages before authentication are rejected and the connection is closed.
Client messages (after auth):
{ "action": "subscribe", "namespaceId": "<id>" }— subscribe to a namespace{ "action": "unsubscribe", "namespaceId": "<id>" }— unsubscribe from a namespace (omit namespaceId to unsubscribe all)
Server events:
| Event | Payload |
|-------|---------|
| agent-task.created | { taskId, jobId, namespaceId, adapterType, status: "queued" } |
| agent-task.started | { taskId, jobId, namespaceId, adapterType, status: "running" } |
| agent-task.tool-call | { taskId, ..., progress, toolCall: { id, stepName, status, inputData, outputData, durationMs } } |
| agent-task.completed | { taskId, ..., status: "done", output, durationMs } |
| agent-task.failed | { taskId, ..., status: "failed"|"timeout", errorMessage } |
Authentication: JWT token sent via first-message handshake (never via query parameter).
Notification Events
ws://<host>/ws/notifications
Uses the same first-message JWT handshake as agent-task events. Server pushes current-user notification events only.
Admin
Bull-board Queue Dashboard
GET /admin/queues
Technical queue monitoring UI. Shows all BullMQ queues (agent-tasks, wa-inbound, wa-outbound, tg-inbound, tg-outbound, knowledge-ingest, memory-extraction).
Auth: permission canEditSettings
Channel Endpoints
WhatsApp
GET /api/v1/whatsapp/status # Connection status
GET /api/v1/whatsapp/webhook # Meta verification (hub.challenge)
POST /api/v1/whatsapp/webhook # Inbound messages from Meta
Telegram
GET /api/v1/telegram/status # Bot status