Accounting & Invoices API
Create, manage, and track invoices with full lifecycle support. Invoices can be linked to matters, synced to external accounting software, and include aging analysis, revenue breakdowns, and dashboard-level statistics.
Endpoints
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/invoices | List invoices with optional filters |
| POST | /api/invoices | Create a new invoice (ADMIN/OWNER) |
| GET | /api/invoices/:id | Get a single invoice by ID |
| PATCH | /api/invoices/:id | Update an invoice (ADMIN/OWNER) |
| DELETE | /api/invoices/:id | Delete a draft invoice (ADMIN/OWNER) |
| POST | /api/invoices/:id/sync | Push invoice to external accounting system or pull status (ADMIN/OWNER) |
| GET | /api/invoices/stats | Invoice summary statistics and aging buckets |
| GET | /api/accounting/stats | Dashboard-level accounting metrics (revenue, monthly trends, top clients) |
List Invoices
Returns a paginated list of invoices for your workspace. Supports filtering by status, matter, and date range.
GET /api/invoices?status=AUTHORISED&matterId=cm_matter_789&from=2026-01-01&to=2026-02-28&page=1&pageSize=25
Query Parameters
| Parameter | Type | Description |
|---|---|---|
status | string | Filter by status: DRAFT, SUBMITTED, AUTHORISED, PAID, VOIDED, OVERDUE |
matterId | string | Filter by linked matter ID |
from | string | Issue date range start (YYYY-MM-DD) |
to | string | Issue date range end (YYYY-MM-DD) |
page | number | Page number (default: 1) |
pageSize | number | Results per page (default: 25, max: 100) |
Response
{
"invoices": [
{
"id": "cm_invoice_abc123",
"invoiceNumber": "INV-0012",
"contactName": "Acme Corp",
"contactEmail": "billing@acme.com",
"currency": "USD",
"subtotal": 5000,
"taxTotal": 500,
"total": 5500,
"status": "AUTHORISED",
"lineItems": [
{
"description": "Legal consultation - Q1 2026",
"quantity": 10,
"unitPrice": 500,
"taxRate": 10,
"amount": 5000
}
],
"issueDate": "2026-02-01T00:00:00.000Z",
"dueDate": "2026-03-01T00:00:00.000Z",
"paidDate": null,
"externalId": null,
"syncedAt": null,
"notes": "Q1 retainer invoice",
"reference": "PO-2026-041",
"matter": {
"id": "cm_matter_789",
"title": "Acme Corp Onboarding",
"number": 12,
"numberPrefix": "MAT"
},
"createdBy": {
"id": "cm_user_123",
"name": "Jane Smith",
"email": "jane@example.com"
},
"createdAt": "2026-02-01T10:00:00.000Z",
"updatedAt": "2026-02-01T10:00:00.000Z"
}
],
"pagination": {
"page": 1,
"pageSize": 25,
"total": 48,
"totalPages": 2
}
}
Create Invoice
Creates a new invoice in DRAFT status. Invoice numbers are auto-generated sequentially (e.g. INV-0001). Subtotal, tax, and total are computed server-side from line items. Requires ADMIN or OWNER role.
POST /api/invoices
Content-Type: application/json
Request Body
{
"matterId": "cm_matter_789",
"contactName": "Acme Corp",
"contactEmail": "billing@acme.com",
"currency": "USD",
"lineItems": [
{
"description": "Legal consultation - Q1 2026",
"quantity": 10,
"unitPrice": 500,
"taxRate": 10,
"amount": 5000
},
{
"description": "Document preparation",
"quantity": 1,
"unitPrice": 750,
"taxRate": 10,
"amount": 750
}
],
"issueDate": "2026-02-01",
"dueDate": "2026-03-01",
"notes": "Q1 retainer invoice",
"reference": "PO-2026-041"
}
Body Fields
| Field | Type | Required | Description |
|---|---|---|---|
contactName | string | Yes | Client or contact name (max 255 chars) |
contactEmail | string | No | Client email address |
matterId | string | No | Link invoice to a matter (must belong to your org) |
currency | string | No | 3-letter currency code (default: USD) |
lineItems | array | Yes | At least one line item (see line item schema below) |
issueDate | string | Yes | Issue date in YYYY-MM-DD format |
dueDate | string | Yes | Due date in YYYY-MM-DD format |
notes | string | No | Invoice notes (max 2000 chars) |
reference | string | No | External reference or PO number (max 255 chars) |
Line Item Schema
| Field | Type | Description |
|---|---|---|
description | string | Line item description (max 500 chars) |
quantity | number | Quantity (must be positive) |
unitPrice | number | Price per unit |
taxRate | number | Tax rate as a percentage, 0-100 (default: 0) |
amount | number | Line total before tax |
Response (201 Created)
{
"id": "cm_invoice_abc123",
"invoiceNumber": "INV-0012",
"contactName": "Acme Corp",
"contactEmail": "billing@acme.com",
"currency": "USD",
"subtotal": 5750,
"taxTotal": 575,
"total": 6325,
"status": "DRAFT",
"lineItems": [
{
"description": "Legal consultation - Q1 2026",
"quantity": 10,
"unitPrice": 500,
"taxRate": 10,
"amount": 5000
},
{
"description": "Document preparation",
"quantity": 1,
"unitPrice": 750,
"taxRate": 10,
"amount": 750
}
],
"issueDate": "2026-02-01T00:00:00.000Z",
"dueDate": "2026-03-01T00:00:00.000Z",
"paidDate": null,
"externalId": null,
"syncedAt": null,
"notes": "Q1 retainer invoice",
"reference": "PO-2026-041",
"matter": {
"id": "cm_matter_789",
"title": "Acme Corp Onboarding",
"number": 12,
"numberPrefix": "MAT"
},
"createdBy": {
"id": "cm_user_123",
"name": "Jane Smith",
"email": "jane@example.com"
},
"createdAt": "2026-02-01T10:00:00.000Z",
"updatedAt": "2026-02-01T10:00:00.000Z"
}
Get Invoice
Returns a single invoice by ID, including the linked matter and creator details.
GET /api/invoices/:id
Response
{
"id": "cm_invoice_abc123",
"invoiceNumber": "INV-0012",
"contactName": "Acme Corp",
"contactEmail": "billing@acme.com",
"currency": "USD",
"subtotal": 6000,
"taxTotal": 600,
"total": 6600,
"status": "SUBMITTED",
"lineItems": [
{
"description": "Legal consultation - Q1 2026",
"quantity": 12,
"unitPrice": 500,
"taxRate": 10,
"amount": 6000
}
],
"issueDate": "2026-02-01T00:00:00.000Z",
"dueDate": "2026-03-01T00:00:00.000Z",
"paidDate": null,
"externalId": null,
"syncedAt": null,
"notes": "Updated with additional hours",
"reference": "PO-2026-041",
"matter": {
"id": "cm_matter_789",
"title": "Acme Corp Onboarding",
"number": 12,
"numberPrefix": "MAT"
},
"createdBy": {
"id": "cm_user_123",
"name": "Jane Smith",
"email": "jane@example.com"
},
"createdAt": "2026-02-01T10:00:00.000Z",
"updatedAt": "2026-02-10T14:30:00.000Z"
}
Update Invoice
Updates an existing invoice. All fields are optional. When line items are updated, subtotal, tax, and total are recalculated automatically. Setting status to PAID will auto-set paidDate to the current date if not provided. Requires ADMIN or OWNER role.
PATCH /api/invoices/:id
Content-Type: application/json
Request Body
{
"status": "SUBMITTED",
"lineItems": [
{
"description": "Legal consultation - Q1 2026",
"quantity": 12,
"unitPrice": 500,
"taxRate": 10,
"amount": 6000
}
],
"notes": "Updated with additional hours"
}
Updatable Fields
| Field | Type | Description |
|---|---|---|
status | string | Transition status: DRAFT, SUBMITTED, AUTHORISED, PAID, VOIDED, OVERDUE |
contactName | string | Updated contact name |
contactEmail | string | Updated contact email |
lineItems | array | Replacement line items (recalculates totals) |
paidDate | string | null | Payment date (YYYY-MM-DD) or null to clear |
issueDate | string | Updated issue date (YYYY-MM-DD) |
dueDate | string | Updated due date (YYYY-MM-DD) |
notes | string | Invoice notes (max 2000 chars) |
reference | string | External reference or PO number |
Delete Invoice
Deletes an invoice. Only invoices with DRAFT status can be deleted. Non-draft invoices should be voided instead. Requires ADMIN or OWNER role.
DELETE /api/invoices/:id
Response
{ "success": true }
Error (409 Conflict)
{ "error": "Only draft invoices can be deleted. Void the invoice instead." }
Invoice Statistics
Returns aggregate invoice statistics including counts by status, outstanding and overdue amounts, monthly paid totals, and aging buckets. Aging buckets group overdue invoices by how many days past due: 0-15, 15-30, 30-60, and 60+ days.
GET /api/invoices/stats
Response
{
"statusCounts": {
"DRAFT": 5,
"SUBMITTED": 8,
"AUTHORISED": 12,
"PAID": 30,
"VOIDED": 2,
"OVERDUE": 3
},
"totalOutstanding": 42500,
"totalOverdue": 8200,
"totalPaidThisMonth": 15750,
"totalInvoiced": 185000,
"agingBuckets": {
"current": { "count": 4, "amount": 12000 },
"days15": { "count": 3, "amount": 9500 },
"days30": { "count": 2, "amount": 6800 },
"days60plus": { "count": 1, "amount": 3200 }
},
"totalCount": 60
}
Aging Buckets
| Bucket | Range | Description |
|---|---|---|
current | 0-15 days | Recently overdue invoices |
days15 | 15-30 days | Moderately overdue |
days30 | 30-60 days | Significantly overdue |
days60plus | 60+ days | Severely overdue, requires immediate attention |
Accounting Dashboard Stats
Returns comprehensive accounting metrics for dashboard widgets: revenue summary, monthly revenue trends (last 12 months), revenue by fee category and board, and top clients ranked by total invoiced amount.
GET /api/accounting/stats
Response
{
"summary": {
"totalRevenue": 152500,
"outstandingAmount": 42500,
"currency": "USD",
"invoiceCounts": {
"draft": 5,
"submitted": 8,
"authorised": 12,
"paid": 30,
"overdue": 3,
"voided": 2,
"total": 60
}
},
"monthlyRevenue": [
{ "month": "Mar 2025", "paid": 8200, "invoiced": 12500 },
{ "month": "Apr 2025", "paid": 9800, "invoiced": 14000 },
{ "month": "...", "paid": 0, "invoiced": 0 },
{ "month": "Feb 2026", "paid": 15750, "invoiced": 18200 }
],
"revenueByFeeCategory": [
{ "category": "Legal Consultation", "revenue": 85000, "fees": 4250, "count": 34 },
{ "category": "Document Preparation", "revenue": 32000, "fees": 1600, "count": 18 },
{ "category": "Uncategorized", "revenue": 12500, "fees": 625, "count": 8 }
],
"revenueByBoard": [
{ "id": "cm_template_abc", "name": "Client Onboarding", "total": 65000, "paid": 48000, "count": 22 },
{ "id": "cm_template_def", "name": "Compliance Review", "total": 42000, "paid": 35000, "count": 15 }
],
"topIndividuals": [
{ "name": "Acme Corp", "email": "billing@acme.com", "total": 32500, "paid": 28000, "count": 8 },
{ "name": "Globex Inc", "email": "finance@globex.com", "total": 24000, "paid": 24000, "count": 6 }
]
}
Response Sections
| Section | Description |
|---|---|
summary | Total revenue, outstanding amount, primary currency, and counts by invoice status |
monthlyRevenue | Last 12 months of paid and invoiced amounts per month |
revenueByFeeCategory | Revenue and fee totals grouped by matter line item fee category |
revenueByBoard | Total and paid revenue grouped by matter board (template) |
topIndividuals | Top 10 individuals ranked by total invoiced amount, with paid breakdown |
Accounting Sync
Syncs an invoice with external accounting software. Supports two actions: push (create or update the invoice externally) and pull (fetch the latest status from external system). Requires an active accounting integration configured in Settings > Integrations. Voided invoices cannot be pushed. Requires ADMIN or OWNER role.
POST /api/invoices/:id/sync
Content-Type: application/json
Request Body
| Field | Type | Description |
|---|---|---|
action | string | push (default) to send to external system, or pull to fetch latest status |
Push Response
Creates the invoice in the external system and stores the external invoice ID. Draft invoices are pushed as DRAFT, all others as AUTHORISED.
{
"invoice": {
"id": "cm_invoice_abc123",
"invoiceNumber": "INV-0012",
"status": "AUTHORISED",
"externalId": "acc_inv_a1b2c3d4",
"syncedAt": "2026-02-10T14:30:00.000Z",
"contactName": "Acme Corp",
"total": 6600
},
"externalInvoiceId": "acc_inv_a1b2c3d4",
"externalStatus": "AUTHORISED"
}
Pull Response
Fetches the current status from the external system and updates the local invoice. Only works for invoices that have been previously pushed (have an externalId).
{
"invoice": {
"id": "cm_invoice_abc123",
"invoiceNumber": "INV-0012",
"status": "PAID",
"externalId": "acc_inv_a1b2c3d4",
"syncedAt": "2026-02-15T09:00:00.000Z",
"paidDate": "2026-02-14T00:00:00.000Z",
"contactName": "Acme Corp",
"total": 6600
},
"externalStatus": "PAID"
}
Invoice Status Lifecycle
| Status | Description |
|---|---|
DRAFT | Invoice created but not yet sent. Can be edited or deleted. |
SUBMITTED | Invoice sent to the client, awaiting approval. |
AUTHORISED | Invoice approved and awaiting payment. |
PAID | Invoice fully paid. Sets paidDate automatically. |
OVERDUE | Invoice past due date and unpaid. |
VOIDED | Invoice cancelled. Cannot be synced externally or deleted. |
Permissions
- Read: All authenticated workspace members can list and view invoices and stats.
- Create / Update / Delete: Requires
ADMINorOWNERrole. - Accounting Sync: Requires
ADMINorOWNERrole and an active accounting integration. - Security: All changes require authentication.
- Audit: All create, update, and delete operations are logged to the audit trail.