La documentación del producto está disponible solo en inglés.
Matters endpoints
A matter is one case or engagement at your firm. These endpoints list, create, read, update, and delete matters, with their clients and team members.
All paths sit under the base address https://api.esqase.com, and every request needs an API key. See Authentication and scopes if you have not made your first call yet.
The endpoints
| Action | Method and path | Scope needed |
|---|---|---|
| List | GET /v1/matters | matters:read |
| Create | POST /v1/matters | matters:write |
| Retrieve | GET /v1/matters/{id} | matters:read |
| Update | PATCH /v1/matters/{id} | matters:write |
| Delete | DELETE /v1/matters/{id} | matters:write |
The matter object
A single-record read, and a create or update response, also carry customFields and unsupportedCustomFields, described in Custom fields in the API. List entries never carry those two properties.
id: the id Esqase assigns. Use it in every other path.publicId: the human-readable matter number your firm's numbering settings produce, such asM-0042. Esqase assigns it; you never send one.title,description: the matter's title and free-text notes.descriptionisnullwhen unset.status:ACTIVE,DRAFT, orARCHIVE.openDate,closeDate,solDate: dates written asYYYY-MM-DD, ornull.solDateis the statute-of-limitations date.practiceArea: an object with the practice area'sidandname, ornull.stage: an object with the stage'sidandname, ornull. This is the stage the matter currently sits in on its practice area board.clients: a list of the matter's clients. Each entry hascontactId,name,isPrimary, andisBillRecipient.team: a list of the matter's team members. Each entry hasmemberId,role,allocation, andname.createdAt,updatedAt: timestamps.
Note: A matter always belongs to exactly one practice area and sits in exactly one of that practice area's stages. Get those ids from Practice areas endpoints before you create a matter.
List matters
GET /v1/matters returns your matters a page at a time. The order is not specified, so do not rely on it. Two query parameters control the page:
limit: how many records to return. Minimum 1, maximum 100, default 20.offset: how many records to skip before the page starts. Minimum 0, default 0.
curl "https://api.esqase.com/v1/matters?limit=1&offset=0" \
-H "Authorization: Bearer $ESQASE_API_KEY"
{
"data": [
{
"id": "c3d4e5f6-0001-4a1b-9c2d-1234567890ab",
"publicId": "M-0042",
"title": "Doe v. Acme Holdings",
"description": null,
"status": "ACTIVE",
"openDate": "2026-07-01",
"closeDate": null,
"solDate": null,
"practiceArea": {
"id": "b2c3d4e5-1000-4a1b-9c2d-1234567890ab",
"name": "Family law"
},
"stage": {
"id": "b2c3d4e5-2000-4a1b-9c2d-1234567890ab",
"name": "To do"
},
"clients": [
{
"contactId": "a1b2c3d4-0001-4a1b-9c2d-1234567890ab",
"name": "Jane Doe",
"isPrimary": true,
"isBillRecipient": true
}
],
"team": [
{
"memberId": "f6a7b8c9-0001-4a1b-9c2d-1234567890ab",
"role": "RESPONSIBLE_ATTORNEY",
"allocation": 100,
"name": "Alex Reyes"
}
],
"createdAt": "2026-07-01T14:40:00.000Z",
"updatedAt": "2026-07-01T14:40:00.000Z"
}
],
"pagination": { "limit": 1, "offset": 0, "total": 57 }
}
Create a matter
POST /v1/matters creates one matter and returns 201 Created with the stored record, timestamps included.
Required:
practiceAreaId: the practice area the matter belongs to.openDate: the date the matter opened, written asYYYY-MM-DD.
Optional:
practiceAreaStageColumnId: the stage the matter starts in. Leave it out and Esqase uses the practice area's first stage. If the practice area has no stages at all, the request is rejected with 400.title: up to 255 characters. If you leave it out or send an empty string, the matter is titledUntitled matter.description: free-text notes. The API applies no length limit of its own.closeDate,solDate: moreYYYY-MM-DDdates.status:ACTIVEorDRAFT. Defaults toACTIVE. You cannot create a matter directly asARCHIVE; archive it with an update instead.clientContactIds: a list of contact ids to attach as clients, up to 50 entries. The first one becomes the primary client and the bill recipient.team: a list of team members, up to 50 entries. Each entry takes:memberId(required): the firm member's id.role(optional):ORIGINATING_ATTORNEY,RESPONSIBLE_ATTORNEY, orRESPONSIBLE_STAFF. Defaults toRESPONSIBLE_ATTORNEY.allocation(optional): that member's share of the matter as a percentage, from 0 to 100. Defaults to 0.
curl -X POST https://api.esqase.com/v1/matters \
-H "Authorization: Bearer $ESQASE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"practiceAreaId": "b2c3d4e5-1000-4a1b-9c2d-1234567890ab",
"practiceAreaStageColumnId": "b2c3d4e5-2000-4a1b-9c2d-1234567890ab",
"openDate": "2026-07-01",
"title": "Doe v. Acme Holdings",
"clientContactIds": ["a1b2c3d4-0001-4a1b-9c2d-1234567890ab"],
"team": [
{
"memberId": "f6a7b8c9-0001-4a1b-9c2d-1234567890ab",
"role": "RESPONSIBLE_ATTORNEY",
"allocation": 100
}
]
}'
{
"data": {
"id": "c3d4e5f6-0001-4a1b-9c2d-1234567890ab",
"publicId": "M-0042",
"title": "Doe v. Acme Holdings",
"description": null,
"status": "ACTIVE",
"openDate": "2026-07-01",
"closeDate": null,
"solDate": null,
"practiceArea": {
"id": "b2c3d4e5-1000-4a1b-9c2d-1234567890ab",
"name": "Family law"
},
"stage": {
"id": "b2c3d4e5-2000-4a1b-9c2d-1234567890ab",
"name": "To do"
},
"clients": [
{
"contactId": "a1b2c3d4-0001-4a1b-9c2d-1234567890ab",
"name": "Jane Doe",
"isPrimary": true,
"isBillRecipient": true
}
],
"team": [
{
"memberId": "f6a7b8c9-0001-4a1b-9c2d-1234567890ab",
"role": "RESPONSIBLE_ATTORNEY",
"allocation": 100,
"name": "Alex Reyes"
}
],
"customFields": {},
"unsupportedCustomFields": [],
"createdAt": "2026-07-01T14:40:00.000Z",
"updatedAt": "2026-07-01T14:40:00.000Z"
}
}
Important: If your firm has not finished its matter numbering setup, POST /v1/matters returns 409 with the code not_configured. An owner or administrator fixes that once in Settings, then Firm, and every later call works.
Retrieve a matter
GET /v1/matters/{id} returns one matter in the shape above, plus the two custom field properties described in Custom fields in the API. List entries never carry those two properties.
curl https://api.esqase.com/v1/matters/c3d4e5f6-0001-4a1b-9c2d-1234567890ab \
-H "Authorization: Bearer $ESQASE_API_KEY"
If no matter in your firm has that id, you get 404 with the code not_found.
Update a matter
PATCH /v1/matters/{id} changes the fields you send and leaves the rest alone. Every field is optional.
title: up to 255 characters.description: send text to change it, ornullto clear it.practiceAreaId,practiceAreaStageColumnId: move the matter to a different practice area or stage. Send both when you move the matter to another practice area, since a stage belongs to one practice area.openDate: aYYYY-MM-DDdate.closeDate,solDate: aYYYY-MM-DDdate, ornullto clear.status:ACTIVE,DRAFT, orARCHIVE. This is how you archive a matter or bring it back.
Clients and team members are not editable through the update endpoint. Set them when you create the matter, or manage them in the dashboard.
curl -X PATCH https://api.esqase.com/v1/matters/c3d4e5f6-0001-4a1b-9c2d-1234567890ab \
-H "Authorization: Bearer $ESQASE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"status": "ARCHIVE",
"closeDate": "2026-09-30"
}'
The response is the reloaded matter, so it reflects the change:
{
"data": {
"id": "c3d4e5f6-0001-4a1b-9c2d-1234567890ab",
"publicId": "M-0042",
"title": "Doe v. Acme Holdings",
"description": null,
"status": "ARCHIVE",
"openDate": "2026-07-01",
"closeDate": "2026-09-30",
"solDate": null,
"practiceArea": {
"id": "b2c3d4e5-1000-4a1b-9c2d-1234567890ab",
"name": "Family law"
},
"stage": {
"id": "b2c3d4e5-2000-4a1b-9c2d-1234567890ab",
"name": "To do"
},
"clients": [
{
"contactId": "a1b2c3d4-0001-4a1b-9c2d-1234567890ab",
"name": "Jane Doe",
"isPrimary": true,
"isBillRecipient": true
}
],
"team": [
{
"memberId": "f6a7b8c9-0001-4a1b-9c2d-1234567890ab",
"role": "RESPONSIBLE_ATTORNEY",
"allocation": 100,
"name": "Alex Reyes"
}
],
"customFields": {},
"unsupportedCustomFields": [],
"createdAt": "2026-07-01T14:40:00.000Z",
"updatedAt": "2026-09-30T09:12:00.000Z"
}
}
Delete a matter
DELETE /v1/matters/{id} is a soft delete: the matter is archived out of the active lists rather than erased, exactly as deleting it in the dashboard does.
curl -X DELETE https://api.esqase.com/v1/matters/c3d4e5f6-0001-4a1b-9c2d-1234567890ab \
-H "Authorization: Bearer $ESQASE_API_KEY"
{
"data": {
"id": "c3d4e5f6-0001-4a1b-9c2d-1234567890ab",
"deleted": true
}
}
Custom fields
Matters accept your firm's matter custom fields through an optional customFields object on create and update, and single-record reads return the values back. See Custom fields in the API for the full rules.
curl -X PATCH https://api.esqase.com/v1/matters/c3d4e5f6-0001-4a1b-9c2d-1234567890ab \
-H "Authorization: Bearer $ESQASE_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "customFields": { "opposing_counsel": "Ramirez & Co." } }'
Full examples in JavaScript and Python
Creating a matter usually takes two calls: read a practice area to get one of its stage ids, then create the matter with that practice area, that stage, and a client contact. Both examples read the key from an environment variable and assume you already have a client contact id.
JavaScript (fetch)
const BASE = "https://api.esqase.com/v1";
const headers = {
Authorization: `Bearer ${process.env.ESQASE_API_KEY}`,
"Content-Type": "application/json",
};
// 1. Read a practice area to get one of its stage ids.
const practiceAreaId = "b2c3d4e5-1000-4a1b-9c2d-1234567890ab";
const paResponse = await fetch(`${BASE}/practice-areas/${practiceAreaId}`, { headers });
const { data: practiceArea } = await paResponse.json();
const stageId = practiceArea.stages[0].id;
// 2. Create a matter in that practice area and stage.
const clientContactId = "a1b2c3d4-0001-4a1b-9c2d-1234567890ab";
const matterResponse = await fetch(`${BASE}/matters`, {
method: "POST",
headers,
body: JSON.stringify({
practiceAreaId,
practiceAreaStageColumnId: stageId,
openDate: "2026-07-01",
title: "Doe v. Acme Holdings",
clientContactIds: [clientContactId],
}),
});
const { data: matter } = await matterResponse.json();
console.log(`Created matter ${matter.publicId}`);
// 3. Archive it later.
await fetch(`${BASE}/matters/${matter.id}`, {
method: "PATCH",
headers,
body: JSON.stringify({ status: "ARCHIVE", closeDate: "2026-09-30" }),
});
Python (requests)
import os
import requests
BASE = "https://api.esqase.com/v1"
headers = {
"Authorization": f"Bearer {os.environ['ESQASE_API_KEY']}",
"Content-Type": "application/json",
}
# 1. Read a practice area to get one of its stage ids.
practice_area_id = "b2c3d4e5-1000-4a1b-9c2d-1234567890ab"
practice_area = requests.get(
f"{BASE}/practice-areas/{practice_area_id}", headers=headers
).json()["data"]
stage_id = practice_area["stages"][0]["id"]
# 2. Create a matter in that practice area and stage.
client_contact_id = "a1b2c3d4-0001-4a1b-9c2d-1234567890ab"
matter = requests.post(
f"{BASE}/matters",
headers=headers,
json={
"practiceAreaId": practice_area_id,
"practiceAreaStageColumnId": stage_id,
"openDate": "2026-07-01",
"title": "Doe v. Acme Holdings",
"clientContactIds": [client_contact_id],
},
).json()["data"]
print(f"Created matter {matter['publicId']}")
# 3. Archive it later.
requests.patch(
f"{BASE}/matters/{matter['id']}",
headers=headers,
json={"status": "ARCHIVE", "closeDate": "2026-09-30"},
)
Errors
| Status | Code | When |
|---|---|---|
| 400 | invalid_request | A field is the wrong type, a date is not YYYY-MM-DD, the practice area has no stage and you sent no practiceAreaStageColumnId, or an update would leave the matter without a practice area or stage. |
| 400 | invalid_custom_field | Something in the customFields object could not be written. See Custom fields in the API. |
| 401 | unauthorized | The key is missing, wrong, revoked, or expired. |
| 403 | forbidden | The key is missing matters:read or matters:write, or the member who created it lacks the matching permission. |
| 404 | not_found | No matter in your firm has that id. |
| 409 | not_configured | The firm has no matter numbering settings yet. |
| 429 | rate_limited | Too many requests. Wait the Retry-After seconds. |
The full list of status codes is in Authentication and scopes.
Common questions
- Where do practice area and stage ids come from? Read a practice area (
GET /v1/practice-areas/{id}); itsstageslist gives you every stage id. See Practice areas endpoints. - Where do member ids come from? From your firm's members in the dashboard. The API does not expose a members endpoint.
- Can I set the matter number myself? No.
publicIdcomes from your firm's numbering settings. - Can I add a client to an existing matter? Not through the API. Clients and team are set when the matter is created; change them in the dashboard afterwards.
- How do I archive a matter? Send
{ "status": "ARCHIVE" }on the update endpoint. Send{ "status": "ACTIVE" }to bring it back.