Skip to main content

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