AI Assistant API
The AI assistant provides a conversational interface with full access to Opbox data via 129 built-in tools. It supports multiple AI model providers, real-time streaming responses, plan mode, saved prompts, @-mention entity references, and live UI refresh when the AI performs write operations.
Overview
- Accessible via
Cmd+K(orCtrl+K) -> AI tab - Real-time streaming responses, displayed word by word
- 129 tools for search, knowledge retrieval, data operations, user interaction, dashboards, files, and more
- Support for multiple AI model providers
- Conversation persistence with thread management
- Page context awareness - AI knows which page you're viewing and can access relevant data automatically
- Plan Mode: restricts AI to read-only tools; no writes possible even if requested
- Saved Prompts: per-user library of reusable prompts (max 50) with usage tracking
- @ Mentions: type
@in the chat input to search and attach entities (matters, forms, tables, documents, workflows, submissions) as context (max 10 references) - Live UI refresh: when the AI performs write operations (e.g. adding subtasks, completing steps), the host page automatically refreshes its data - no manual reload needed
- Write limits per session and per day to prevent misuse
- Security scanning on all tool results to detect and block injection attempts
Endpoints
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/ai/chat | Send a message and stream AI response with tool execution |
| POST | /api/integrations/ai/execute | Execute a one-shot AI prompt (used by automation AI steps) |
| POST | /api/ai/passport-ocr | Extract structured data from passport images using AI vision |
| POST | /api/ai/transcribe | Upload an audio file and transcribe it (Whisper or WhisperX with speaker diarization) |
| GET | /api/ai/transcripts | List the current user's own transcripts (private per-user) |
| GET | /api/ai/transcripts/:id | Get a single transcript by ID (must be owned by the requesting user) |
| DELETE | /api/ai/transcripts/:id | Delete a transcript (must be owned by the requesting user) |
Chat History
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/ai/chats | List chat sessions |
| POST | /api/ai/chats | Create a new chat session |
| GET | /api/ai/chats/:id | Get a chat session with metadata |
| PATCH | /api/ai/chats/:id | Update a chat session (rename, archive) |
| DELETE | /api/ai/chats/:id | Delete a chat session and all its messages |
| GET | /api/ai/chats/:id/messages | List messages in a chat session |
Skills
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/ai/skills | List skills (max 50 per user) |
| POST | /api/ai/skills | Create a skill |
| DELETE | /api/ai/skills/:id | Delete a skill |
| POST | /api/ai/skills/:id | Record usage of a skill |
Chat Streaming
Sends a conversation to the AI and streams the response as Server-Sent Events (SSE). The AI can call tools to query and modify Opbox data, then continue generating text. The AI may call multiple tools per request to fulfil your query.
POST /api/ai/chat
Content-Type: application/json
{
"messages": [
{ "role": "user", "content": "Summarise this matter and suggest next steps" }
],
"provider": "anthropic",
"pageContext": "/matters",
"references": [
{ "id": "cm1abc...", "type": "matters", "title": "ACME Ltd Incorporation" }
]
}
Body Parameters
| Field | Type | Required | Description |
|---|---|---|---|
messages | array | Yes | Conversation history with role (user/assistant) and content |
provider | string | No | "anthropic" or "openai". Auto-resolves based on configured API keys. |
pageContext | string | No | Current page pathname (e.g. /matters/cm...). Used to provide the AI with awareness of the current page. |
planMode | boolean | No | When true, the AI is restricted to read-only tools. Write operations are unavailable regardless of what the AI requests. |
scopeToPage | boolean | No | When true (and pageContext is set), the AI is instructed to focus on the current page's resource. |
references | array | No | Array of { id, type, title } objects from @-mention selections. Entity content is fetched and provided to the AI as context. Supported types: matters, forms, tables, documents, workflows, submissions. Max 10 references. |
Stream Events
The response is an SSE stream (text/event-stream). Each event is a JSON line:
| Event Type | Description |
|---|---|
text | Chunk of assistant text content |
text_delta | Token-level text delta (for real-time streaming) |
tool_start | Tool execution started (includes tool name and input) |
tool_result | Tool execution finished - includes toolName, result, and isError. Successful write operations trigger a live UI refresh. |
confirmation_request | AI is asking the user to confirm a write operation before proceeding. Includes message (description of the action) and actionDescription (short label). The UI renders an Approve/Deny dialog. User response is sent as a follow-up message. |
question_request | AI is asking the user a clarifying question. Includes question and optional options array. The UI renders an inline input with optional quick-pick buttons. |
error | Error during processing |
done | Stream complete |
Example Stream
data: {"type":"text_delta","content":"Here"}
data: {"type":"text_delta","content":" are"}
data: {"type":"text_delta","content":" your"}
data: {"type":"tool_start","toolName":"list_matters","toolId":"toolu_01...","input":{"status":"OPEN"}}
data: {"type":"tool_result","toolName":"list_matters","toolId":"toolu_01...","result":{...},"isError":false}
data: {"type":"text_delta","content":"You have 5 open matters..."}
data: {"type":"done"}
Available Tools (129)
The AI assistant has access to 129 tools across all major Opbox modules. Tools are automatically called by the AI based on user requests. Write operations use a confirm-then-act pattern - the AI calls ask_confirmation before mutating data, rendering an Approve/Deny dialog in the UI. Archive operations are intentionally excluded from the AI toolset - they are available in the UI only.
User Interaction
| Tool | Description |
|---|---|
ask_confirmation | Ask the user to approve a write operation before proceeding. Renders an Approve/Deny dialog. The AI pauses until the user responds. |
ask_question | Ask the user a clarifying question. Renders an inline input with optional quick-pick options. The AI pauses until the user answers. |
Search
| Tool | Description |
|---|---|
search | Search across all entities (forms, matters, tables, documents, etc.) |
Knowledge Retrieval (RAG)
| Tool | Description |
|---|---|
knowledge_search | Hybrid semantic + full-text search across all org content (documents, comments, notes, transcripts, compliance exceptions). Returns ranked chunks with source metadata and relevance scores. |
get_document_content | Retrieve full document content by ID for deeper context after a knowledge_search match. Returns complete structured JSON or plain text. |
get_document_extractions | Get AI-extracted structured data (entities, key-value pairs) from a document. |
Matters
| Tool | Description |
|---|---|
list_matters | List matters with filters (status, priority, board, assignee) |
search_matters | Full-text search across matter titles, descriptions, and metadata |
get_matter | Get matter details with steps and properties |
get_matter_by_number | Look up matter by number (e.g. MAT-0001) |
create_matter | Create a new matter from a board |
update_matter | Update matter fields (title, priority, status, due date, tags) |
complete_step | Complete a matter step - queued for confirmation |
get_step_form_data | Retrieve current form field values for a FORM step |
fill_step_form | Fill in form field values on a FORM step (user reviews and completes in UI) |
reject_step | Reject a matter step with optional reason - queued for confirmation |
skip_step | Skip a matter step |
reopen_step | Reopen a completed or rejected step - queued for confirmation |
assign_step | Assign a step to a user |
update_step_config | Update step configuration (name, description, required, assignee) |
preview_step_document | Preview a document template step with current matter data |
scaffold_form_from_pack | Auto-generate a form from a template pack definition |
add_comment | Add a comment to a matter step |
update_matter_metadata | Update board property values |
add_line_item | Add a line item with pricing |
list_line_items | List matter line items |
update_line_item | Update a line item |
list_subtasks | List subtasks for a step |
add_subtask | Add a subtask to a step |
complete_subtask | Toggle a subtask between completed and incomplete |
update_subtask | Update a subtask title or assignee |
delete_line_item | Archive (soft-delete) a line item - queued for confirmation |
get_matter_documents | List documents attached to a matter |
Boards
| Tool | Description |
|---|---|
list_boards | List all matter templates/boards |
get_board | Get board details with steps and settings |
list_board_properties | List metadata field definitions for a board |
list_labels | Get org-wide matter label suggestions |
Forms & Submissions
| Tool | Description |
|---|---|
list_forms | List forms with optional status filter |
get_form | Get form details with field definitions |
create_form | Create a new form with fields |
list_submissions | List submissions with filters |
get_submission | Get full submission data |
approve_submission | Approve a pending submission (destructive) |
reject_submission | Reject a pending submission (destructive) |
Tables & Data
| Tool | Description |
|---|---|
list_tables | List all tables in the workspace |
create_table | Create a new table with optional columns |
get_table_details | Get full table schema with columns and metadata |
update_table | Update table name or description (ADMIN/OWNER) |
delete_table | Delete a table permanently (ADMIN/OWNER) |
get_table_rows | Query table rows with server-side search and pagination |
summarize_table | Get aggregate statistics for a table (row count, column distributions) |
create_row | Create a new table row |
update_row | Update an existing table row |
patch_row | Partially update specific fields on a row without replacing all data |
delete_row | Delete a row from a table |
bulk_create_rows | Create up to 50 rows in one call |
bulk_update_rows | Update multiple rows in a single call (up to 50) |
create_column | Add a column to a table (18 types, ADMIN/OWNER) |
update_column | Update column name or config (ADMIN/OWNER) |
delete_column | Delete a column from a table (ADMIN/OWNER) |
Documents & Workflows
| Tool | Description |
|---|---|
list_documents | List documents in the knowledge base |
get_document | Get document content (truncated to 500 chars for safety) |
update_document | Update a document's content (full structured JSON) - queued for confirmation |
patch_document | Partially update document metadata (title, status, parent) without replacing content |
list_workflows | List automation workflows |
execute_workflow | Execute an automation - queued for confirmation |
Notifications & Organisation
| Tool | Description |
|---|---|
get_notifications | Get recent notifications |
mark_notifications_read | Mark notifications as read |
get_dashboard_stats | Get dashboard statistics |
list_members | List workspace members |
Transcripts (Private Per-User)
| Tool | Description |
|---|---|
list_my_transcripts | List the current user's own voice transcripts (private) |
get_my_transcript | Get full text of a transcript owned by the current user |
API Access
| Tool | Description |
|---|---|
list_api_endpoints | Discover available API endpoints (read-only) |
call_api_endpoint | Call any Opbox GET endpoint - restricted to read-only (GET) requests only |
Dashboard Tools
| Tool | Description |
|---|---|
list_dashboard_widgets | List all widgets on a dashboard |
add_dashboard_widget | Add a new widget to a dashboard with full config |
update_dashboard_widget | Update an existing widget (type, data source, appearance, layout) |
remove_dashboard_widget | Remove a widget from a dashboard |
bulk_update_dashboard | Batch update multiple widgets and dashboard settings in one call |
set_dashboard_config | Replace the entire dashboard config (widgets, filters, auto-refresh) |
get_dashboard_stats | Get summary statistics for the workspace dashboard |
SQL Query Library
| Tool | Description |
|---|---|
execute_sql | Execute a read-only SQL query against the workspace database |
save_sql_query | Save a SQL query to the user's library for later reuse |
list_saved_queries | List saved SQL queries for the current user |
run_saved_query | Execute a previously saved SQL query by ID |
File Registry
| Tool | Description |
|---|---|
list_files_for_entity | List files linked to a specific entity (document, form, etc.) |
list_files_for_matter | List all files associated with a matter and its steps |
list_files_for_row | List files linked to a specific table row |
get_file_record | Get metadata for a single file record by ID |
link_file_to_entity | Create a link between an existing file and an entity |
Identity & Access
| Tool | Description |
|---|---|
list_access_grants | List cross-workspace access grants for an entity |
list_workspace_grants | List all access grants visible to the current workspace |
get_document_presence | Check if a document exists across workspaces (presence only, no content) |
resolve_row_identity | Resolve a table row to a global identity |
get_file_version_history | Get version history for a file record |
get_identity_review_queue | List identity matches pending review |
resolve_identity_review | Approve or reject an identity match review |
get_open_escalations | List open escalations requiring attention |
resolve_escalation | Resolve an open escalation with a decision and notes |
check_file_sensitivity | Check sensitivity classification of a file |
get_registry_coverage | Get file registry coverage statistics for the workspace |
request_file_access | Request access to a file from another workspace |
get_extraction_quality | Get quality metrics for document extractions |
Ownership & Corporate Structure
| Tool | Description |
|---|---|
get_ownership_chain | Trace the ownership chain from entity to ultimate parent |
get_corporate_family_tree | Get the full corporate group tree for an entity |
detect_circular_ownership | Detect circular ownership references in entity structures |
get_ubos | Get ultimate beneficial owners (UBOs) for an entity |
check_ubo_mismatches | Compare declared vs computed UBOs and flag discrepancies |
Registers & Compliance
| Tool | Description |
|---|---|
get_register | Get a statutory register (shareholders, directors, etc.) for an entity |
get_register_history | Get historical changes to a register with audit trail |
get_stakeholder_exposure | Get cross-entity exposure summary for a stakeholder |
find_shared_stakeholders | Find stakeholders shared across multiple entities |
detect_conflicts_of_interest | Detect potential conflicts of interest based on stakeholder overlaps |
get_compliance_calendar | Get upcoming compliance deadlines and filing dates |
get_kyc_expiry_report | Get KYC document expiry report across entities |
get_entity_compliance_summary | Get overall compliance health summary for an entity |
Entity File Intelligence
| Tool | Description |
|---|---|
get_entity_files_deep | Deep file aggregation for a CSP entity - traverses direct files, matter files, and stakeholder files |
get_individual_files_deep | Deep file aggregation for a CSP individual - traverses direct files, matter files, and bridged KYC docs |
get_stakeholder_file_timeline | Chronological cross-entity file timeline for a stakeholder across all roles and matters |
link_entity_to_matter | Link a CSP entity to a matter |
link_individual_to_matter | Link a CSP individual to a matter |
sync_matter_entity_links | Sync entity links from matter metadata and linked records |
backfill_matter_files | Backfill file propagation for an existing matter |
bridge_kyc_documents | Bridge KYC documents to the file registry for unified search |
Self-Knowledge
| Tool | Description |
|---|---|
search_system_docs | Search Opbox's own product documentation by keyword. Returns relevant sections with relevance scoring. |
embed_system_docs | Index product documentation for semantic search. |
Self-knowledge tools enable the AI to answer questions about Opbox's own architecture, features, and data model. REST equivalents are available at GET /api/docs/search and POST /api/docs/embed. See the Intelligence Architecture page for details.
For a complete per-tool reference with parameter types and descriptions, see the AI Tools Reference page.
Parallel Tool Execution
The AI can call multiple tools in a single response. Independent tool calls execute concurrently on the server for maximum speed. The chat UI groups multiple tool calls into a single collapsible disclosure:
- Single tool call: Shown inline with status indicator and label (expandable for input/result)
- Multiple tool calls: Collapsed into "N tool calls" - click to expand and see each call individually, click a call to see its input/result
- During streaming: Tool group auto-expands to show progress; auto-collapses when all tools complete successfully
- Error handling: Failed tool count shown in the collapsed summary; group stays expanded if any tool errored
Chat Thread Management
Conversations are persisted server-side as threads. Each thread stores its messages with tool call history.
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/ai/chats | List chat threads for the current user |
| POST | /api/ai/chats | Create a new chat thread |
| GET | /api/ai/chats/:id | Get a thread with its messages |
| PATCH | /api/ai/chats/:id | Archive a chat thread |
| POST | /api/ai/chats/:id/messages | Persist messages (user + assistant) to a thread |
Confirm-Then-Act Pattern
Before performing any write operation, the AI calls ask_confirmation which renders an Approve / Deny dialog in the chat UI. The agentic loop pauses until the user responds. If approved, the AI proceeds with the action in the next turn. If denied, it acknowledges and stops. This replaces the previous apply: prefix requirement.
Similarly, ask_question renders an inline input when the AI needs clarification. The user can type a response or click a suggested option.
All write operations are scoped to the authenticated user's active workspace and logged in the audit trail.
Archive operations (archive_matter, archive_table, etc.) are not available to the AI - they are human-only actions accessible only through the Opbox UI.
Security Model
| Control | Value | Description |
|---|---|---|
| Session write limit | Per session | Maximum mutating tool calls per chat session. Resets on new thread. |
| Daily write limit | Per user | Per-user daily write cap across all sessions. Skipped in Plan Mode. |
| Context window | Recent messages | Only recent messages are sent to the AI per request. Full thread is preserved. |
| Injection scanning | Automatic | Tool results are scanned for injection attempts. Detected injections are sanitised before the AI processes them. |
| API endpoint access | Read-only | The call_api_endpoint tool is restricted to GET requests. Sensitive endpoints are blocked. |
| SQL queries | Read-only, sandboxed | Read-only SQL execution with workspace isolation, query timeout, and row limits. ADMIN role required. |
| Cell locking | Enforced | Locked cells cannot be modified by the AI or API updates. |
| Content truncation | Automatic | Document content and tool results are truncated to safe limits. |
Plan Mode
Plan Mode restricts the AI to read-only tools. When planMode: true is sent, write tools are not available to the AI and cannot be called. Toggle available in the chat UI as a "Plan" pill below the input box.
Page Context Preloading
When a user sends their first message from a matter or document page, the current resource's data is automatically preloaded for the AI. This eliminates the need for extra lookups before performing operations, significantly reducing latency.
Matter pages preload: matter title, status, priority, assignee, due date, labels, board name, board properties (with current values), step pipeline (with stepId for each step), form titles for FORM steps, and APPROVAL step -> form linkage. The AI can use preloaded step IDs directly for add_subtask, complete_step, assign_step, etc. without calling get_matter first.
Document pages preload: document title, content (first 3000 chars), and custom metadata properties.
Form Awareness in Matters
The AI understands which specific forms are associated with matter steps:
FORM steps: the AI sees the assigned form title (e.g. "KYC Questionnaire") for both pending and completed FORM steps. Multi-form steps with formIds show all available form titles. Completed FORM steps also include collected field data.
APPROVAL steps: the AI sees which FORM step an approval reviews (e.g. "reviews: Collect KYC - form: KYC Questionnaire"), derived from the reviewFormStepIndex in the approval step config.
Live UI Refresh
When the AI performs write operations via the sidebar chat, the host page automatically refreshes its data without requiring a manual page reload. For example, completing a step or adding a subtask via AI chat will immediately update the matter detail view.
Saved Prompts
Per-user reusable prompt library backed by Knowledge Base documents. Prompts are stored as full KB documents inside a "Prompts" system folder (auto-created on first KB visit alongside the Transcripts folder). Users can save frequently-used inputs from the chat UI, browse and edit them as rich documents in the Knowledge Base, and organise them into subfolders. Max 50 prompts per user (counted across the entire Prompts folder tree).
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/ai/skills | List skill documents from the Skills KB folder (including subfolders), newest first. Content extracted as plain text from structured JSON. |
| POST | /api/ai/skills | Create a skill document in the Skills KB folder - body: { title, content } (title max 120 chars, content max 5 000 chars; 50-skill limit). Plain text converted to structured JSON. |
| DELETE | /api/ai/skills/:id | Delete a skill document (must be under the Skills folder tree in the current org) |
| POST | /api/ai/skills/:id | Mark skill as used - body: { action: "use" } (no-op, kept for API compatibility) |
Admin: Security Events (OWNER only)
Query the workspace's security event log. Events are recorded automatically by the security monitoring system. Requires OWNER role.
GET /api/admin/security-events?type=AI_INJECTION_ATTEMPT&limit=50&cursor=cm...
| Query Param | Description |
|---|---|
type | Filter by event type: AI_INJECTION_ATTEMPT, AI_RATE_LIMIT, CSRF_VIOLATION, LOGIN_FAILURE, RATE_LIMIT |
cursor | Event ID for cursor-based pagination (from previous response nextCursor) |
limit | Results per page (max 100, default 50) |
{
"events": [
{
"id": "cm...",
"type": "AI_INJECTION_ATTEMPT",
"detail": "Injection pattern detected in tool result",
"metadata": { "toolName": "get_document", "pattern": "\\[INST\\]" },
"ipAddress": "1.2.3.4",
"createdAt": "2026-02-17T10:30:00.000Z",
"user": { "name": "Jane Doe", "email": "jane@example.com" }
}
],
"total": 42,
"nextCursor": "cm..."
}
The admin view at Settings -> Security -> Events surfaces this data with filter tabs and an injection alert banner.
Admin: RAG Embeddings (ADMIN / OWNER)
Monitor and manage the RAG (Retrieval-Augmented Generation) embedding pipeline. Content from documents, comments, notes, transcripts, and compliance exceptions is automatically chunked and embedded for semantic search.
Get Status
GET /api/admin/rag?sourceType=DOCUMENT
{
"ragEnabled": true,
"embeddingModel": "text-embedding-3-small",
"statusBySourceType": {
"DOCUMENT": { "COMPLETED": 42, "PENDING": 2 },
"MATTER_COMMENT": { "COMPLETED": 15 }
},
"chunksBySourceType": {
"DOCUMENT": { "chunks": 210, "tokens": 48500 }
},
"recentFailures": []
}
Trigger Backfill (OWNER only)
POST /api/admin/rag
Content-Type: application/json
{ "sourceType": "DOCUMENT" } // Optional - omit to backfill all types
Backfill runs asynchronously. Supported source types: DOCUMENT, MATTER_COMMENT, TRANSCRIPT, SUBMISSION_NOTE, REGULATOR_COMMENT, TABLE_ROW_NOTE, COMPLIANCE_EXCEPTION.
AI Execute (Automations)
One-shot AI prompt execution used by automation AI steps and the workflow builder test button. Supports both Anthropic and OpenAI providers. API keys are configured in user preferences (Settings -> AI).
POST /api/integrations/ai/execute
Content-Type: application/json
{
"provider": "anthropic",
"model": "claude-sonnet-4-5-20250929",
"prompt": "Summarize this form submission: ...",
"systemPrompt": "You are a helpful assistant.",
"temperature": 0.7,
"maxTokens": 1024
}
Response
{
"success": true,
"requestId": "ai-1708000000-abc1234",
"provider": "anthropic",
"model": "claude-sonnet-4-5-20250929",
"response": {
"content": "The form submission contains...",
"finishReason": "end_turn"
},
"usage": {
"promptTokens": 150,
"completionTokens": 200,
"totalTokens": 350
}
}
Provider Configuration
AI providers are configured in Settings -> AI (requires ADMIN or OWNER role). The system supports two providers:
| Provider | Models | Env Variable |
|---|---|---|
| Anthropic | Claude Sonnet 4.5, Claude Haiku 4.5, Claude Opus 4.6 | ANTHROPIC_API_KEY |
| OpenAI | GPT-5.2, o3, GPT-4o, GPT-4o Mini, o3-mini | OPENAI_API_KEY |
Keys configured in Settings -> AI take priority over environment variables. For automation AI steps, the system loads the org admin/owner's configured keys.
Passport OCR
AI-powered passport data extraction using vision models. Extracts structured data from passport images including name, nationality, date of birth, document number, expiry date, and MRZ data.
POST /api/ai/passport-ocr
Content-Type: application/json
{
"imageUrl": "https://example.com/passport-scan.jpg"
}
{
"success": true,
"data": {
"fullName": "JOHN DOE",
"nationality": "United Kingdom",
"dateOfBirth": "1985-03-15",
"documentNumber": "AB1234567",
"expiryDate": "2030-01-20",
"sex": "M",
"mrzLine1": "P<GBRDOE<<JOHN<<<<<<<<<<<<<<<<<<<<<<<<",
"mrzLine2": "AB12345670GBR8503151M3001207<<<<<<<<<<<04"
}
}
Voice Transcription
Transcribe audio files (voice memos, recordings) using Whisper (cloud) or WhisperX (local with speaker diarization). The provider is configured in Settings -> AI -> Transcription.
Privacy: Transcripts are private to the uploading user. The list and detail endpoints filter by the authenticated user's ID - users can only see and manage their own transcripts. The AI assistant accesses transcripts via dedicated list_my_transcripts / get_my_transcript tools which enforce the same per-user scoping.
Transcription Providers
| Provider | Speaker Diarization | Requirements |
|---|---|---|
| Whisper (cloud) | No - plain text only | OpenAI API key |
| WhisperX (local) | Yes - timestamped segments with speaker labels | Python 3, pip install whisperx, HuggingFace token (HF_TOKEN), WHISPERX_ENABLED=true |
Provider selection: auto uses WhisperX if available (enabled + installed), otherwise Whisper. Force a specific provider via transcriptionProvider in user AI preferences.
Upload & Transcribe
POST /api/ai/transcribe
Content-Type: multipart/form-data
file: <audio file (max 1GB)>
title: "Lee & Will" // optional - user-chosen display name
Supported formats: .m4a, .mp3, .wav, .webm, .ogg, .flac, .mp4, .qta (Apple Voice Memos - auto-converted to m4a). The UI shows a naming dialog on file selection - the title defaults to the filename (minus extension) and is used as the Knowledge Base document name with a date prefix (e.g. "18 February 2026 - Lee & Will").
// Response (WhisperX - with speaker diarization)
{
"id": "cm...",
"filename": "meeting.m4a",
"title": "Lee & Will",
"text": "Hello everyone, let's get started...",
"language": "en",
"durationSecs": 120.5,
"provider": "whisperx",
"speakerCount": 3,
"segments": [
{ "start": 0.5, "end": 3.2, "text": "Hello everyone, let's get started.", "speaker": "SPEAKER_00" },
{ "start": 3.8, "end": 7.1, "text": "Thanks for joining.", "speaker": "SPEAKER_01" }
],
"createdAt": "2026-02-18T10:30:00.000Z"
}
// Response (Whisper - plain text, no segments)
{
"id": "cm...",
"filename": "recording.m4a",
"title": null,
"text": "Hello, this is a transcription of...",
"language": "english",
"durationSecs": 42.5,
"provider": "openai",
"speakerCount": null,
"segments": null,
"createdAt": "2026-02-18T10:30:00.000Z"
}
List Transcripts
GET /api/ai/transcripts?search=meeting&page=1&limit=20
{
"transcripts": [
{
"id": "cm...",
"filename": "meeting-notes.m4a",
"title": "Lee & Will",
"fileSize": 1048576,
"mimeType": "audio/mp4",
"durationSecs": 120.3,
"text": "...",
"language": "en",
"status": "completed",
"provider": "whisperx",
"speakerCount": 2,
"segments": [...],
"createdAt": "2026-02-17T10:30:00.000Z",
"user": { "name": "John Doe", "email": "john@example.com" }
}
],
"total": 1,
"page": 1,
"totalPages": 1
}
Response Fields
| Field | Type | Description |
|---|---|---|
title | string | null | User-chosen display name set at upload time. Null if not provided. Used as the KB document title with a date prefix (e.g. "18 February 2026 - Lee & Will"). Falls back to filename (minus extension) when null. |
provider | string | null | "openai" or "whisperx". Null for legacy transcripts. |
speakerCount | number | null | Number of unique speakers detected. Only populated by WhisperX. |
segments | array | null | Array of { start, end, text, speaker } objects with timestamps in seconds. Only populated by WhisperX. |
Get / Delete Transcript
GET /api/ai/transcripts/:id // Returns full transcript record with segments
DELETE /api/ai/transcripts/:id // Deletes the transcript
Custom System Prompt
Admins can configure a custom system prompt in Settings -> AI that is prepended to the AI assistant's built-in instructions. This allows customising the assistant's behaviour, tone, and domain knowledge for your workspace. Maximum 5,000 characters.
The custom prompt is applied to the Spotlight AI chat only (not to automation AI steps, which use their own per-step prompt configuration in the workflow builder).
Tool Result Entity Cards
When the AI assistant calls read-only tools that return structured entity data, the results are rendered as clickable inline entity cards below the tool call indicator. Each card shows the entity icon, title, and metadata chips (status, priority, assignee, board name, due date, etc.).
Mapped Tools
The following 12 read-only tools produce entity cards. Write/action tools (e.g. create_matter, approve_submission) display only the tool badge.
| Tool | Entity Type | Card Metadata |
|---|---|---|
list_matters / get_matter | Matter | Status, priority (with colour dot), board name, assignee, due date (red if overdue) |
list_forms / get_form | Form | Status, submission count |
list_submissions / get_submission | Submission | Status, form title, submitter name |
list_tables | Table | Category (System/Form/User), row count, column count |
list_documents / get_document | Document | Title |
list_workflows | Workflow | Status |
search | Search Result | Entity type badge, status from metadata |
Card Behaviour
Cards are clickable - clicking navigates to the entity detail page and closes the Spotlight dialog. List results show a maximum of 5 cards with a "Show N more" toggle to expand. Cards render in both the inline Spotlight AI chat (compact mode) and the standalone AI chat panel.
Chat History
Conversations are persisted to the database with per-user isolation. Each chat session stores its messages, provider, and page context. Sessions are auto-created when sending the first message. The last 25 messages are sent per request (compaction).
// List chat sessions
GET /api/ai/chats
// Response
{
"chats": [
{
"id": "cm...",
"title": "Summarise ACME matter",
"createdAt": "2026-02-19T10:00:00.000Z",
"updatedAt": "2026-02-19T10:05:00.000Z",
"messageCount": 8
}
]
}
// Get messages for a session
GET /api/ai/chats/:id/messages
// Delete a session
DELETE /api/ai/chats/:id
Skills
Per-user library of reusable skills - instructions, playbooks, and saved prompts (max 50). Skills are stored as Knowledge Base documents in a private "Skills" system folder. They are Document records with plain text content and are also discoverable by the AI via the list_skills and get_skill tools.
// List skills
GET /api/ai/skills
// Response
[
{
"id": "cm...",
"title": "Summarise matter progress",
"content": "Give me a summary of the current matter...",
"createdAt": "2026-02-18T09:00:00.000Z"
}
]
// Create a skill
POST /api/ai/skills
{ "title": "Weekly review", "content": "Summarise all active matters..." }
// Delete a skill
DELETE /api/ai/skills/:id
// Record usage
POST /api/ai/skills/:id
{ "action": "use" }
AI Agent (Autonomous Worker)
Server-side autonomous task processor that executes assigned tasks with configurable autonomy levels and tool access controls.
| Control | Description |
|---|---|
| Autonomy levels | Configurable autonomy levels control which tools the agent can access. Default is read-only. |
| API key management | API key creation requires ADMIN/OWNER role with validated autonomy level |
| Injection scanning | Tool results are scanned for injection attempts and sanitised automatically |
| Task depth limit | Prevents runaway self-spawning task chains |
| Task timeout | Each task has a maximum execution time |
| Concurrency cap | Limited concurrent task execution |
| Rate limiting | Per-tool and per-key rate limits protect against overuse |
| Cost tracking | Per-task cost tracking with budget checks before execution |
AI Cost Control
Every LLM call records cost to the AiCostLedger with atomic budget snapshot upserts. Per-workspace monthly budgets with configurable hard cap and soft limit.
| Feature | Description |
|---|---|
| Atomic recording | $transaction() creates ledger entry + upserts monthly snapshot in one operation |
| Budget gate | checkBudget() pre-call check with hard cap and configurable soft limit percentage |
| Fail-open | Cost recording and budget checks never block LLM calls on error - availability over consistency |
| Reconciliation | reconcileBudgetSnapshot() detects drift between snapshot totals and ledger aggregates, auto-repairs |
| Key source tracking | Distinguishes SERVER_KEY, WORKSPACE_KEY, USER_KEY - user keys bypass budget by default |
| Daily usage | getWorkspaceDailyUsage() for admin dashboard charts, getTopUsersBySpend() for user breakdown |
Transcript Titling
When uploading audio for transcription, an optional title field can be provided via the upload form. The title is stored on the Transcript model and used when syncing to the Knowledge Base (format: "DD Month YYYY - Title"). Transcripts are auto-labeled with the transcript label in the KB. Searchable by title via GET /api/ai/transcripts?search=....
POST /api/ai/transcribe
Content-Type: multipart/form-data
file: <audio file>
title: "Client Meeting - ACME Ltd" // optional
provider: "openai" // or "whisperx"
// Response includes transcript ID and KB document ID
{
"id": "cm...",
"text": "...",
"title": "Client Meeting - ACME Ltd",
"documentId": "cm..." // KB page created
}