DocumentationBrowse
Matters
A matter is a session that stays open. It holds the conversation so far and the case files that belong to it. Pass its id on a chat request and the API prepends the stored turns, narrows case-file retrieval to the attached files, and stores the new exchange when the model answers. Close the laptop, come back next week, send the next question with the same id.
Matters are scoped to the same owner as case files: the account behind a key, or the key itself when it was minted outside an account. Nothing about a matter is visible to any other owner.
POST /matters
| Field | Type | Notes |
|---|---|---|
title | string, optional | 1 to 200 characters. Defaults to Untitled matter. |
{
"id": "9b1c2e4a-0f6d-4b7e-9a21-6c3d1e8f5a70",
"title": "Smith v. Jones",
"created_at": "2026-08-27T18:24:34.235Z",
"updated_at": "2026-08-27T18:24:34.235Z",
"archived_at": null,
"message_count": 0,
"file_count": 0
}GET /matters
| Query | Default | Meaning |
|---|---|---|
archived | false | Archived matters are hidden unless you pass archived=true. |
limit | 100 | 1 to 500. |
{
"data": [
{ "id": "9b1c2e4a-…", "title": "Smith v. Jones",
"created_at": "2026-08-27T18:24:34.235Z", "updated_at": "2026-08-27T19:02:11.804Z",
"archived_at": null, "message_count": 6, "file_count": 2 }
]
}Ordered by updated_at descending. Any stored exchange, rename, attach or detach bumps it, so the list reads as "what did I touch last".
GET /matters/:id
| Query | Default | Meaning |
|---|---|---|
limit | 50 | Messages per page, 1 to 200. |
before | none | A message id. Returns the page of messages older than it. Pass the smallest id you already hold to walk backwards. |
{
"id": "9b1c2e4a-…", "title": "Smith v. Jones", "created_at": "…", "updated_at": "…", "archived_at": null,
"message_count": 4, "file_count": 1,
"messages": [ // chronological, oldest first
{ "id": 41, "matter_id": "9b1c2e4a-…", "role": "user",
"content": "What does the tolling agreement in my case file say?",
"request_id": null, "verification": null, "created_at": "…" },
{ "id": 42, "matter_id": "9b1c2e4a-…", "role": "assistant",
"content": "The agreement executed March 3, 2024 tolls …",
"request_id": "req_4f1e…", // the chat request that produced this turn
"verification": { "checked": 2, "fabricated": [], "revised": false, … },
"created_at": "…" },
…
],
"has_more": false, // true when an older page exists
"files": [
{ "file_id": "cd044ac7-…", "name": "smith-v-jones-tolling.txt", "chars": 2410, "chunks": 2, "attached_at": "…" }
]
}Message ids are integers that only ever increase within your account, which is what makes them a stable cursor. A user turn carries no request_id; the assistant turn that answered it carries the request id and the verification report from that call. A file deleted after it was attached still appears in files withname set to null; detach it or leave it, it no longer affects retrieval either way.
PATCH /matters/:id
| Field | Type | Meaning |
|---|---|---|
title | string | 1 to 200 characters. |
archived | boolean | Sets or clears archived_at. An archived matter still answers chat requests; it is only hidden from the default list. |
At least one of the two must be present. Returns the updated matter with counts.
DELETE /matters/:id
{ "ok": true, "id": "9b1c2e4a-…" }Removes the matter, every stored message and every attachment row. The files themselves are not touched; delete those through DELETE /files/:id if you want them gone.
Attaching files
{ "file_id": "cd044ac7-85ec-4430-b37b-1bf6a966cf5f" }{
"ok": true, "matter_id": "9b1c2e4a-…", "file_id": "cd044ac7-…",
"files": [ { "file_id": "cd044ac7-…", "name": "smith-v-jones-tolling.txt", "chars": 2410, "chunks": 2, "attached_at": "…" } ]
}The file must be yours. Attaching is idempotent. Detach with DELETE /matters/:id/files/:file_id, which returns { "ok": true }, or 404 when the file was not attached.
With case_file: true and no attachments, a request on a matter searches every file you own, exactly as it would without a matter. Attach one or more files and the search is confined to them. That is the whole point of attaching: opposing counsel's exhibit in one matter never surfaces in another.
Asking with matter_id
Add docketrouter.matter_id to a chat completion. Three things happen:
| Step | What the API does |
|---|---|
| Before the model | The last 20 stored turns are placed in front of your messages. Your messages always come last. |
| Retrieval | When case_file is true and the matter has attachments, only those files are searched. |
| After a successful answer | Your non-system messages from this request and the assistant's answer are appended, with the request_id and the verification report. Nothing is stored when the request fails. |
"docketrouter": {
"matter_id": "9b1c2e4a-…", // echoed; null when the request carried none
"request_id": "req_4f1e…",
"verification": { … },
…
}The API stores every non-system message you send on a matter request. If you keep sending the whole transcript, OpenAI style, every turn is stored again on every call and the history doubles. On a matter, send the system prompt if you use one and the new user message, nothing else.
Streamed requests store the exchange after the stream completes. If the connection drops mid-stream nothing is stored, so a retry does not leave a half answer in the history.
The full workflow
# 1. create a matter
MATTER=$(curl -s https://docketrouter.ai/api/v1/matters \
-H "Authorization: Bearer dr-…" -H "content-type: application/json" \
-d '{ "title": "Smith v. Jones" }' | jq -r .id)
# 2. attach a file you uploaded earlier (POST /files)
curl -s https://docketrouter.ai/api/v1/matters/$MATTER/files \
-H "Authorization: Bearer dr-…" -H "content-type: application/json" \
-d '{ "file_id": "cd044ac7-…" }'
# 3. ask; only the new turn goes in messages
curl -s https://docketrouter.ai/api/v1/chat/completions \
-H "Authorization: Bearer dr-…" -H "content-type: application/json" \
-d "{ \"model\": \"deepseek/deepseek-v4-flash\", \"max_tokens\": 1200,
\"messages\": [{ \"role\": \"user\", \"content\": \"What does the tolling agreement say about limitations?\" }],
\"docketrouter\": { \"case_file\": true, \"matter_id\": \"$MATTER\" } }" \
| jq '{answer: .choices[0].message.content, matter: .docketrouter.matter_id}'
# 4. next week: continue. The earlier exchange is already in front of this message.
curl -s https://docketrouter.ai/api/v1/chat/completions \
-H "Authorization: Bearer dr-…" -H "content-type: application/json" \
-d "{ \"model\": \"deepseek/deepseek-v4-flash\", \"max_tokens\": 1200,
\"messages\": [{ \"role\": \"user\", \"content\": \"Does that change if the agreement was never countersigned?\" }],
\"docketrouter\": { \"case_file\": true, \"matter_id\": \"$MATTER\" } }"
# 5. read the history back
curl -s https://docketrouter.ai/api/v1/matters/$MATTER -H "Authorization: Bearer dr-…" | jq '.messages[] | {role, content}'Errors and limits
| Situation | Status | Body |
|---|---|---|
| No key and no session | 401 | {"error":{"message":"API key or sign-in required","type":"authentication_error"}} |
| Matter id you do not own, or that does not exist | 404 | {"error":{"message":"matter not found","type":"not_found_error"}} |
| Attaching a file you do not own | 404 | {"error":{"message":"file not found","type":"not_found_error"}} |
| Detaching a file that is not attached | 404 | {"error":{"message":"file is not attached to this matter","type":"not_found_error"}} |
Chat request with an unknown matter_id | 404 | Same envelope as above. The model is not called and nothing is billed. |
| Bad body | 400 | {"error":{"message":"invalid body: …","type":"invalid_request_error"}} |
A matter you do not own answers 404, not 403, on every endpoint, so ids cannot be probed.
Limits
| Limit | Value |
|---|---|
| Turns prepended to a chat request | the last 20 stored messages |
| Messages per page on GET /matters/:id | 50 by default, 200 max |
| Matters per list call | 100 by default, 500 max |
| Stored content per message | 200,000 characters, then clipped |
| Title | 200 characters |
| Retention | none; a matter lives until you delete it |
The 20 prepended turns are sent to the model and billed as prompt tokens like any other message. They are not counted against the 64-message or 200,000-character request limits, which apply to what you send.
Something here wrong or missing? Mail hello@docketrouter.ai with the request_id and we will fix the docs or the API, whichever is broken.