Matters API
Manage matters (cases/tickets), their lifecycle steps, comments, and documents. Matters are created from boards and progress through defined pipelines. Three board types are supported: PROCESS (linear stepped workflow with phase enforcement), PIPELINE (sales funnel with free movement and win/loss terminal stages), and KANBAN (task board with free movement and done-state columns).
Sidebar UX note: matter Comments and AI composers are bottom-anchored and align with the document sidebar interaction pattern. This is a UI-layer update only; API endpoints remain unchanged.
Board Types
| Type | Movement | Steps/Stages | Completion |
|---|---|---|---|
PROCESS | Linear (phase-enforced) | Typed steps (FORM, APPROVAL, TASK...) | Complete each step -> auto-advance |
PIPELINE | Free (any direction via goto) | Named stages with optional closedState: 'won' | 'lost' | Move to terminal stage -> auto-complete/cancel |
KANBAN | Free (any direction via goto) | Status columns with optional doneState: true | Move to done column -> auto-complete |
- PROCESS uses
advanceandcompletefor linear progression. Steps must be completed in order within each phase. - PIPELINE/KANBAN use
gotofor all movement. Stages are materialized as MANUAL steps. Terminal stages auto-update matter status. - Set
boardTypewhen creating a board viaPOST /api/matter-templates. Defaults toPROCESS.
Route Conventions
- All routes require authentication and workspace authorization.
- Mutation routes validate request payloads and return standardized errors.
- Error responses follow
{ success, error, code, details? }.
Endpoints
| Method | Endpoint | Description |
|---|---|---|
GET | /api/matters | List all matters |
POST | /api/matters | Create a matter from board |
GET | /api/matters/labels | List all labels used across matters in the org |
GET | /api/matters/:id | Get matter details |
PATCH | /api/matters/:id | Update matter (title, status, assignee, due date, priority, labels) |
DELETE | /api/matters/:id | Cancel a matter |
POST | /api/matters/:id/advance | Advance to next step |
POST | /api/matters/:id/goto | Jump to a specific step |
POST | /api/matters/:id/steps/:stepId/complete | Complete a specific step |
POST | /api/matters/:id/steps/:stepId/reject | Reject or send back a step |
POST | /api/matters/:id/steps/:stepId/skip | Skip a step |
POST | /api/matters/:id/steps/:stepId/reopen | Reopen a completed step |
POST | /api/matters/:id/steps/:stepId/assign | Assign a step to a user |
GET | /api/matters/:id/data | Get aggregated form data from all steps |
GET | /api/matters/:id/metadata | Get board property schema and values |
PATCH | /api/matters/:id/metadata | Update board property values |
GET | /api/matters/:id/line-items | List line items with pricing |
POST | /api/matters/:id/line-items | Create a line item |
PATCH | /api/matters/:id/line-items | Bulk update / reorder line items |
DELETE | /api/matters/:id/line-items | Bulk delete line items |
POST | /api/matters/:id/steps/:stepId/init-form | Initialize form for a FORM step |
POST | /api/matters/:id/steps/:stepId/send-form | Send form to a recipient email |
POST | /api/matters/:id/steps/:stepId/clear-form | Clear form selection for a FORM step |
GET | /api/matters/:id/steps/:stepId/form-status | Get FORM step status (selection, sent, submitted) |
GET | /api/matters/:id/steps/:stepId/automation-context | Get step automation context |
GET | /api/matters/:id/comments | List matter comments |
POST | /api/matters/:id/comments | Add a comment |
GET | /api/matters/:id/documents | List matter documents |
POST | /api/matters/:id/documents | Attach a document |
DELETE | /api/matters/:id/documents/:docId | Remove a document |
POST | /api/matters/:id/documents/download | Download all documents as ZIP archive |
POST | /api/matters/:id/files/filing-folder | Create a filing event folder for a matter |
PATCH | /api/matters/:id/steps/:stepId/due-date | Set or clear a step due date |
GET | /api/matters/:id/steps/:stepId/subtasks | List subtasks for a step |
POST | /api/matters/:id/steps/:stepId/subtasks | Create a subtask on a step |
PATCH | /api/matters/:id/steps/:stepId/subtasks/:subtaskId | Update a subtask |
DELETE | /api/matters/:id/steps/:stepId/subtasks/:subtaskId | Delete a subtask |
POST | /api/matters/:id/steps/:stepId/subtasks/:subtaskId/toggle | Toggle subtask completion |
GET | /api/matters/:id/activity | Get matter activity timeline |
GET | /api/matters/:id/follow | Get follow status |
POST | /api/matters/:id/follow | Follow a matter |
DELETE | /api/matters/:id/follow | Unfollow a matter |
POST | /api/matters/:id/duplicate | Duplicate a matter |
GET | /api/matters/:id/relations | List related matters |
POST | /api/matters/:id/relations | Link a related matter |
DELETE | /api/matters/:id/relations/:relationId | Remove a matter relation |
GET | /api/matters/:id/compliance-exceptions | List compliance exceptions with summary |
POST | /api/matters/:id/compliance-exceptions | Create a compliance exception |
GET | /api/matters/:id/compliance-exceptions/:exceptionId | Get a single compliance exception |
PATCH | /api/matters/:id/compliance-exceptions/:exceptionId | Update a compliance exception |
DELETE | /api/matters/:id/compliance-exceptions/:exceptionId | Delete a compliance exception |
GET | /api/matters/:id/envelopes | List signing envelopes for a matter |
POST | /api/matters/:id/envelopes | Create a signing envelope |
GET | /api/matters/:id/envelopes/:envelopeId | Get envelope details |
PATCH | /api/matters/:id/envelopes/:envelopeId | Update envelope status or recipient signing status |
DELETE | /api/matters/:id/envelopes/:envelopeId | Delete an envelope |
POST | /api/matters/:id/envelopes/:envelopeId/signing | Trigger signing action (send, remind, void, status-check) |
GET | /api/matters/:id/regulator-comments | List regulator return comments |
POST | /api/matters/:id/regulator-comments | Add a regulator return comment |
PATCH | /api/matters/:id/regulator-comments/:commentId | Update a regulator comment (status, resolution) |
DELETE | /api/matters/:id/regulator-comments/:commentId | Delete a regulator comment |
POST | /api/matters/:id/portal | Create a client portal link for a matter |
POST | /api/matters/bulk-assign | Bulk assign steps to a user |
GET | /api/matters/:id/views | Get view analytics (total, unique, leaderboard, 30-day chart) |
POST | /api/matters/:id/views | Record a page view (throttled) |
GET | /api/cron/trigger-schedule | Evaluate schedule-type MatterTriggers and auto-create matters (cron) |
Matter Boards
| Method | Endpoint | Description |
|---|---|---|
GET | /api/matter-templates | List boards |
POST | /api/matter-templates | Create a board |
GET | /api/matter-templates/:id | Get board details |
PATCH | /api/matter-templates/:id | Update a board (including folderTemplate) |
DELETE | /api/matter-templates/:id | Archive or permanently delete a board |
GET | /api/matter-templates/options | Get forms, users, and emails for board configuration |
GET | /api/matters/templates/folder-presets | List available folder template presets |
GET | /api/matter-templates/:id/versions | List board version history |
GET | /api/matter-templates/:id/versions/:versionId | Get a specific board version with full steps |
POST | /api/matter-templates/:id/versions/:versionId/rollback | Rollback board to a previous version |
Board Property Fields
Org-wide reusable field definitions for board properties. All CRUD is handled by the collection route with field ID in the request body.
| Method | Endpoint | Description |
|---|---|---|
GET | /api/matter-metadata-fields | List all board property definitions |
POST | /api/matter-metadata-fields | Create a board property field |
PATCH | /api/matter-metadata-fields | Update a board property field (id in body) |
DELETE | /api/matter-metadata-fields | Delete a board property field (id in body) |
List Matters
Returns a paginated list of matters for your workspace with filtering and sorting. When includeSubordinates=true is passed and the user is an overseer, matters from subordinate workspaces are included with a workspace field identifying the source.
GET /api/matters?status=OPEN&sortBy=updatedAt&sortOrder=desc&page=1&limit=20
Query Parameters
| Parameter | Type | Description |
|---|---|---|
status | string | Filter by status: OPEN, ON_HOLD, COMPLETED, CANCELLED |
templateId | string | Filter by board (template ID) |
assignedToId | string | Filter by assignee user ID. Use "unassigned" to filter unassigned matters. |
search | string | Search by matter title, board name, or number (e.g. "ONB-0001") |
updatedFrom / updatedTo | string | Filter by updated date range (ISO date strings) |
sortBy | string | Sort field: createdAt, updatedAt, number, title, status (default: updatedAt) |
sortOrder | string | Sort direction: asc or desc (default: desc) |
includeAllSteps | boolean | When true, returns all steps per matter (with stepIndex, phaseIndex, phaseName) instead of only the active step. Used by the kanban board view. |
phase | string | Filter by phase name (exact match on active step's phaseName) |
stepType | string | Filter by active step type: FORM, APPROVAL, TASK, DOCUMENT, WORKFLOW, MANUAL, EMAIL |
includeSubordinates | boolean | When true, includes matters from subordinate workspaces (oversight). Each matter includes a workspace field with the source workspace id and name. Ignored if the user has no subordinate workspaces. |
page / limit | int | Pagination (default: page 1, limit 20) |
Response
{
"matters": [
{
"id": "cm...",
"number": 1,
"numberPrefix": "ONB",
"title": "Client Onboarding - Acme Corp",
"status": "OPEN",
"currentStepIndex": 2,
"template": { "id": "cm...", "name": "Client Onboarding" },
"workspace": { "id": "cm...", "name": "Dubai Branch" },
"assignedTo": { "id": "cm...", "name": "Jane Doe", "email": "jane@example.com" },
"createdBy": { "id": "cm...", "name": "Admin" },
"steps": [
{ "id": "cm...", "stepIndex": 0, "name": "KYC Review", "type": "APPROVAL", "status": "ACTIVE" }
],
"_count": { "steps": 5, "comments": 3, "documents": 2 },
"createdAt": "2026-01-15T10:00:00.000Z",
"updatedAt": "2026-01-16T14:00:00.000Z"
}
],
"filters": {
"templates": [
{ "id": "cm...", "name": "Client Onboarding", "status": "ACTIVE", "matterCount": 15 }
],
"assignees": [
{ "id": "cm...", "name": "Jane Doe", "email": "jane@example.com" }
],
"phases": ["Intake", "Verification", "Completion"]
},
"pagination": { "page": 1, "limit": 20, "total": 42, "pages": 3 },
"isOverseer": true
}
Get Matter Details
Returns full matter details including all steps with phase information, assignee, and completion data.
GET /api/matters/:id
Response
{
"id": "cm...",
"number": 1,
"title": "Client Onboarding - Acme Corp",
"status": "OPEN",
"currentStepIndex": 2,
"template": { "id": "cm...", "name": "Client Onboarding" },
"assignedTo": { "id": "cm...", "name": "Jane Doe", "email": "jane@example.com" },
"createdBy": { "id": "cm...", "name": "Admin" },
"dueDate": "2026-03-01T00:00:00.000Z",
"priority": "HIGH",
"tags": ["onboarding", "enterprise"],
"submission": null,
"metadata": { "priority": "high" },
"steps": [
{
"id": "cm...",
"stepIndex": 0,
"name": "KYC Review",
"type": "APPROVAL",
"status": "COMPLETED",
"phaseIndex": 0,
"phaseName": "Intake",
"config": { "approvalRoles": ["ADMIN", "OWNER"] },
"assignedTo": { "id": "cm...", "name": "Jane Doe", "email": "jane@example.com" },
"completedAt": "2026-01-16T14:00:00.000Z",
"completedBy": { "id": "cm...", "name": "Jane Doe" },
"data": { "approved": true }
},
{
"id": "cm...",
"stepIndex": 1,
"name": "Identity Confirmation",
"type": "MANUAL",
"status": "ACTIVE",
"phaseIndex": 1,
"phaseName": "Verification",
"config": {
"checklist": ["Verify government ID", "Confirm address"],
"requireNote": true
},
"assignedTo": null,
"completedAt": null,
"completedBy": null,
"data": null
}
],
"createdAt": "2026-01-15T10:00:00.000Z",
"updatedAt": "2026-01-16T14:00:00.000Z"
}
Create a Matter
Creates a new matter from a board. The board's phase-grouped steps are materialized into individual step records with phaseIndex and phaseName fields. You can optionally provide custom steps to override the board defaults. The first step is automatically set to ACTIVE and the matter assignee follows the first step's assignee. Steps with slaOffsetDays configured in the board automatically receive computed due dates at creation time (first step: creation date + offset; subsequent: previous step's due date + offset).
POST /api/matters
Content-Type: application/json
{
"templateId": "cm...",
"title": "Client Onboarding - Acme Corp",
"assignedToId": "cm...",
"dueDate": "2026-03-15T00:00:00.000Z",
"submissionId": "cm...",
"metadata": { "priority": "high" },
"steps": [
{ "id": "step-1", "name": "KYC Review", "type": "APPROVAL", "required": true },
{ "id": "step-2", "name": "Account Setup", "type": "TASK", "required": true }
]
}
Body Parameters
| Field | Type | Required | Description |
|---|---|---|---|
templateId | string | Yes | ID of the board to use |
title | string | Yes | Matter title |
steps | array | No | Custom steps to override the board. Each step needs id, name, type, and required. |
submissionId | string | No | Link to a form submission. The linked submission's GET /api/submissions response will include this matter in its matters array. |
assignedToId | string | No | User ID to assign the matter to. If omitted, auto-follows the first step's assignee. |
dueDate | string (ISO 8601) | No | Due date for the matter. Pass an ISO date string or null to leave unset. |
metadata | object | No | Custom key-value metadata |
Advance to Next Step
Completes the current active step and automatically activates the next pending step. Phase-aware: if the current phase is complete, the first step of the next phase is activated. The matter's assignee auto-follows the newly activated step's assignee.
POST /api/matters/:id/advance
Content-Type: application/json
{
"data": { "notes": "Review passed" }
}
Response
{
"matter": { "id": "cm...", "status": "OPEN", "currentStepIndex": 2, "steps": [] },
"completedStep": { "id": "cm...", "name": "KYC Review", "status": "COMPLETED" },
"advancedToStep": { "id": "cm...", "name": "Document Collection", "status": "ACTIVE" }
}
Jump to a Specific Step
Moves the matter to a specific step by index or step ID. When jumping forward, intermediate steps are marked as SKIPPED. When jumping backward, intermediate steps are reset to PENDING. For PIPELINE boards, moving to a stage with closedState: 'won' auto-completes the matter; closedState: 'lost' auto-cancels it. For KANBAN boards, moving to a column with doneState: true auto-completes the matter. This is the primary movement endpoint for PIPELINE and KANBAN boards.
POST /api/matters/:id/goto
Content-Type: application/json
{ "stepIndex": 3 }
Or jump by step ID:
{ "stepId": "cm..." }
Body Parameters
| Field | Type | Description |
|---|---|---|
stepIndex | number | Target step index (0-based). Provide this or stepId. |
stepId | string | Target step ID. Provide this or stepIndex. |
Response
{
"matter": { "id": "cm...", "currentStepIndex": 3, "steps": [] },
"activatedStep": { "id": "cm...", "name": "Account Setup", "status": "ACTIVE" },
"direction": "forward"
}
Step Lifecycle
Steps progress through a lifecycle: PENDING -> ACTIVE -> COMPLETED (or SKIPPED / BLOCKED). Only the active step can be completed, rejected, or skipped. Step progression is phase-aware - a phase must be complete before the next phase's steps activate.
Assignee Auto-Follow
When a step is completed, skipped, rejected, or reopened, the matter's assignedToId is automatically updated to match the newly activated step's assignee. If the next step has an assigneeConfig (auto-assignment rule), it is resolved at activation time. Followers are notified of step completions, rejections, and new assignments. FORM-type steps automatically initialize their form lifecycle when activated.
Complete a Step
POST /api/matters/:id/steps/:stepId/complete
Content-Type: application/json
{
"data": { "approved": true, "notes": "All documents verified" }
}
Marks the step as COMPLETED and automatically activates the next pending step (phase-aware). The optional data field stores step output. For MANUAL steps, include checklist and note data:
{
"data": {
"checklist": [
{ "item": "Verify government ID", "checked": true },
{ "item": "Confirm address", "checked": true }
],
"note": "All items confirmed in person"
}
}
For DOCUMENT steps with autoGenerate: true in their step config, completing the step triggers the Document Engine to generate documents using the configured templatePack. For WORKFLOW steps, the automation is executed first, then the step auto-completes with the run reference. For EMAIL steps, the user composes the email in the UI and can attach files from the matter's documents:
{ "data": {} }
{ "data": { "workflowRunId": "cm..." } }
{
"data": {
"emailRecipient": "client@example.com",
"emailSubject": "Your documents are ready",
"emailBody": "Please find the attached documents...",
"attachmentIds": ["cm...", "cm..."]
}
}
Reject a Step
POST /api/matters/:id/steps/:stepId/reject
Content-Type: application/json
{
"reason": "Missing KYC documentation",
"sendBackToIndex": 1
}
Marks the step as BLOCKED. If sendBackToIndex is provided, the target step is reactivated for rework. Otherwise, the next pending step is activated (phase-aware) with auto-assignment resolution.
Skip a Step
POST /api/matters/:id/steps/:stepId/skip
Marks the step as SKIPPED and advances to the next pending step (phase-aware). The matter's assignee auto-follows the next step.
Reopen a Step
POST /api/matters/:id/steps/:stepId/reopen
Reopens a previously completed or skipped step. This resets the step to ACTIVE and marks all later steps as PENDING, effectively rolling back progress to this step. The matter's assignee auto-follows the reopened step's assignee.
Assign a Step
POST /api/matters/:id/steps/:stepId/assign
Content-Type: application/json
{
"assignedToId": "cm..."
}
Form Data Capture
Matters accumulate structured data as FORM steps are completed. Each completed FORM step stores a denormalized snapshot of the submitted form data in its data field. This means the captured data is self-contained and doesn't require re-fetching the original form schema.
FORM Step Completion
When completing a FORM step, pass the denormalized form data and optionally a submissionId to link to the underlying form submission:
POST /api/matters/:id/steps/:stepId/complete
Content-Type: application/json
{
"submissionId": "cm...",
"data": {
"_type": "form_submission",
"formId": "cm...",
"formTitle": "Client Intake Form",
"submittedAt": "2026-02-12T10:00:00.000Z",
"fields": [
{
"key": "full_name",
"label": "Full Name",
"type": "text",
"value": "John Smith",
"sectionTitle": "Personal Details"
},
{
"key": "email",
"label": "Email",
"type": "email",
"value": "john@example.com",
"sectionTitle": "Personal Details"
},
{
"key": "company_name",
"label": "Company Name",
"type": "text",
"value": "Acme Corp",
"sectionTitle": "Business Details"
}
]
}
}
Step Form Data Shape
| Field | Type | Description |
|---|---|---|
_type | string | Always "form_submission" |
formId | string | ID of the form that was submitted |
formTitle | string | Title of the form at submission time |
submittedAt | string | ISO 8601 timestamp |
fields | array | Array of StepFormField objects (see below) |
Step Form Field Shape
| Field | Type | Description |
|---|---|---|
key | string | Field key from the form schema |
label | string | Human-readable field label |
type | string | Field type (text, email, select, checkbox, date, currency, etc.) |
value | any | The submitted value (string, number, boolean, array, or null) |
sectionTitle | string? | Which form section this field belongs to (for grouped display) |
Get Aggregated Matter Data
Returns all form data accumulated across FORM steps in a matter. Useful for integrations, client portals, and the matter Data tab.
GET /api/matters/:id/data
Response
{
"matterId": "cm...",
"matterTitle": "Client Onboarding - Acme Corp",
"matterNumber": 1,
"totalFields": 8,
"completedFields": 5,
"steps": [
{
"stepId": "cm...",
"stepName": "Client Intake Form",
"stepIndex": 0,
"formId": "cm...",
"formTitle": "Client Intake Form",
"status": "COMPLETED",
"submittedAt": "2026-02-12T10:00:00.000Z",
"phaseName": "Intake",
"fields": [
{
"key": "full_name",
"label": "Full Name",
"type": "text",
"value": "John Smith",
"sectionTitle": "Personal Details"
}
]
},
{
"stepId": "cm...",
"stepName": "Financial Details",
"stepIndex": 3,
"formId": "cm...",
"formTitle": null,
"status": "PENDING",
"submittedAt": null,
"phaseName": "Verification",
"fields": []
}
]
}
Phases
Boards organize steps into named phases. Phases are sequential - all required steps in a phase must be completed or skipped before the next phase's steps activate. Each materialized step includes phaseIndex (0-based) and phaseName fields that are captured at creation time.
Step Phase Fields
| Field | Type | Description |
|---|---|---|
phaseIndex | number | null | 0-based index of the phase this step belongs to. Null for legacy matters without phases. |
phaseName | string | null | Name of the phase (e.g. "Intake", "Verification"). Captured at creation time; board changes don't affect existing matters. |
Comments
Add notes and comments to matters, optionally scoped to a specific step. Use mentionedUserIds to @mention users - mentioned users receive a notification regardless of their preference settings.
POST /api/matters/:id/comments
Content-Type: application/json
{
"content": "Client confirmed bank details - @Jane please review",
"stepId": "cm...",
"type": "COMMENT",
"mentionedUserIds": ["cm_user_id_1", "cm_user_id_2"]
}
| Field | Type | Description |
|---|---|---|
content | string | Comment text (required) |
stepId | string | Optional step to scope the comment to |
type | string | COMMENT (default), NOTE, or SYSTEM |
mentionedUserIds | string[] | User IDs to @mention - they receive a direct notification |
Matter Documents
Attach files to a matter, optionally linked to a specific step.
POST /api/matters/:id/documents
Content-Type: application/json
{
"name": "KYC-verification.pdf",
"fileUrl": "/uploads/abc123.pdf",
"fileSize": 245760,
"mimeType": "application/pdf",
"stepId": "cm...",
"category": "kyc"
}
Create a Board
Boards define the phase-grouped steps for matters. Each board can be linked to a form for auto-creation on submission. Steps support the v2 phase schema or a legacy flat array (automatically wrapped in a single "Default" phase).
v2 Phase Schema (recommended)
POST /api/matter-templates
Content-Type: application/json
{
"name": "Client Onboarding",
"description": "Standard onboarding for new clients",
"formId": "cm...",
"steps": {
"version": 2,
"phases": [
{
"id": "phase-1",
"name": "Intake",
"steps": [
{ "id": "step-1", "name": "KYC Review", "type": "APPROVAL", "required": true, "assigneeUserId": "cm...", "slaOffsetDays": 2 },
{ "id": "step-2", "name": "Document Generation", "type": "DOCUMENT", "required": true, "templatePack": "onboarding/kyc", "autoGenerate": true, "assigneeConfig": { "mode": "least_assigned" }, "slaOffsetDays": 3 }
]
},
{
"id": "phase-2",
"name": "Verification",
"steps": [
{ "id": "step-3", "name": "Identity Confirmation", "type": "MANUAL", "required": true, "checklist": ["Verify ID", "Confirm address"], "requireNote": true, "slaOffsetDays": 5 },
{ "id": "step-4", "name": "Account Setup", "type": "TASK", "required": true, "taskDescription": "Create CRM account" }
]
},
{
"id": "phase-3",
"name": "Completion",
"steps": [
{ "id": "step-5", "name": "Welcome Email", "type": "WORKFLOW", "required": false }
]
}
]
}
}
Legacy Flat Array (still supported)
{
"name": "Simple Workflow",
"steps": [
{ "id": "step-1", "name": "KYC Review", "type": "APPROVAL", "required": true },
{ "id": "step-2", "name": "Account Setup", "type": "TASK", "required": true }
]
}
Flat arrays are automatically wrapped in a single "Default" phase for backward compatibility.
Auto-managed Matter Table
When a board is created, a dedicated MATTER category table is automatically created with 17 default columns (Matter ID, Title, Status, Priority, Assignee, Due Date, Labels, Board, Current Step, Created By, Created, Updated, Step Progress, Next Step, Current Phase, Phase Progress, Last Reviewed, Source Submission). If the board has metadata properties, corresponding columns are added automatically. Step/phase columns derive values from the matter's steps (e.g. Step Progress shows "3 of 7", Next Step shows the next pending step name). Matter data is synced to this table as rows whenever matters are created, updated, or steps are completed/skipped. When the board's metadata schema is updated (via PATCH), the table columns are synced to match. The table ID is stored in the board's dataConfig.matterTableId field.
Step Types
| Type | Description | Config Fields |
|---|---|---|
FORM | Collect additional data via a sub-form | formId |
APPROVAL | Requires explicit approval to proceed | approvalRoles |
TASK | General task or action item | taskDescription |
DOCUMENT | Document collection and generation. Optionally triggers the Document Engine integration on completion when autoGenerate is enabled. | documentTypes, templatePack, autoGenerate |
WORKFLOW | Triggers an automated workflow run, then auto-completes | workflowId |
MANUAL | Confirm a real-world action was completed | checklist, requireNote |
EMAIL | Compose and send an email with optional file attachments from the matter | emailRecipient, emailSubject, emailBody |
All step types also support these common fields: required (boolean), assigneeUserId (string), assigneeConfig (object with mode), conditionalVisibility (object), and slaOffsetDays (number - days after previous step's due date, or matter creation for the first step).
DOCUMENT Step Config
DOCUMENT steps support file collection and optional auto-generation via the Document Engine integration (configured in Settings > Integrations).
| Field | Type | Description |
|---|---|---|
templatePack | string | Document Engine template pack path (e.g. incorporation/spv). The pack must belong to the current workspace. Required when autoGenerate is true. |
autoGenerate | boolean | When true, completing this step automatically triggers the Document Engine to generate documents from the template pack using accumulated matter data. The referenced template pack must belong to the current workspace. |
documentTypes | string[] | Accepted file types for manual uploads (e.g. ["PDF", "DOCX"]). Empty or omitted means all file types are accepted. |
Common Step Fields
All step types support these optional fields when defining board steps:
| Field | Type | Description |
|---|---|---|
assigneeUserId | string | User ID to auto-assign when this step activates |
notifyEmail | string | Email address to notify when this step activates |
description | string | Step description |
required | boolean | Whether this step must be completed (default: true). Non-required steps can be skipped without blocking phase progression. |
assigneeConfig | object | Auto-assignment rule resolved when this step activates. mode: random, least_assigned, most_assigned, or specific. When mode is specific, include specificUserIds array. Takes precedence over assigneeUserId. |
Set Step Due Date
Set or clear a due date on a specific step. Pass null to clear an existing due date. If subsequent steps have slaOffsetDays configured, their due dates are automatically cascaded forward based on the new date.
PATCH /api/matters/:id/steps/:stepId/due-date
Content-Type: application/json
{
"dueDate": "2026-03-15T00:00:00.000Z"
}
To clear:
{
"dueDate": null
}
Body Parameters
| Field | Type | Description |
|---|---|---|
dueDate | string | null | ISO 8601 date string, or null to clear |
SLA Cascade
When you set a due date on a step, the API walks forward through subsequent steps. Any step with slaOffsetDays in its config has its due date automatically recomputed as previousStepDueDate + slaOffsetDays. Steps without SLA config act as manual anchors and break the cascade chain. The response includes a cascadedSteps array listing any steps whose due dates were updated.
Response
{
"id": "cm...",
"name": "KYC Review",
"status": "ACTIVE",
"dueDate": "2026-03-15T00:00:00.000Z",
"cascadedSteps": [
{ "id": "cm...", "name": "Document Generation", "dueDate": "2026-03-18T00:00:00.000Z" },
{ "id": "cm...", "name": "Identity Confirmation", "dueDate": "2026-03-23T00:00:00.000Z" }
]
}
Step Subtasks
Create subtasks within a step to break down work into smaller items. Subtasks have their own status (PENDING / COMPLETED), optional assignee, and optional due date.
Create a Subtask
POST /api/matters/:id/steps/:stepId/subtasks
Content-Type: application/json
{
"title": "Verify government ID",
"assignedToId": "cm...",
"dueDate": "2026-03-10T00:00:00.000Z"
}
Body Parameters
| Field | Type | Required | Description |
|---|---|---|---|
title | string | Yes | Subtask title |
assignedToId | string | No | User ID to assign the subtask to. Must be an org member. |
dueDate | string | No | ISO 8601 date string for subtask deadline |
Response (201 Created)
{
"id": "cm...",
"stepId": "cm...",
"title": "Verify government ID",
"status": "PENDING",
"assignedTo": { "id": "cm...", "name": "Jane Doe", "email": "jane@example.com" },
"dueDate": "2026-03-10T00:00:00.000Z",
"completedAt": null,
"sortOrder": 0,
"createdAt": "2026-02-13T10:00:00.000Z",
"updatedAt": "2026-02-13T10:00:00.000Z"
}
Update a Subtask
PATCH /api/matters/:id/steps/:stepId/subtasks/:subtaskId
Content-Type: application/json
{
"title": "Updated title",
"assignedToId": "cm...",
"dueDate": "2026-03-15T00:00:00.000Z",
"sortOrder": 2
}
All fields are optional. Set assignedToId to null to unassign, or dueDate to null to clear.
Delete a Subtask
DELETE /api/matters/:id/steps/:stepId/subtasks/:subtaskId
Toggle Subtask Completion
POST /api/matters/:id/steps/:stepId/subtasks/:subtaskId/toggle
Toggles the subtask between PENDING and COMPLETED. When completing, records completedAt and completedById. When reopening, clears those fields. Notifies the step assignee on completion. Also auto-completes any branch subtasks linked to this subtask.
Subtask Status
Subtasks have two statuses: PENDING and COMPLETED. Subtasks are independent of the parent step's lifecycle - completing all subtasks does not auto-complete the step.
Branch Subtasks
Subtasks can be branched (review copies) via POST /api/tasks/bulk. A branch subtask has branchSourceId and branchSourceType (STEP or SUBTASK) linking it to the original. When the source step or subtask is completed, all linked branch subtasks are automatically completed. See the Tasks API docs for details.
Activity Timeline (Audit Log)
Returns a comprehensive audit trail for a matter, suitable for regulatory compliance. Merges three data sources into a unified feed: step transitions (system comments), user comments, document uploads, and audit log events covering all matter mutations. Events are sorted newest first. Every change to a matter - including step lifecycle, metadata updates, follower changes, line items, subtasks, relations, and field-level before/after values - is captured.
GET /api/matters/:id/activity
Data Sources
- Comments - user comments and system-generated step transition notes
- Audit log - all mutations across matter resources (steps, comments, documents, portal links)
- Documents - file upload events with category and step context
Response
[
{
"id": "audit-cm...",
"type": "audit",
"trigger": "update",
"description": "Priority changed: MEDIUM -> HIGH",
"actor": { "name": "Admin", "email": "admin@example.com" },
"createdAt": "2026-02-13T15:00:00.000Z",
"resource": "MATTER",
"metadata": {
"changes": ["priority"],
"fieldChanges": {
"priority": { "from": "MEDIUM", "to": "HIGH" }
}
}
},
{
"id": "audit-cm...",
"type": "audit",
"trigger": "complete",
"description": "Step \"KYC Review\" completed",
"actor": { "name": "Admin", "email": "admin@example.com" },
"createdAt": "2026-02-13T14:00:00.000Z",
"resource": "MATTER_STEP",
"stepName": "KYC Review"
},
{
"id": "comment-cm...",
"type": "comment",
"trigger": "comment",
"description": "Client confirmed bank details",
"actor": { "name": "Jane Doe", "email": "jane@example.com" },
"createdAt": "2026-02-13T10:30:00.000Z",
"commentType": "COMMENT"
},
{
"id": "doc-cm...",
"type": "document",
"trigger": "document_upload",
"description": "Document uploaded: KYC-verification.pdf",
"actor": { "name": "Admin", "email": "admin@example.com" },
"createdAt": "2026-02-13T09:00:00.000Z",
"stepName": "KYC Review"
},
{
"id": "audit-cm...",
"type": "audit",
"trigger": "create",
"description": "Matter created",
"actor": { "name": "Admin", "email": "admin@example.com" },
"createdAt": "2026-02-12T10:00:00.000Z",
"resource": "MATTER"
}
]
Tracked Event Types
The following actions are captured with full audit metadata. Field-level changes include before/after values where applicable.
| Category | Events | Tracked Fields |
|---|---|---|
| Matter lifecycle | Create, update, cancel, duplicate | Title, status, priority, assignee, due date, labels (before/after) |
| Step lifecycle | Complete, reject, skip, reopen, advance, jump (goto) | Step name, rejection reason, completion notes |
| Step assignment | Assign user to step | Previous assignee, new assignee name |
| Step due dates | Set or remove due date | Previous due date, new due date |
| Subtasks | Create, update, delete, complete, reopen | Subtask title, changed fields |
| Form steps | Initialize form, send form to client | Recipient email, matter number |
| Metadata | Update metadata property values | Changed field names, per-field before/after values |
| Line items | Add, update, delete, reorder | Line item name |
| Relations | Add or remove related matters | Related matter title, relation type |
| Followers | Follow, unfollow | Actor name |
| Documents | Upload, remove | File name, category, step context |
| Portal links | Create client portal link | Client email |
| Description | Update rich text description | Change flag |
Event Object Shape
{
"id": "string",
"type": "audit | comment | document",
"trigger": "string",
"description": "string",
"actor": {
"name": "string | null",
"email": "string"
},
"createdAt": "string",
"resource": "string (optional)",
"stepName": "string (optional)",
"commentType": "string (optional)",
"metadata": {
"changes": ["string"],
"fieldChanges": {
"field": { "from": "any", "to": "any" }
}
}
}
id is prefixed: audit-, comment-, or doc-. type is one of audit, comment, document. trigger is the action verb (e.g. "create", "complete", "comment"). description is a human-readable summary with old/new values. commentType is COMMENT, SYSTEM, or NOTE.
Followers & Notifications
Users can follow matters to receive in-app notifications about lifecycle events. Followers are automatically added when a user is assigned to, comments on, or uploads a document to a matter. Users can also manually follow or unfollow via the API.
Get Follow Status
GET /api/matters/:id/follow
{
"isFollowing": true,
"followerCount": 3,
"followers": [
{ "id": "cm...", "name": "Jane Doe", "email": "jane@example.com" },
{ "id": "cm...", "name": "Admin", "email": "admin@example.com" }
]
}
Follow a Matter
POST /api/matters/:id/follow
Response (201 Created):
{
"isFollowing": true,
"followerCount": 4
}
Unfollow a Matter
DELETE /api/matters/:id/follow
{
"isFollowing": false,
"followerCount": 3
}
Notification Events
Followers receive in-app notifications for matter lifecycle events. The actor who triggered the event is never notified of their own action. Direct assignees and @mentioned users always receive notifications regardless of preference settings.
| Event | Type | Recipients | Default |
|---|---|---|---|
| Matter assigned | MATTER_ASSIGNED | Assignee | ON |
| Step assigned | MATTER_STEP_ASSIGNED | Step assignee | ON |
| Step completed | MATTER_STEP_COMPLETED | Followers | ON |
| Step rejected | MATTER_STEP_REJECTED | Followers + send-back assignee | ON |
| Status changed | MATTER_STATUS_CHANGED | Followers | ON |
| Comment added | MATTER_COMMENT | Followers + @mentioned users | OFF |
| Document uploaded | MATTER_DOCUMENT | Followers | OFF |
| Subtask assigned | MATTER_SUBTASK_ASSIGNED | Subtask assignee | OFF |
| Subtask completed | MATTER_SUBTASK_COMPLETED | Step assignee | OFF |
| Due date changed | MATTER_DUE_DATE | Step assignee | OFF |
| Step skipped | MATTER_STEP_SKIPPED | Followers | OFF |
| Step reopened | MATTER_STEP_REOPENED | Followers + step assignee | OFF |
| Step jumped | MATTER_STEP_JUMPED | Followers | OFF |
Auto-Follow Triggers
Users are automatically added as followers when they: create a matter, are assigned to a matter or step, comment on a matter, or upload a document. Users can configure which notification categories they receive in Settings > Notifications.
Board Version History
Every board save creates a new version with a computed changelog. You can view the history and rollback to any previous version. Rollback is non-destructive - it creates a new version (N+1) with the restored steps.
List Versions
GET /api/matter-templates/:id/versions
{
"versions": [
{
"id": "cm...",
"version": 2,
"name": "Client Onboarding",
"status": "ACTIVE",
"changelog": {
"summary": "Added phase \"Verification\", added 2 steps",
"entries": [
{ "type": "added", "target": "phase", "description": "Added phase \"Verification\"" },
{ "type": "added", "target": "step", "description": "Added step \"ID Check\"" }
]
},
"createdBy": { "name": "Admin" },
"createdAt": "2026-02-13T10:00:00.000Z"
}
]
}
Rollback
POST /api/matter-templates/:id/versions/:versionId/rollback
Creates version N+1 with the target version's steps.
{
"success": true,
"version": 3,
"message": "Rolled back to version 1"
}
Rollback requires ADMIN or OWNER role. Existing matters are not affected - they keep their materialized steps.
Archive or Delete a Board
Uses a two-phase delete pattern. The first DELETE call soft-archives the board (sets status to ARCHIVED). A second DELETE call on an already-archived board permanently deletes it and all its versions.
DELETE /api/matter-templates/:id
Phase 1: Active board -> Archived (200):
{
"message": "Template archived"
}
Phase 2: Already archived board -> Permanently deleted (200):
{
"message": "Template permanently deleted"
}
Archived boards are hidden from the active board list but remain accessible in the sidebar's Archived section. Existing matters created from the board are not affected by archiving or deletion.
Board Status Values
| Status | Description |
|---|---|
ACTIVE | Board is visible and can be used to create new matters |
DRAFT | Board is in draft state and not yet published |
ARCHIVED | Board is archived and hidden from the active list. Can be permanently deleted. |
List Labels
Returns all distinct labels used across matters in the organization, sorted by usage count. Useful for populating label pickers and autocomplete suggestions.
GET /api/matters/labels
Response (200 OK):
{
"labels": [
{ "name": "onboarding", "count": 12 },
{ "name": "enterprise", "count": 8 },
{ "name": "urgent", "count": 5 },
{ "name": "compliance", "count": 3 }
]
}
| Field | Type | Description |
|---|---|---|
labels[].name | string | The label text |
labels[].count | number | Number of matters using this label |
Update a Matter
Update matter properties including title, status, assignee, due date, priority, and labels. All fields are optional.
PATCH /api/matters/:id
Content-Type: application/json
{
"title": "Updated Title",
"status": "ON_HOLD",
"assignedToId": "cm...",
"dueDate": "2026-03-15T00:00:00.000Z",
"priority": "HIGH",
"tags": ["onboarding", "enterprise", "urgent"],
"metadata": { "notes": "Custom data" }
}
Body Parameters
| Field | Type | Description |
|---|---|---|
title | string | Matter title |
status | string | OPEN, ON_HOLD, COMPLETED, or CANCELLED |
assignedToId | string | null | User ID to assign, or null to unassign |
dueDate | string | null | ISO 8601 date, or null to clear |
priority | string | NONE, LOW, MEDIUM, HIGH, or URGENT |
tags | string[] | Array of label strings (max 20) |
metadata | object | Custom key-value metadata (merged with existing) |
View Analytics
Track page views per matter. Views are recorded automatically when a user opens a matter detail page. The GET endpoint returns analytics data including total views, unique viewer count, a leaderboard of top viewers, and daily view counts for the last 30 days (used for charts). The POST endpoint records a view, throttled to max 1 per user per 5 minutes per matter.
Get View Analytics
GET /api/matters/:id/views
{
"totalViews": 42,
"uniqueViewers": 8,
"recentViewers": [
{
"viewedAt": "2026-02-19T10:30:00.000Z",
"user": { "id": "cm...", "name": "Alice", "email": "alice@example.com", "image": null }
}
],
"leaderboard": [
{ "user": { "id": "cm...", "name": "Alice", "email": "alice@example.com", "image": null }, "views": 12 },
{ "user": { "id": "cm...", "name": "Bob", "email": "bob@example.com", "image": null }, "views": 8 }
],
"viewsOverTime": [
{ "date": "2026-02-01", "count": 3 },
{ "date": "2026-02-02", "count": 5 }
]
}
Record a View
POST /api/matters/:id/views
Response (201 Created):
{ "recorded": true }
Response (200 - throttled, already viewed within 5 min):
{ "recorded": false }
The MatterView model stores matterId, userId, workspaceId, and viewedAt. Views are scoped to the active workspace. The leaderboard returns up to 20 users sorted by view count descending. The viewsOverTime array contains daily view counts for the last 30 days.
Duplicate a Matter
Creates a copy of a matter with all steps reset. The new matter gets a fresh number, all steps are set to PENDING (first step to ACTIVE), and completion data is cleared.
POST /api/matters/:id/duplicate
Response (201 Created):
{
"id": "cm...",
"number": 42
}
The duplicate copies title (with "(Copy)" suffix), template, metadata, and all step definitions. It does not copy: assignee, submission link, comments, documents, subtasks, or relations.
Related Matters
Link matters to each other with typed relationships. Relations are bidirectional - directional types (BLOCKS/BLOCKED_BY, PARENT/CHILD) are automatically inverted when viewed from the other side.
List Relations
GET /api/matters/:id/relations
{
"relations": [
{
"id": "cm...",
"relationType": "RELATED",
"matter": {
"id": "cm...",
"number": 5,
"numberPrefix": "ONB",
"title": "Acme Corp - Phase 2",
"status": "OPEN"
},
"createdAt": "2026-02-13T10:00:00.000Z"
},
{
"id": "cm...",
"relationType": "BLOCKED_BY",
"matter": {
"id": "cm...",
"number": 3,
"numberPrefix": "ONB",
"title": "Pending KYC",
"status": "OPEN"
},
"createdAt": "2026-02-12T14:00:00.000Z"
}
]
}
Create a Relation
POST /api/matters/:id/relations
Content-Type: application/json
{
"relatedMatterId": "cm...",
"relationType": "BLOCKS"
}
Body Parameters
| Field | Type | Required | Description |
|---|---|---|---|
relatedMatterId | string | Yes | ID of the matter to link. Must be in the same workspace. |
relationType | string | No | RELATED (default), BLOCKS, BLOCKED_BY, PARENT, or CHILD |
Relation Types
| Type | Inverse | Description |
|---|---|---|
RELATED | RELATED | General association (symmetric) |
BLOCKS | BLOCKED_BY | This matter blocks progress on the related matter |
BLOCKED_BY | BLOCKS | This matter is blocked by the related matter |
PARENT | CHILD | This matter is the parent of the related matter |
CHILD | PARENT | This matter is a child of the related matter |
Remove a Relation
DELETE /api/matters/:id/relations/:relationId
Returns 409 Conflict if a relation between the two matters already exists. A matter cannot be related to itself.
Compliance Exceptions
Track compliance issues per matter. Exceptions have a type, severity, status lifecycle, optional due date, and resolution notes. Useful for regulatory tracking and audit trails.
List Exceptions
GET /api/matters/:id/compliance-exceptions?status=OPEN
{
"exceptions": [
{
"id": "cm...",
"type": "KYC",
"severity": "HIGH",
"description": "Missing passport verification",
"status": "OPEN",
"dueDate": "2026-03-01T00:00:00.000Z",
"resolutionNotes": null,
"createdBy": { "id": "cm...", "name": "Admin", "email": "admin@example.com" },
"resolvedBy": null,
"createdAt": "2026-02-13T10:00:00.000Z"
}
],
"summary": {
"total": 5,
"openCount": 3,
"overdueCount": 1
}
}
Query Parameters
| Parameter | Type | Description |
|---|---|---|
status | string | Filter by status: OPEN, ACKNOWLEDGED, IN_PROGRESS, RESOLVED, or WAIVED |
Create an Exception
POST /api/matters/:id/compliance-exceptions
Content-Type: application/json
{
"type": "KYC",
"severity": "HIGH",
"description": "Missing passport verification for primary shareholder",
"dueDate": "2026-03-01T00:00:00.000Z"
}
Body Parameters (Create)
| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | Exception type (e.g. KYC, Documentation, Regulatory, Operational) |
severity | string | Yes | LOW, MEDIUM, HIGH, or CRITICAL |
description | string | Yes | Detailed description (max 5000 characters) |
dueDate | string | null | No | ISO 8601 date for resolution deadline |
Update an Exception
PATCH /api/matters/:id/compliance-exceptions/:exceptionId
Content-Type: application/json
{
"status": "RESOLVED",
"resolutionNotes": "Passport verified and uploaded to Documents tab"
}
All fields from the create payload are accepted, plus status and resolutionNotes. When status changes to RESOLVED or WAIVED, resolvedAt and resolvedById are automatically set. Re-opening clears these fields.
Exception Status Lifecycle
| Status | Description |
|---|---|
OPEN | Newly created, awaiting action |
ACKNOWLEDGED | Reviewed and acknowledged by the team |
IN_PROGRESS | Actively being worked on |
RESOLVED | Issue resolved. Sets resolvedAt and resolvedById automatically. |
WAIVED | Exception waived (accepted risk). Sets resolvedAt and resolvedById automatically. |
Delete an Exception
DELETE /api/matters/:id/compliance-exceptions/:exceptionId
All compliance exception mutations are logged to the audit trail with full before/after metadata.
Bulk Step Assignment
Assign multiple steps to a user in a single operation. Useful for reassigning workload across team members. Supports an optional reason that is included in audit logs and notifications.
POST /api/matters/bulk-assign
Content-Type: application/json
{
"stepIds": ["cm_step_1", "cm_step_2", "cm_step_3"],
"assignedToId": "cm_user_456",
"reason": "Reassigning due to team member on leave"
}
Body Parameters
| Field | Type | Required | Description |
|---|---|---|---|
stepIds | string[] | Yes | Array of step IDs (max 50) |
assignedToId | string | Yes | User ID of the new assignee. Must be an org member. |
reason | string | No | Reassignment reason (max 500 characters). Included in audit log and notifications. |
Response
{
"updated": 3,
"errors": []
}
Partial failure:
{
"updated": 2,
"errors": [
{ "stepId": "cm_step_3", "error": "Step not found or not accessible" }
]
}
The previous assignee receives an "unassigned" notification, and the new assignee receives an "assigned" notification (unless the actor is the assignee). Each step update is individually audited.
Signing Envelopes
Track signing envelope status per matter. See which recipients have signed and which are still pending in multi-recipient envelopes. Status updates can be driven by webhooks.
POST /api/matters/:id/envelopes
{
"envelopeId": "envelope-id",
"subject": "Incorporation Documents",
"status": "sent",
"recipients": [
{ "email": "signer@example.com", "name": "John Doe", "status": "pending" }
]
}
Response:
{
"id": "cm...",
"envelopeId": "envelope-id",
"subject": "Incorporation Documents",
"status": "sent",
"recipients": [
{ "email": "signer@example.com", "name": "John Doe", "status": "pending", "signedAt": null }
]
}
Regulator Return Comments
Track return comments from regulators on matter filings. Each comment has a status lifecycle (OPEN -> IN_PROGRESS -> RESOLVED) with resolution notes.
POST /api/matters/:id/regulator-comments
{
"comment": "Please provide additional KYC documentation for the beneficial owner.",
"source": "ADGM Registration Authority",
"category": "KYC",
"priority": "HIGH"
}
Response:
{
"id": "cm...",
"comment": "Please provide additional KYC documentation...",
"source": "ADGM Registration Authority",
"category": "KYC",
"priority": "HIGH",
"status": "OPEN",
"resolutionNotes": null,
"createdAt": "2026-02-16T..."
}
Update status / resolve:
PATCH /api/matters/:id/regulator-comments/:commentId
{
"status": "RESOLVED",
"resolutionNotes": "Additional passport and utility bill uploaded to Documents tab."
}
Client Portal Links
Generate a token-based portal link that gives external clients read access to their matter and its FORM steps. The client can view progress, fill assigned forms, and upload requested documents without needing a full account.
POST /api/matters/:id/portal
{
"clientName": "John Doe",
"clientEmail": "john@example.com"
}
Response:
{
"token": "portal-token-string",
"url": "/c/portal-token-string/portal/dashboard"
}
Board Properties
Board properties are org-scoped reusable field definitions. Boards define which fields apply via their metadataSchema; matters store values in metadataValues JSON.
Field Types
| Type | Value Format | Description |
|---|---|---|
text | string | Free text |
number | number | Numeric value |
date | string (ISO 8601) | Date value |
select | string | Single selection from options |
multiselect | string[] | Multiple selections |
boolean | boolean | True/false toggle |
currency | number | Currency amount |
url | string | URL link |
email | string | Email address |
document | {documentId, documentTitle, documentIcon?} | Knowledge Base document reference |
Get Board Properties
GET /api/matters/:id/metadata
{
"schema": [
{
"fieldId": "cm...",
"key": "jurisdiction",
"name": "Jurisdiction",
"type": "select",
"required": true,
"config": { "options": ["ADGM", "DIFC", "JAFZA"] }
},
{
"fieldId": "cm...",
"key": "estimated_value",
"name": "Estimated Value",
"type": "currency",
"required": false
},
{
"fieldId": "cm...",
"key": "engagement_letter",
"name": "Engagement Letter",
"type": "document",
"required": false
}
],
"values": {
"cm_field_1": "ADGM",
"cm_field_2": 50000,
"cm_field_3": {
"documentId": "cm...",
"documentTitle": "Engagement Letter - Acme",
"documentIcon": "file-text"
}
}
}
Update Board Properties
PATCH /api/matters/:id/metadata
Content-Type: application/json
{
"cm_field_1": "DIFC",
"cm_field_2": 75000
}
Pass field IDs as keys with their new values. Only fields present in the request body are updated; others are preserved. Changes are audited with per-field before/after values.
Line Items
Line items track pricing for a matter. Each item has quantity, unit price/cost, discount %, tax %, and an optional fee category. All CRUD uses the collection route (/api/matters/:id/line-items).
List Line Items
GET /api/matters/:id/line-items
{
"lineItems": [
{
"id": "cm...",
"description": "Government Filing Fee",
"quantity": 1,
"unitPrice": 500,
"unitCost": 0,
"discountPercent": 0,
"taxPercent": 5,
"feeCategory": "Government",
"sortOrder": 0,
"archived": false
}
],
"summary": {
"subtotal": 500,
"totalDiscount": 0,
"totalTax": 25,
"total": 525
}
}
Create Line Item
POST /api/matters/:id/line-items
Content-Type: application/json
{
"description": "Professional Services Fee",
"quantity": 10,
"unitPrice": 200,
"unitCost": 100,
"discountPercent": 10,
"taxPercent": 5,
"feeCategory": "Processing"
}
FORM Step Lifecycle
FORM steps follow a lifecycle: select form -> send to recipient -> recipient submits -> step auto-completes with captured data. Steps with autoRun: true in their config auto-send to the client portal when activated.
Initialize Form
POST /api/matters/:id/steps/:stepId/init-form
Content-Type: application/json
{
"formId": "cm..."
}
Selects a form for the step and creates a DRAFT submission. The step's config is updated with the form ID.
Send Form
POST /api/matters/:id/steps/:stepId/send-form
Content-Type: application/json
{
"recipientEmail": "client@example.com",
"recipientName": "John Smith"
}
Sends the form to the recipient via the client portal. Creates a portal link and sends an email notification.
Get Form Status
GET /api/matters/:id/steps/:stepId/form-status
{
"mode": "form_selected",
"formId": "cm...",
"formTitle": "Client Intake Form",
"submissionId": "cm...",
"submissionStatus": "PENDING",
"sentAt": "2026-02-13T10:00:00.000Z",
"recipientEmail": "client@example.com"
}
Clear Form
POST /api/matters/:id/steps/:stepId/clear-form
Clears the form selection and removes the associated submission. Resets the step to the form selection state.
Download All Documents
Downloads all matter documents as a ZIP archive.
POST /api/matters/:id/documents/download
Response: ZIP file stream (application/zip)
Folder Templates
Boards can define a folderTemplate that specifies a folder hierarchy to be automatically created when a matter is instantiated from the board. The template is a recursive JSON structure of type FolderTemplateNode[].
FolderTemplateNode Schema
{
"name": "string",
"children": "FolderTemplateNode[] (optional, nested sub-folders)"
}
Auto-folder Creation
When a matter is created, if its board has a folderTemplate, the entire folder hierarchy is automatically created and linked to the matter. This ensures every matter starts with a consistent file organisation structure.
Set Folder Template on a Board
PATCH /api/matter-templates/:id
Content-Type: application/json
{
"folderTemplate": [
{
"name": "Corporate Documents",
"children": [
{ "name": "Incorporation" },
{ "name": "Resolutions" },
{ "name": "Share Certificates" }
]
},
{
"name": "Compliance",
"children": [
{ "name": "KYC" },
{ "name": "AML" }
]
},
{ "name": "Correspondence" }
]
}
To clear the folder template, pass { "folderTemplate": null }. The folderTemplate field is a nullable JSON array on the board.
List Folder Template Presets
GET /api/matters/templates/folder-presets
{
"presets": [
{
"key": "corporate-services",
"name": "Corporate Services",
"description": "Standard folder structure for CSP matters",
"template": [
{
"name": "Corporate Documents",
"children": [
{ "name": "Incorporation" },
{ "name": "Resolutions" }
]
},
{ "name": "Compliance" },
{ "name": "Correspondence" }
]
}
]
}
Create Filing Event Folder
POST /api/matters/:id/files/filing-folder
Content-Type: application/json
{
"description": "Annual Return 2026"
}
Response:
{
"folderPath": "Compliance/Filings/Annual Return 2026"
}
Creates a new folder within the matter's file hierarchy for a specific filing event. The folder is named using the provided description and placed in the appropriate location within the matter's folder structure.
Board Permissions & Grants
Board access is controlled via the permissions JSON on the board (matter template). In addition to per-user member entries, boards support extended grants for workspace roles and oversight workspace access.
Update Board Permissions
PATCH /api/matter-templates/:id
Content-Type: application/json
{
"permissions": {
"members": [
{ "userId": "cm...", "role": "edit" },
{ "userId": "cm...", "role": "view", "sourceWorkspaceId": "cm_ws_overseer" }
],
"grants": [
{ "targetType": "workspace_role", "targetId": "MEMBER", "accessLevel": "view" },
{ "targetType": "oversight_workspace", "targetId": "cm_ws_overseer", "accessLevel": "view" }
]
}
}
Members with a sourceWorkspaceId are users from an oversight workspace. The grants array supports user, workspace_role, and oversight_workspace target types. Oversight refs are validated against active relationships.
Access Resolution
- OWNER/ADMIN always have full access (view + edit + manage)
- Explicit member entry with
editrole grants view + edit - Workspace role or oversight grants provide view or edit access as specified
- If no permissions are configured, all workspace members have view access by default
Matter Status Flow
OPEN -> ON_HOLD (pause) -> OPEN (resume) -> COMPLETED (all steps done) or CANCELLED. A matter can only be marked COMPLETED when all required steps are COMPLETED or SKIPPED.