List cases
/api/casesReturns a paginated list of cases for your tenant with filtering, search, and sorting.
Required scope: cases:read
Response
{
"cases": [
{
"id": "clx...",
"reference": "CW-2026-0042",
"internalRef": "CA-2026-1187",
"status": "open",
"residentName": "Jane Smith",
"residentEmail": "jane@example.com",
"residentPhone": "07700900123",
"residentAddress": "12 High Street",
"residentPostcode": "TR11AA",
"summary": "Pothole on High Street near junction with Mill Lane",
"sourceChannel": "email",
"archived": false,
"createdAt": "2026-01-15T10:30:00.000Z",
"updatedAt": "2026-01-15T10:30:00.000Z",
"category": { "id": "cat-456", "name": "Roads & Pavements" },
"ward": { "id": "ward-123", "name": "Truro East" },
"assignee": { "id": "usr-789", "name": "John Doe", "role": "caseworker" }
}
],
"pagination": {
"page": 1,
"pageSize": 20,
"total": 42,
"totalPages": 3
}
}
Search behaviour
When the search parameter is provided, results are ranked by relevance and the response includes "searchActive": true. Non-search filters still apply alongside the search query.
Caseworker visibility
Users with the caseworker role can only see cases assigned to them. Admins and managers can see all cases and filter by assigneeId.
Get a case
/api/cases/:idFetch a single case with its full activity timeline.
Required scope: cases:read
Response
{
"id": "clx...",
"reference": "CW-2026-0042",
"internalRef": null,
"status": "in_progress",
"residentName": "Jane Smith",
"residentEmail": "jane@example.com",
"residentPhone": "07700900123",
"residentAddress": "12 High Street",
"residentPostcode": "TR11AA",
"summary": "Pothole on High Street near junction with Mill Lane",
"sourceChannel": "email",
"archived": false,
"highlightedForPublic": false,
"publicSummary": null,
"publicThemes": null,
"createdAt": "2026-01-15T10:30:00.000Z",
"updatedAt": "2026-01-16T09:00:00.000Z",
"category": { "id": "cat-456", "name": "Roads & Pavements" },
"ward": { "id": "ward-123", "name": "Truro East" },
"assignee": { "id": "usr-789", "name": "John Doe", "role": "caseworker" },
"createdBy": { "id": "usr-101", "name": "Admin User", "role": "admin" },
"activities": [
{
"id": "act-001",
"type": "status_change",
"content": "Status changed from Open to In Progress",
"oldValue": "open",
"newValue": "in_progress",
"createdAt": "2026-01-16T09:00:00.000Z",
"user": { "id": "usr-789", "name": "John Doe", "role": "caseworker" }
},
{
"id": "act-002",
"type": "note",
"content": "Contacted highways department, repair scheduled for next week.",
"oldValue": null,
"newValue": null,
"createdAt": "2026-01-16T09:15:00.000Z",
"user": { "id": "usr-789", "name": "John Doe", "role": "caseworker" }
}
]
}
Create a case
/api/casesCreate a new case. Returns the created case with a generated reference number.
Required scope: cases:write
Returns 201 Created with the full case object including the generated reference.
Validation errors
When required fields are missing or foreign keys are invalid, the API returns 400 with an errors object:
{
"errors": {
"residentName": "Resident name is required",
"summary": "Case summary is required"
}
}
Update a case
/api/cases/:idUpdate case status, reassign, archive, or manage public highlighting.
Required scope: cases:write
Returns the updated case object. Status changes, reassignments, and archive actions are automatically recorded in the activity timeline.
Resident notifications
When the status changes to In Progress, Resolved, or Closed, the resident is automatically notified via email and SMS (if contact details are on file).
Bulk operations
/api/cases/bulkPerform bulk actions on up to 100 cases at once.
Required scope: cases:write
Actions
| Action | Description | Extra fields |
|---|---|---|
status_change | Set all cases to a new status | status (required) |
reassign | Reassign all cases to a user | assigneeId (required) |
close | Close all specified cases | None |
archive | Archive all specified cases | None |
Example: bulk status change
{
"caseIds": ["clx-001", "clx-002", "clx-003"],
"action": "status_change",
"status": "resolved"
}
Response
{
"updated": 3,
"action": "status_change",
"status": "resolved"
}
Bulk limits
A maximum of 100 cases can be updated in a single bulk request. All cases must belong to your tenant.
Add a note
/api/cases/:id/activityAdd a note to the case activity timeline.
Required scope: cases:write
Returns 201 Created with the activity log entry:
{
"id": "act-003",
"type": "note",
"content": "Contacted highways department, repair scheduled.",
"oldValue": null,
"newValue": null,
"createdAt": "2026-01-16T14:30:00.000Z",
"user": { "id": "usr-789", "name": "John Doe", "role": "caseworker" }
}
List attachments
/api/cases/:id/attachmentsList all file attachments for a case.
Required scope: cases:read
Response
[
{
"id": "att-001",
"originalName": "pothole-photo.jpg",
"mimeType": "image/jpeg",
"sizeBytes": 245760,
"storageKey": "cases/2026/01/abc123.jpg",
"thumbKey": "cases/2026/01/abc123_thumb.jpg",
"createdAt": "2026-01-16T10:00:00.000Z",
"uploadedBy": { "id": "usr-789", "name": "John Doe" }
}
]
Upload attachment
/api/cases/:id/attachmentsUpload a file attachment to a case. Requires Team plan or above.
Required scope: cases:write (and file_attachments plan feature)
Returns 201 Created with the attachment object. Image files automatically get a thumbnail generated.
Allowed file types
| Category | Types |
|---|---|
| Images | JPEG, PNG, GIF, WebP, SVG |
| Documents | PDF, Word (.doc, .docx), Excel (.xls, .xlsx) |
| Text | Plain text, CSV |
Plan requirement
File attachments require the Team plan or above. Requests from lower-tier plans receive a 403 Forbidden response.
Case lifecycle
Cases follow a defined status workflow. Only valid transitions are allowed — invalid transitions return a 400 error.
open --> in_progress --> chasing --> resolved --> closed
| ^ |
+--------> resolved -----+ |
|
open <------------------------------+
(reopen)
Valid transitions
| From | To |
|---|---|
open | in_progress |
in_progress | chasing, resolved |
chasing | in_progress, resolved |
resolved | closed |
closed | open (reopen) |
Status descriptions
| Status | Description |
|---|---|
open | New case, not yet being worked on |
in_progress | Actively being investigated or actioned |
chasing | Waiting on a response from an external party |
resolved | Issue has been addressed, pending confirmation |
closed | Case is complete and closed |
Archiving vs closing
Closing a case marks it as complete. Archiving removes it from the default case list but preserves the full history. Archived cases can be restored at any time.
Activity types
The activity timeline tracks all changes to a case. Each entry has a type field:
| Type | Description |
|---|---|
status_change | Status was changed (includes oldValue and newValue) |
reassignment | Case was reassigned to a different user |
note | A free-text note was added by a user |
attachment | A file was attached to the case |
highlight | Case was highlighted or removed from public content |
Webhook events
Status changes and notes trigger webhook events for integrations. See the Webhooks documentation for setup.
| Event | Triggered when |
|---|---|
case_created | A new case is created |
case_status_changed | Any status transition occurs |
case_resolved | Case moves to resolved |
case_closed | Case moves to closed |
case_note_added | A note is added to the timeline |