Documentation
Documents
documents.list
REST
ShippingPOST /api/v1/documents/listMCP tool
Livedocuments.listExposed on: REST API · Shop MCP server · Claude connector · Dash (in-app copilot) · Zapier. Part of the Documents domain.
Operating contract
The filing cabinet: photos, PDFs, contracts and signed paperwork, newest first, with what each one is filed against.
filed_against is the useful column. A document can be attached to several records at once — the same photo can sit on a job, a vehicle and a warranty claim — and each entry names the record type, its id and a human label. Those ids are what customers.get, jobs.get, vehicles.get and the rest take.
kind separates what a file IS: photo and video are the work, contract and signature are paperwork somebody signed, pdf is everything else printed.
THE FILE ITSELF IS NOT HERE, and there is no capability that returns it. This read gives you a document's metadata; a URL to its bytes would be a bearer link into the shop's private storage, and a read that hands one out is a distribution channel wearing a search box.
q matches the title and the stored filename, so a document nobody titled is still findable by what it was called when it was uploaded.
TRUNCATION: total is the exact number of documents matching the filters and omitted is how many are not in this response. When omitted is not 0, call again with page incremented.
Who may call it
- Permission
- NoneThe library is open to every active member of the shop — `/documents` is guarded by `requireShopContext()` and by no section at all, because a technician needs the photos of the car they are working on. Gating this would make a machine see less of the shop's own filing than the bench does.
- Plan
- Every planNo plan gate. Available on every Service VIN plan.
- Retries
naturalNaturally idempotent — running it twice leaves the same world as running it once. A retrying integration needs no key.- Rate class
readCounted against the read budget — the widest of the four.
Partial answers
omitted is how many matching records are not in the response, and 0 is a real answer meaning you have all of them. Continue with page.
Do not summarise from a truncated response
Input
| Field | Type | Description |
|---|---|---|
| q | stringmax 120 chars | Part of a document's title or its original filename. |
| kind | string | One document kind — photo, video, pdf, signature, contract or other. One of:photo, video, pdf, signature, contract, other |
| tag | stringmax 60 chars | One tag, exactly as it appears on another document's `tags`. |
| page | integer1–1000 | 1-based page number. Defaults to 1. Default:1 |
Output
| Field | Type | Description |
|---|---|---|
| items | object[] | — |
| items[].id | string | — |
| items[].title | string | null | — |
| items[].kind | string | — |
| items[].content_type | string | null | — |
| items[].size_bytes | number | null | — |
| items[].tags | string[] | — |
| items[].created_at | string | — |
| items[].filed_against | object[] | — |
| items[].filed_against[].owner_type | string | — |
| items[].filed_against[].owner_id | string | — |
| items[].filed_against[].label | string | — |
| items[].filed_against[].role | string | null | — |
| total | number | — |
| page | number | — |
| page_size | number | — |
| omitted | number | — |
Examples
Built from this capability's own schema — required fields and the ones carrying a default, and nothing invented. Paste one and it validates.
export SERVICEVIN_API_KEY=svk_live_…
curl -X POST https://www.servicevin.com/api/v1/documents/list \
-H "Authorization: Bearer $SERVICEVIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"page":1}'const res = await fetch("https://www.servicevin.com/api/v1/documents/list", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SERVICEVIN_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"page": 1
}),
});
// Success and failure are both envelopes. Switch on error.code, never
// on error.message — the codes are stable, the messages are for people.
const payload = await res.json();
if (!res.ok) throw new Error(payload.error.code);
const data = payload.data;import os, requests
res = requests.post(
"https://www.servicevin.com/api/v1/documents/list",
headers={"Authorization": f"Bearer {os.environ['SERVICEVIN_API_KEY']}"},
json={
"page": 1
},
timeout=30,
)
payload = res.json()
if not res.ok:
raise RuntimeError(payload["error"]["code"])
data = payload["data"]{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "documents.list",
"arguments": {
"page": 1
}
}
}Refusals
The four gates run in this order on every surface, and the order is not arbitrary — see Authentication.
| Status | Code | When |
|---|---|---|
| 404 | not_found | The id is unknown, or the feature is not enabled for this account. Deliberately the same answer for both. |
| 422 | validation_error | An argument was wrong. The message names the field. |
| 429 | rate_limited | Too many read calls. Back off and retry. |
| 500 | internal_error | Something failed on our side. Nothing was changed. |