Opbox

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

TypeMovementSteps/StagesCompletion
PROCESSLinear (phase-enforced)Typed steps (FORM, APPROVAL, TASK...)Complete each step -> auto-advance
PIPELINEFree (any direction via goto)Named stages with optional closedState: 'won' | 'lost'Move to terminal stage -> auto-complete/cancel
KANBANFree (any direction via goto)Status columns with optional doneState: trueMove to done column -> auto-complete
  • PROCESS uses advance and complete for linear progression. Steps must be completed in order within each phase.
  • PIPELINE/KANBAN use goto for all movement. Stages are materialized as MANUAL steps. Terminal stages auto-update matter status.
  • Set boardType when creating a board via POST /api/matter-templates. Defaults to PROCESS.

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

MethodEndpointDescription
GET/api/mattersList all matters
POST/api/mattersCreate a matter from board
GET/api/matters/labelsList all labels used across matters in the org
GET/api/matters/:idGet matter details
PATCH/api/matters/:idUpdate matter (title, status, assignee, due date, priority, labels)
DELETE/api/matters/:idCancel a matter
POST/api/matters/:id/advanceAdvance to next step
POST/api/matters/:id/gotoJump to a specific step
POST/api/matters/:id/steps/:stepId/completeComplete a specific step
POST/api/matters/:id/steps/:stepId/rejectReject or send back a step
POST/api/matters/:id/steps/:stepId/skipSkip a step
POST/api/matters/:id/steps/:stepId/reopenReopen a completed step
POST/api/matters/:id/steps/:stepId/assignAssign a step to a user
GET/api/matters/:id/dataGet aggregated form data from all steps
GET/api/matters/:id/metadataGet board property schema and values
PATCH/api/matters/:id/metadataUpdate board property values
GET/api/matters/:id/line-itemsList line items with pricing
POST/api/matters/:id/line-itemsCreate a line item
PATCH/api/matters/:id/line-itemsBulk update / reorder line items
DELETE/api/matters/:id/line-itemsBulk delete line items
POST/api/matters/:id/steps/:stepId/init-formInitialize form for a FORM step
POST/api/matters/:id/steps/:stepId/send-formSend form to a recipient email
POST/api/matters/:id/steps/:stepId/clear-formClear form selection for a FORM step
GET/api/matters/:id/steps/:stepId/form-statusGet FORM step status (selection, sent, submitted)
GET/api/matters/:id/steps/:stepId/automation-contextGet step automation context
GET/api/matters/:id/commentsList matter comments
POST/api/matters/:id/commentsAdd a comment
GET/api/matters/:id/documentsList matter documents
POST/api/matters/:id/documentsAttach a document
DELETE/api/matters/:id/documents/:docIdRemove a document
POST/api/matters/:id/documents/downloadDownload all documents as ZIP archive
POST/api/matters/:id/files/filing-folderCreate a filing event folder for a matter
PATCH/api/matters/:id/steps/:stepId/due-dateSet or clear a step due date
GET/api/matters/:id/steps/:stepId/subtasksList subtasks for a step
POST/api/matters/:id/steps/:stepId/subtasksCreate a subtask on a step
PATCH/api/matters/:id/steps/:stepId/subtasks/:subtaskIdUpdate a subtask
DELETE/api/matters/:id/steps/:stepId/subtasks/:subtaskIdDelete a subtask
POST/api/matters/:id/steps/:stepId/subtasks/:subtaskId/toggleToggle subtask completion
GET/api/matters/:id/activityGet matter activity timeline
GET/api/matters/:id/followGet follow status
POST/api/matters/:id/followFollow a matter
DELETE/api/matters/:id/followUnfollow a matter
POST/api/matters/:id/duplicateDuplicate a matter
GET/api/matters/:id/relationsList related matters
POST/api/matters/:id/relationsLink a related matter
DELETE/api/matters/:id/relations/:relationIdRemove a matter relation
GET/api/matters/:id/compliance-exceptionsList compliance exceptions with summary
POST/api/matters/:id/compliance-exceptionsCreate a compliance exception
GET/api/matters/:id/compliance-exceptions/:exceptionIdGet a single compliance exception
PATCH/api/matters/:id/compliance-exceptions/:exceptionIdUpdate a compliance exception
DELETE/api/matters/:id/compliance-exceptions/:exceptionIdDelete a compliance exception
GET/api/matters/:id/envelopesList signing envelopes for a matter
POST/api/matters/:id/envelopesCreate a signing envelope
GET/api/matters/:id/envelopes/:envelopeIdGet envelope details
PATCH/api/matters/:id/envelopes/:envelopeIdUpdate envelope status or recipient signing status
DELETE/api/matters/:id/envelopes/:envelopeIdDelete an envelope
POST/api/matters/:id/envelopes/:envelopeId/signingTrigger signing action (send, remind, void, status-check)
GET/api/matters/:id/regulator-commentsList regulator return comments
POST/api/matters/:id/regulator-commentsAdd a regulator return comment
PATCH/api/matters/:id/regulator-comments/:commentIdUpdate a regulator comment (status, resolution)
DELETE/api/matters/:id/regulator-comments/:commentIdDelete a regulator comment
POST/api/matters/:id/portalCreate a client portal link for a matter
POST/api/matters/bulk-assignBulk assign steps to a user
GET/api/matters/:id/viewsGet view analytics (total, unique, leaderboard, 30-day chart)
POST/api/matters/:id/viewsRecord a page view (throttled)
GET/api/cron/trigger-scheduleEvaluate schedule-type MatterTriggers and auto-create matters (cron)

Matter Boards

MethodEndpointDescription
GET/api/matter-templatesList boards
POST/api/matter-templatesCreate a board
GET/api/matter-templates/:idGet board details
PATCH/api/matter-templates/:idUpdate a board (including folderTemplate)
DELETE/api/matter-templates/:idArchive or permanently delete a board
GET/api/matter-templates/optionsGet forms, users, and emails for board configuration
GET/api/matters/templates/folder-presetsList available folder template presets
GET/api/matter-templates/:id/versionsList board version history
GET/api/matter-templates/:id/versions/:versionIdGet a specific board version with full steps
POST/api/matter-templates/:id/versions/:versionId/rollbackRollback 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.

MethodEndpointDescription
GET/api/matter-metadata-fieldsList all board property definitions
POST/api/matter-metadata-fieldsCreate a board property field
PATCH/api/matter-metadata-fieldsUpdate a board property field (id in body)
DELETE/api/matter-metadata-fieldsDelete 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

ParameterTypeDescription
statusstringFilter by status: OPEN, ON_HOLD, COMPLETED, CANCELLED
templateIdstringFilter by board (template ID)
assignedToIdstringFilter by assignee user ID. Use "unassigned" to filter unassigned matters.
searchstringSearch by matter title, board name, or number (e.g. "ONB-0001")
updatedFrom / updatedTostringFilter by updated date range (ISO date strings)
sortBystringSort field: createdAt, updatedAt, number, title, status (default: updatedAt)
sortOrderstringSort direction: asc or desc (default: desc)
includeAllStepsbooleanWhen true, returns all steps per matter (with stepIndex, phaseIndex, phaseName) instead of only the active step. Used by the kanban board view.
phasestringFilter by phase name (exact match on active step's phaseName)
stepTypestringFilter by active step type: FORM, APPROVAL, TASK, DOCUMENT, WORKFLOW, MANUAL, EMAIL
includeSubordinatesbooleanWhen 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 / limitintPagination (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

FieldTypeRequiredDescription
templateIdstringYesID of the board to use
titlestringYesMatter title
stepsarrayNoCustom steps to override the board. Each step needs id, name, type, and required.
submissionIdstringNoLink to a form submission. The linked submission's GET /api/submissions response will include this matter in its matters array.
assignedToIdstringNoUser ID to assign the matter to. If omitted, auto-follows the first step's assignee.
dueDatestring (ISO 8601)NoDue date for the matter. Pass an ISO date string or null to leave unset.
metadataobjectNoCustom 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

FieldTypeDescription
stepIndexnumberTarget step index (0-based). Provide this or stepId.
stepIdstringTarget 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

FieldTypeDescription
_typestringAlways "form_submission"
formIdstringID of the form that was submitted
formTitlestringTitle of the form at submission time
submittedAtstringISO 8601 timestamp
fieldsarrayArray of StepFormField objects (see below)

Step Form Field Shape

FieldTypeDescription
keystringField key from the form schema
labelstringHuman-readable field label
typestringField type (text, email, select, checkbox, date, currency, etc.)
valueanyThe submitted value (string, number, boolean, array, or null)
sectionTitlestring?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

FieldTypeDescription
phaseIndexnumber | null0-based index of the phase this step belongs to. Null for legacy matters without phases.
phaseNamestring | nullName 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"]
}
FieldTypeDescription
contentstringComment text (required)
stepIdstringOptional step to scope the comment to
typestringCOMMENT (default), NOTE, or SYSTEM
mentionedUserIdsstring[]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).

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

TypeDescriptionConfig Fields
FORMCollect additional data via a sub-formformId
APPROVALRequires explicit approval to proceedapprovalRoles
TASKGeneral task or action itemtaskDescription
DOCUMENTDocument collection and generation. Optionally triggers the Document Engine integration on completion when autoGenerate is enabled.documentTypes, templatePack, autoGenerate
WORKFLOWTriggers an automated workflow run, then auto-completesworkflowId
MANUALConfirm a real-world action was completedchecklist, requireNote
EMAILCompose and send an email with optional file attachments from the matteremailRecipient, 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).

FieldTypeDescription
templatePackstringDocument Engine template pack path (e.g. incorporation/spv). The pack must belong to the current workspace. Required when autoGenerate is true.
autoGeneratebooleanWhen 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.
documentTypesstring[]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:

FieldTypeDescription
assigneeUserIdstringUser ID to auto-assign when this step activates
notifyEmailstringEmail address to notify when this step activates
descriptionstringStep description
requiredbooleanWhether this step must be completed (default: true). Non-required steps can be skipped without blocking phase progression.
assigneeConfigobjectAuto-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

FieldTypeDescription
dueDatestring | nullISO 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

FieldTypeRequiredDescription
titlestringYesSubtask title
assignedToIdstringNoUser ID to assign the subtask to. Must be an org member.
dueDatestringNoISO 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.

CategoryEventsTracked Fields
Matter lifecycleCreate, update, cancel, duplicateTitle, status, priority, assignee, due date, labels (before/after)
Step lifecycleComplete, reject, skip, reopen, advance, jump (goto)Step name, rejection reason, completion notes
Step assignmentAssign user to stepPrevious assignee, new assignee name
Step due datesSet or remove due datePrevious due date, new due date
SubtasksCreate, update, delete, complete, reopenSubtask title, changed fields
Form stepsInitialize form, send form to clientRecipient email, matter number
MetadataUpdate metadata property valuesChanged field names, per-field before/after values
Line itemsAdd, update, delete, reorderLine item name
RelationsAdd or remove related mattersRelated matter title, relation type
FollowersFollow, unfollowActor name
DocumentsUpload, removeFile name, category, step context
Portal linksCreate client portal linkClient email
DescriptionUpdate rich text descriptionChange 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.

EventTypeRecipientsDefault
Matter assignedMATTER_ASSIGNEDAssigneeON
Step assignedMATTER_STEP_ASSIGNEDStep assigneeON
Step completedMATTER_STEP_COMPLETEDFollowersON
Step rejectedMATTER_STEP_REJECTEDFollowers + send-back assigneeON
Status changedMATTER_STATUS_CHANGEDFollowersON
Comment addedMATTER_COMMENTFollowers + @mentioned usersOFF
Document uploadedMATTER_DOCUMENTFollowersOFF
Subtask assignedMATTER_SUBTASK_ASSIGNEDSubtask assigneeOFF
Subtask completedMATTER_SUBTASK_COMPLETEDStep assigneeOFF
Due date changedMATTER_DUE_DATEStep assigneeOFF
Step skippedMATTER_STEP_SKIPPEDFollowersOFF
Step reopenedMATTER_STEP_REOPENEDFollowers + step assigneeOFF
Step jumpedMATTER_STEP_JUMPEDFollowersOFF

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

StatusDescription
ACTIVEBoard is visible and can be used to create new matters
DRAFTBoard is in draft state and not yet published
ARCHIVEDBoard 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 }
  ]
}
FieldTypeDescription
labels[].namestringThe label text
labels[].countnumberNumber 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

FieldTypeDescription
titlestringMatter title
statusstringOPEN, ON_HOLD, COMPLETED, or CANCELLED
assignedToIdstring | nullUser ID to assign, or null to unassign
dueDatestring | nullISO 8601 date, or null to clear
prioritystringNONE, LOW, MEDIUM, HIGH, or URGENT
tagsstring[]Array of label strings (max 20)
metadataobjectCustom 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.

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

FieldTypeRequiredDescription
relatedMatterIdstringYesID of the matter to link. Must be in the same workspace.
relationTypestringNoRELATED (default), BLOCKS, BLOCKED_BY, PARENT, or CHILD

Relation Types

TypeInverseDescription
RELATEDRELATEDGeneral association (symmetric)
BLOCKSBLOCKED_BYThis matter blocks progress on the related matter
BLOCKED_BYBLOCKSThis matter is blocked by the related matter
PARENTCHILDThis matter is the parent of the related matter
CHILDPARENTThis 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

ParameterTypeDescription
statusstringFilter 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)

FieldTypeRequiredDescription
typestringYesException type (e.g. KYC, Documentation, Regulatory, Operational)
severitystringYesLOW, MEDIUM, HIGH, or CRITICAL
descriptionstringYesDetailed description (max 5000 characters)
dueDatestring | nullNoISO 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

StatusDescription
OPENNewly created, awaiting action
ACKNOWLEDGEDReviewed and acknowledged by the team
IN_PROGRESSActively being worked on
RESOLVEDIssue resolved. Sets resolvedAt and resolvedById automatically.
WAIVEDException 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

FieldTypeRequiredDescription
stepIdsstring[]YesArray of step IDs (max 50)
assignedToIdstringYesUser ID of the new assignee. Must be an org member.
reasonstringNoReassignment 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."
}

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

TypeValue FormatDescription
textstringFree text
numbernumberNumeric value
datestring (ISO 8601)Date value
selectstringSingle selection from options
multiselectstring[]Multiple selections
booleanbooleanTrue/false toggle
currencynumberCurrency amount
urlstringURL link
emailstringEmail 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 edit role 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.