Cases
A case is a matter tied to a client, identified by a case number: a criminal charge, a citation matter, or any docketed proceeding. Events and payments carry a case_number, so a case groups the records that share it; a message links to a case by its UUID. A case can have several attorneys, one judge, and one location.
This page assumes the shared API Conventions for pagination, filtering, lifecycle actions, and references. All endpoints use the region's base URL and a Bearer token.
Endpoints
| Method | Path | Description |
|---|---|---|
GET | /v1/cases | List cases |
GET | /v1/cases/total | Count cases matching filters |
GET | /v1/cases/{uuid} | Get a single case (by UUID or case_number) |
GET | /v1/cases/{uuid}/attorneys | List the attorneys on a case, with when and how each was attached |
GET | /v1/cases/{uuid}/assignments | List a case's assignment history |
GET | /v1/cases/{uuid}/pdf | Get a generated PDF of the case |
GET | /v1/attorneys/{uuid}/cases | List the cases an attorney is attached to |
DELETE | /v1/attorneys/{uuid} | Remove an attorney from the agency and from every case of the agency |
POST | /v1/cases | Create a case |
PATCH | /v1/cases/{uuid} | Update a case |
PUT | /v1/cases/{uuid} | Update a case (alias of PATCH) |
POST | /v1/cases/assign | Preview or run an assignment of attorneys, judges, or locations to many cases |
POST | /v1/cases/assign/{run}/undo | Undo an assignment run |
PUT | /v1/cases/{uuid}/archive | Archive a case |
PUT | /v1/cases/{uuid}/restore | Restore an archived or deleted case |
DELETE | /v1/cases/{uuid} | Delete a case (soft delete) |
The Case Object
| Field | Type | Description |
|---|---|---|
uuid | string | Unique identifier, generated when the case is created. |
client | string | UUID of the client the case belongs to. |
case_number | string | The case number. Required on create. |
status | string | Agency-defined case status. |
type | string | Agency-defined case type. |
charge_number | string | Charge number. |
codes | string | Statute or charge codes. |
description | string | Case description. |
offense_type | string | Offense type. |
offense_description | string | Offense description. |
offense_date | string | Offense date (YYYY-MM-DD). |
arrest_date | string | Arrest date (YYYY-MM-DD). |
arresting_agency | string | Arresting agency. |
arrest_tracking | string | Arrest tracking number. |
file_date | string | Filing date (YYYY-MM-DD). |
judgment_date | string | Judgment date (YYYY-MM-DD). |
warrant_status | string | Warrant status for the case. |
conditions | string | Case conditions. |
victim_name | string | Victim name. |
offender_name | string | Offender name. |
location | string | UUID of the location tied to the case. |
judge | string | UUID of the judge assigned to the case, or null. A case has one judge. |
attorneys | array | Every attorney on the case, oldest link first. On reads, each item is an object with uuid, first_name, last_name, firm_name, and bar_number. |
upload | string | UUID of the file import associated with the case, when linked to one. |
created_by | string | Email or UUID of the creator. |
created_at | string | Creation timestamp, UTC. |
updated_at | string | Last update timestamp, UTC. |
archived_at | string | Set when the case is archived, otherwise null. |
deleted_at | string | Set when the case is deleted, otherwise null. |
List and single-case responses include attorneys and judge unless slim is set. They also include attorney, the first item of attorneys, for older integrations. attorney is an empty array [] when the case has no attorney. Read attorneys in new integrations, because a case can have more than one attorney.
Primary attorney. attorneys is in attach order: by attach date, then by link row. The first item is the primary attorney of the case. The legacy attorney field is the primary attorney. The attorney merge tags in messages (for example [AttorneyName]) also use the primary attorney. Thus, when an attorney is added as co-counsel, the attorney that a message names does not change. When the primary attorney is removed, the next attorney in attach order becomes the primary attorney.
Create a Case
POST /v1/cases
Request:
curl -X POST "$BASE_URL/v1/cases" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"client": "{client_uuid}",
"case_number": "CASE-2024-001",
"type": "criminal",
"status": "open",
"offense_date": "2024-01-10",
"description": "Traffic violation"
}'
Response (201): the created case.
Create Behavior
case_number: required. A create without one returns400withCase number is required.client: attach the case to a client by UUID.attorneys: an optional array of attorney UUIDs (or objects with auuid) to attach to the case. A comma-separated string is also accepted. Each attorney must be one of the agency's attorneys. Otherwise the API returns422witherrors.attorneysset toChoose attorneys from this agency's list.and writes nothing. The response includes the attachedattorneysas UUIDs.judge: an optional judge UUID. The judge must belong to the agency and must not be archived or trashed. Otherwise the API returns422witherrors.judgeset toChoose a judge from this agency's list.- Each attorney, the judge, and the
locationare written to the case's assignment history with sourcemanual. - Creating a case with a client attached triggers a
case_createdauto message.
List Cases
GET /v1/cases
curl -X GET "$BASE_URL/v1/cases?client={client_uuid}&status=open&limit=50" \
-H "Authorization: Bearer $TOKEN"
Response (200): a JSON array of case objects.
Query Parameters
| Parameter | Description |
|---|---|
search | Partial match on case_number. |
case_number | Exact match on case number. |
client | Filter by client UUID. |
status | Exact match on status. |
type | Exact match on type. |
charge_number | Exact match on charge number. |
arresting_agency | Exact match on arresting agency. |
arrest_tracking | Exact match on arrest tracking number. |
offense_type | Exact match on offense type. |
arrest_date | Exact match on arrest date. |
offense_date | Exact match on offense date. |
file_date | Exact match on file date. |
judgment_date | Exact match on judgment date. |
description | Partial match on description. |
offense_description | Partial match on offense description. |
offender_name | Partial match on offender name. |
location | A location UUID. none returns cases with no location, and any returns cases with a location. |
judge | A judge UUID, or a comma-separated list of judge UUIDs. none returns cases with no judge, and any returns cases with a judge. A value that names no UUID (and is not none or any) matches no case. |
attorneys | A comma-separated list of attorney UUIDs: returns cases that have any of them. none returns cases with no attorney, and any returns cases with at least one attorney. A value that names no UUID (and is not none or any) matches no case. |
upload | Filter by the file import (upload) UUID that created the cases. |
created_by | Filter by the creator. |
GET /v1/cases/total accepts the same filters.
The attorneys and judge filters read the links in a second database. On some deployments, the API cannot read the links in the same query, so it lists the linked cases first. If that list has more than 50,000 cases, the API refuses the filter with 422. The error is on the filter field:
{
"message": "This attorney filter matches too many cases to run here. Choose a specific attorney, or clear the filter.",
"errors": {
"attorneys": ["This attorney filter matches too many cases to run here. Choose a specific attorney, or clear the filter."]
}
}
For judge, the field is errors.judge and the message is This judge filter matches too many cases to run here. Choose a specific judge, or clear the filter.
Pagination and shaping (limit, skip, sort, sortDirection, fields) follow the shared conventions.
Get a Case
GET /v1/cases/{uuid}
The path value accepts a case UUID or a case_number. Returns the case with its attorneys, judge, and legacy attorney. Returns 404 with a message of Case {uuid} not found when no case matches.
Update a Case
PATCH /v1/cases/{uuid}
PUT is an accepted alias. The path value accepts a UUID or a case_number. Send only the fields to change.
curl -X PATCH "$BASE_URL/v1/cases/{case_uuid}" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"status": "closed",
"judgment_date": "2024-04-01"
}'
Attorneys, Judge, and Location on Update
attorneys: the full list of attorney UUIDs the case must have. The API applies the list as a difference. An attorney already on the case keeps its link and its attach date. An attorney not yet on the case is added. An attorney missing from the list is removed. An empty array removes every attorney. Omitattorneysto leave the attorneys unchanged. The response includes theattorneysthat were sent.- Each attorney that the update adds must be one of the agency's attorneys. Attorneys already on the case are not checked. If the check fails, the API returns
422witherrors.attorneysset toChoose attorneys from this agency's list.and writes nothing. judge: a judge UUID sets the judge.""ornullclears it. Omitjudgeto leave it unchanged. An unknown, archived, or trashed judge returns422witherrors.judgeset toChoose a judge from this agency's list.The judge the case already has is accepted, even when that judge was archived later.location: a change is written to the assignment history as the old location removed and the new location added.
Each change has source manual in the assignment history. To add or remove one attorney without sending the full list, use POST /v1/cases/assign with a cases scope that holds the one case.
Attorneys
GET /v1/cases/{uuid}/attorneys
Returns every attorney on the case, oldest link first:
[
{
"uuid": "2c3d4e5f-6a7b-4c8d-9e0f-0a1b2c3d4e5f",
"first_name": "Lucia",
"last_name": "Alvarez",
"firm_name": "Alvarez Defense Group",
"bar_number": "IL-6291045",
"email": "lucia@alvarezdefense.com",
"attached_at": "2026-10-06 14:03:11",
"attached_by": "clerk@springfieldcourt.gov",
"source": "bulk"
}
]
attached_at: when the attorney was attached, UTC.attached_by: the user who attached the attorney (email, else name, else UUID), ornull.source: how the attorney was attached, ornull.manualis a case screen,POST /v1/cases, orPATCH /v1/cases/{uuid}.bulkis an assignment run.uploadis a file import.undois an undo that put back a link whose original details were not recorded.
Links made before the assignment history existed have attached_by and source set to null. Their attached_at is the date of the case's last save before that change, so treat it as "on or before" that date.
An Attorney's Cases
GET /v1/attorneys/{uuid}/cases
Returns the cases the attorney is attached to that are not archived or trashed, newest link first. limit (default 10, at most 100) and skip apply after the API removes archived and trashed cases. Thus each page is full until the list ends. Returns 404 with Attorney {uuid} not found when the attorney does not exist.
[
{
"uuid": "7a8b9c0d-1e2f-4a3b-8c4d-5e6f7a8b9c0d",
"case_number": "CR-2025-0117",
"status": "pending",
"type": "criminal",
"client": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"attached_at": "2026-10-06 14:03:11",
"attached_by": "clerk@springfieldcourt.gov",
"source": "manual"
}
]
attached_at, attached_by, and source have the same meaning as on GET /v1/cases/{uuid}/attorneys.
Remove an Attorney from the Agency
DELETE /v1/attorneys/{uuid}
Attorney records are shared between agencies. This request removes the agency from the attorney's list of agencies. It also removes the attorney from every case of the agency, as one run. Each removal is written to the assignment history of the case with source manual and the run UUID. The request needs the trash_attorneys permission. There is no request that adds the agency back to the attorney.
| Status | Body | When |
|---|---|---|
204 | No body. | The attorney was not attached to a case of the agency. |
200 | {"message": "Attorney removed from 2 cases", "run": "{run_uuid}", "detached": 2} | The attorney was removed from cases. The message says 1 case for one case. |
200 | {"message": "Attorney removed, but not from their cases: remove them from each case.", "run": null, "detached": 0} | The attorney was removed from the agency, but the API could not remove the attorney from the cases. Remove the attorney from each case. |
404 | {"message": "Attorney not found"} | No attorney matches. |
To put the links back, send the run UUID to POST /v1/cases/assign/{run}/undo. The undo restores each link with its original attach date, attached by, and source. The undo does not add the agency back to the attorney.
Assign Cases in Bulk
POST /v1/cases/assign
Adds or removes attorneys, and sets or clears the judge or location, on many cases in one request. The request runs synchronously and returns 200 with the result. Send "dry_run": true to preview: the API computes the same result and writes nothing. A preview and a run use the same code and the same case query as GET /v1/cases. When the cases do not change in between, the run gets the counts of the preview. The request needs the update_events permission.
Request:
curl -X POST "$BASE_URL/v1/cases/assign" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"scope": { "filters": { "status": "pending", "type": "criminal", "attorneys": "none" } },
"attorneys": {
"mode": "fill",
"uuids": ["2c3d4e5f-6a7b-4c8d-9e0f-0a1b2c3d4e5f", "3d4e5f6a-7b8c-4d9e-8f0a-1b2c3d4e5f6a"],
"spread": true,
"max_cases": 150
},
"judge": { "mode": "fill", "uuids": ["4e5f6a7b-8c9d-4e0f-9a1b-2c3d4e5f6071"] },
"notify_attorneys": true,
"dry_run": true
}'
Request Body
| Field | Type | Description |
|---|---|---|
scope | object | Required. Either {"cases": [...]} or {"filters": {...}}, not both. |
scope.cases | array | 1 to 2,000 case UUIDs. Trashed cases are left out. Archived cases are included. |
scope.filters | object | The query parameters of GET /v1/cases (for example status, type, location, judge, attorneys, archived). The API selects the cases with the same query as GET /v1/cases, with one difference: an assignment never reaches trashed cases. Filters that ask for trashed cases (deleted, else trashed, set to true or deleted) are refused with 422 on scope, for a preview and for a run. Send archived=true to reach archived cases. limit, skip, sort, sortDirection, slim, and fields are ignored. |
attorneys | object | Optional. {mode, uuids, spread, max_cases}. Omit it, or send null, to leave attorneys unchanged. |
judge | object | Optional. {mode, uuids, spread, max_cases}. Omit it to leave the judge unchanged. |
location | object | Optional. {mode, uuids, spread, max_cases}. Omit it to leave the location unchanged. |
notify_attorneys | boolean | Default false. On a run, email each attorney one list of the cases newly added to them. Ignored on a preview. |
dry_run | boolean | Default false. true previews and writes nothing. |
source | string | bulk (default) or manual. Use manual when the request changes one case from a case screen. The value is recorded in the assignment history. |
email_report | boolean | Default true. After a run with source bulk that changes at least one case, email a PDF report to the signed-in user. Ignored on a preview and when source is manual. See Completion Report. |
At least one of attorneys, judge, or location is required.
Section Fields
| Field | Description |
|---|---|
mode | For attorneys: add, replace, fill, or remove. For judge and location: replace, fill, or clear. |
uuids | 1 to 50 unique UUIDs of the agency's own records. An attorney must list the agency in its agencies. A judge or location must belong to the agency and must not be archived or trashed. clear takes no UUIDs. remove needs the attorneys to remove, and they do not have to be the agency's own. |
spread | Default false. With true, each case gets one of the chosen records instead of all of them. A judge or location section with two or more UUIDs must set spread to true, because a case has one judge and one location. remove does not accept spread. |
max_cases | null or a whole number of 1 or more. Accepted only with spread. A record whose caseload has reached this number is passed over. The caseload is the open cases linked to the record, plus the changes of this run. A case with no candidate left is skipped and counted as at_limit. |
Modes
| Mode | Section | Effect on each case in scope |
|---|---|---|
add | attorneys | Adds the chosen attorneys. Attorneys already on the case stay. |
replace | attorneys | Adds the chosen attorneys and removes every other attorney. A chosen attorney already on the case keeps its link and attach date. |
fill | attorneys | Adds the chosen attorneys only to a case with no attorney. Skips a case that has one. |
remove | attorneys | Removes the chosen attorneys. Other attorneys stay. |
replace | judge, location | Sets the chosen value, whatever the case had. |
fill | judge, location | Sets the chosen value only on a case with none. |
clear | judge, location | Removes the value. |
Spread and Order
The API takes the cases in case_number order, then uuid order. With spread, each case gets the candidate with the smallest caseload. The caseload counts only open cases: cases that exist and are neither archived nor trashed. It includes the open cases already linked to the candidate and the changes of this run so far. An archived case in the scope of the run counts for no candidate. A location's caseload is its open cases. A tie goes to the candidate sent first in uuids, so the order of uuids is part of the request. The result is the same for the same request and the same data, so a preview predicts the run.
Limits
- A run reaches at most 2,000 cases.
- A
casesscope with more than 2,000 entries returns422witherrors["scope.cases"]set toCheck at most 2,000 cases.This applies to a preview and to a run. - A
filtersscope that matches more than 2,000 cases on a preview returns200withover_limit: trueand every section count at zero. - A
filtersscope that matches more than 2,000 cases on a run returns422witherrors.scope.
Response (200)
A preview and a run return the same shape. This is the response to the request above, sent again with "dry_run": false:
{
"run": "5f6a7b8c-9d0e-4f1a-8b2c-3d4e5f6a7b8c",
"dry_run": false,
"cases": 340,
"limit": 2000,
"over_limit": false,
"changed": 262,
"attorneys": {
"mode": "fill",
"cases": 200,
"added": 200,
"removed": 0,
"skipped": { "has_attorney": 0, "at_limit": 140, "already_linked": 0, "not_linked": 0 },
"by_record": [
{ "uuid": "2c3d4e5f-6a7b-4c8d-9e0f-0a1b2c3d4e5f", "added": 110, "removed": 0 },
{ "uuid": "3d4e5f6a-7b8c-4d9e-8f0a-1b2c3d4e5f6a", "added": 90, "removed": 0 }
]
},
"judge": {
"mode": "fill",
"cases": 90,
"set": 90,
"cleared": 0,
"skipped": { "has_value": 250, "at_limit": 0, "unchanged": 0 },
"by_record": [
{ "uuid": "4e5f6a7b-8c9d-4e0f-9a1b-2c3d4e5f6071", "added": 90, "removed": 0 }
]
},
"location": null,
"notified": 2,
"report": "queued"
}
In this example, the two attorneys already had 40 and 60 open cases. The cap of 150 let them take 110 and 90 more, and the other 140 cases were skipped as at_limit.
| Field | Description |
|---|---|
run | The UUID of the run. Pass it to the undo endpoint. null on a preview. |
dry_run | true for a preview. |
cases | The number of cases in scope. |
limit | The most cases a run can reach (2000). |
over_limit | true when cases is more than limit. |
changed | The number of cases where at least one thing changed. |
attorneys, judge, location | What each section did, or null when the request did not include it. |
notified | The number of attorney emails accepted. Always 0 on a preview. |
report | queued when the API queued the completion report, failed when it could not queue the report, or null when no report was due. |
Each section has mode, cases, and by_record. cases is the number of cases that the section changed. by_record lists each chosen UUID in request order, with the number of cases that gained it (added) or lost it (removed). The attorneys section counts links in added and removed. Attorneys that replace removes are counted in removed, and they are not listed in by_record unless they were also chosen. The judge and location sections count cases in set and cleared.
| Skipped count | Meaning |
|---|---|
has_attorney | fill: the case already had an attorney. |
already_linked | add or replace: the chosen attorney was already on the case. A case where every chosen attorney already was is not counted in changed. |
not_linked | remove: the attorney was not on the case. |
has_value | fill: the case already had a judge or location. |
unchanged | replace: the case already had that judge or location. |
at_limit | spread with max_cases: every candidate had reached the cap. |
Attorney Email
When notify_attorneys is true on a run, the API sends an email to each attorney that got new cases. An attorney with no valid email is skipped. Each attorney gets one email for the run, not one email per case. The subject is the agency setting attorney_notification_subject, or Cases Assigned when that setting is blank. The sender is attorney_notification_from_email when it is a valid email. The body says how many cases were assigned, then lists the case number, status, type, and description of each.
The API sends the emails after it saves the run. If the emails fail, the run stays saved and the response is still 200 with the run UUID, so the run can be undone. notified counts only the emails that were accepted.
Completion Report
After a run, the API queues a PDF report for the signed-in user when all of these are true:
dry_runisfalseandsourceisbulk.email_reportistrueor not sent.changedis more than 0.- The signed-in user has an email address.
A worker builds the PDF and emails it to that address only. The subject is Case Assignment Complete: N cases changed. The PDF lists who ran the run and when, the scope and its filters, the request, the totals and skipped counts, the changes for each attorney, judge, and location, and every changed case. For each changed case, it shows the attorneys added and removed, and the judge and location before and after.
report in the response is queued when the job was queued and null when no report was due. It is failed when the API could not queue the job. A failed report does not fail the request: the run stays saved and the response is still 200. A change to one case with source manual, and the removal of an attorney from the agency, never send a report.
Errors
A request that fails validation returns 422. message holds the first problem, and errors holds every problem by field path:
{
"message": "Choose at least one attorney, judge or location to assign.",
"errors": {
"assign": ["Choose at least one attorney, judge or location to assign."]
}
}
{
"message": "This reaches 4,210 cases. A run can change at most 2,000: narrow the filters or check the cases to assign.",
"errors": {
"scope": ["This reaches 4,210 cases. A run can change at most 2,000: narrow the filters or check the cases to assign."]
}
}
{
"message": "Assignments do not apply to trashed cases.",
"errors": {
"scope": ["Assignments do not apply to trashed cases."]
}
}
| Field path | Message |
|---|---|
scope | Choose the cases to assign: check them in the list, or send the filters the list is showing. or Assignments do not apply to trashed cases. (the filters ask for trashed cases). |
scope.cases | Check at least one case to assign., Check at most 2,000 cases., or Each checked case must be a case uuid. |
scope.filters | Send the filters as the cases list sends them to GET /cases. |
scope.filters.attorneys, scope.filters.judge | This attorney filter matches too many cases to run here. Choose a specific attorney, or clear the filter. or This judge filter matches too many cases to run here. Choose a specific judge, or clear the filter. The API could not list the linked cases for the filter (more than 50,000). |
attorneys.mode | Choose how to assign attorneys: add, replace, fill or remove. |
judge.mode, location.mode | Choose how to assign the judge: replace, fill or clear. (or the location) |
{section}.uuids | Choose at least one attorney., Choose each attorney once., Choose at most 50 attorneys., or Choose attorneys from this agency's list. (with the noun of the section) |
{section}.uuids | 1 chosen attorney is not in this agency's list. or 2 chosen judges are archived or not in this agency's list. |
judge.spread, location.spread | A case has one judge: to assign several, spread the cases across them. |
attorneys.spread | Spread applies when assigning attorneys, not when removing them. |
{section}.max_cases | The most cases must be a whole number of 1 or more. or Set the most cases only when spreading the cases across the attorneys. |
attorneys, judge, location | Send attorneys as an object with a mode and uuids. (with the noun of the section) |
notify_attorneys, dry_run, email_report, {section}.spread | Notify attorneys must be true or false., Dry run must be true or false., Email report must be true or false., or Spread must be true or false. |
source | Source must be bulk or manual. |
An unexpected failure returns 500 with The assignment could not be saved. Try again.
Earlier Request Body
Before October 2026, this endpoint queued a background job and returned 202. That job no longer exists. The API still accepts the earlier body: a body with no scope and with attorney, judge, or location as a string. The API maps it to the current shape and runs it at once (it never previews):
- The scope is the cases that match
typeandstatus. - A UUID becomes
replacewith that one record. randombecomesreplaceover all of the agency's records withspread: true, andmax_per_attorney,max_per_judge, ormax_per_locationbecomesmax_cases. A blank, zero, or negative value means no cap.- A blank value leaves that section unchanged.
notify_attorneybecomesnotify_attorneys.
The 2,000 case limit applies. The response is the 200 result above. New integrations must send the current shape.
Undo an Assignment Run
POST /v1/cases/assign/{run}/undo
Reverses one run. {run} is the run UUID from the assign response. The API reads the changes of that run, newest first, and reverses a change only when the case still holds what the run left:
- An attorney the run added is removed, if it is still on the case.
- An attorney the run removed is put back, if it is not on the case now. The link gets its original attach date, attached by, and source when those were recorded.
- A judge or location that the run set goes back to the previous value. This occurs only if the case still holds the value that the run set.
A change the case no longer holds is left as it is and counted in kept. The reversals are written to the assignment history with source undo and a new run UUID. The request needs the update_events permission.
Request body (optional):
{ "email_report": true }
email_report defaults to true. A value that is not a boolean returns 422 with errors.email_report set to Email report must be true or false. The API queues a PDF report of the undo for the signed-in user only when email_report is true, the undo reversed at least one change, and the undone run was a bulk run. The subject is Case Assignment Undone: N changes reversed. An undo of a manual change or of an attorney removal never sends a report.
Response (200):
{
"run": "6a7b8c9d-0e1f-4a2b-9c3d-4e5f6a7b8c9d",
"undone": "5f6a7b8c-9d0e-4f1a-8b2c-3d4e5f6a7b8c",
"reversed": 180,
"kept": 4,
"report": "queued"
}
report has the same values as on POST /v1/cases/assign: queued, failed, or null.
Returns 404 with Assignment run {run} not found when the agency has no changes for that run. A second undo of the same run reverses nothing, and the counts show that.
Assignment History
GET /v1/cases/{uuid}/assignments
Returns every change to the attorneys, judge, and location of a case, newest first. limit defaults to 50, with a maximum of 100. skip pages through older changes. Changes come from case saves, assignment runs, file imports, and undos. The request needs the view_any_event permission.
[
{
"id": 991,
"kind": "attorney",
"record": "2c3d4e5f-6a7b-4c8d-9e0f-0a1b2c3d4e5f",
"name": "Lucia Alvarez (Alvarez Defense Group)",
"action": "added",
"source": "bulk",
"run": "5f6a7b8c-9d0e-4f1a-8b2c-3d4e5f6a7b8c",
"created_by": "clerk@springfieldcourt.gov",
"created_at": "2026-10-06 14:03:11"
}
]
| Field | Description |
|---|---|
kind | attorney, judge, or location. |
record | The UUID of the attorney, judge, or location. |
name | The record's name. An attorney reads as first and last name with the firm in parentheses, else the firm, else the bar number. null when the record no longer exists. |
action | added or removed. |
source | manual, bulk, upload, or undo. |
run | The run UUID for a change made by an assignment run or an undo. null for a single change. |
created_by | The user who made the change (email, else name, else UUID), or null. |
created_at | When the change was made, UTC. |
Archive, Restore, and Delete
PUT /v1/cases/{uuid}/archive
PUT /v1/cases/{uuid}/restore
DELETE /v1/cases/{uuid}
- Archive sets
archived_atand removes the case from default lists. Returns{"message": "Case archived"}, or404withCase not found. - Delete is a soft delete: it sets
deleted_at. Returns a204with no body. - Restore clears
archived_atanddeleted_at. Returns{"message": "Case restored"}, or404withCase not found.
The agency setting Auto Delete Trashed Data deletes trashed cases permanently after its retention period. When a case is deleted this way, its attorney links, its judge, and its assignment history are deleted with it.
FAQs
How does a case relate to events, payments, and messages?
Events and payments carry a case_number, and the case groups the records that share it. Set the same case_number on an event or payment as on the case to associate them, and filter events or payments by case_number. A message links to a case by its UUID through the message case field, so filter messages with ?case={case_uuid} rather than by case number.
How do I attach a case to a client?
Set the client field to the client's UUID on create. A case with a client attached also triggers a case_created auto message.
How do I look up a case by its case number?
Pass the case number in the path of a get or update (GET /v1/cases/CASE-2024-001), or filter the list with GET /v1/cases?case_number=CASE-2024-001. Use search for a partial match.
How do I assign an attorney to a case?
Send POST /v1/cases/assign with "scope": {"cases": ["{case_uuid}"]} and "attorneys": {"mode": "add", "uuids": ["{attorney_uuid}"]}. Use "mode": "remove" to take the attorney off. This changes only that attorney. An attorneys array on create or update also works. On update, the array sets the full list, so an attorney not in the array is removed. Read the result with GET /v1/cases/{uuid}/attorneys.
How do I preview a bulk assignment before it runs?
Send the same body with "dry_run": true. The response has the same counts the run would produce and run is null. Send the body again with "dry_run": false to run it.
How do I find cases with no attorney or no judge?
Filter with attorneys=none or judge=none on GET /v1/cases or GET /v1/cases/total. Use location=none for cases with no location. The same filters work in the filters scope of an assignment.
Why did creating a case return a 400?
case_number is required. A create without it returns 400 with Case number is required.
Related Documentation
- API Conventions: shared listing, filtering, and lifecycle behavior
- Clients: the person a case belongs to
- Events: hearings and dates that share a case number
- Payments: balances that share a case number
- Error Handling: status codes and error responses