curl --request PATCH \
--url https://api.vaquill.ai/workspace/v1/matters/{matterId}/documents/{documentId} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"folderId": "fld_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6"
}
'import requests
url = "https://api.vaquill.ai/workspace/v1/matters/{matterId}/documents/{documentId}"
payload = { "folderId": "fld_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6" }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.patch(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PATCH',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({folderId: 'fld_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6'})
};
fetch('https://api.vaquill.ai/workspace/v1/matters/{matterId}/documents/{documentId}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.vaquill.ai/workspace/v1/matters/{matterId}/documents/{documentId}"
payload := strings.NewReader("{\n \"folderId\": \"fld_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6\"\n}")
req, _ := http.NewRequest("PATCH", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}{
"id": "doc_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"matterId": "mat_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"folderId": "fld_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"filename": "msa-acme-v3.docx",
"contentType": "application/pdf",
"fileSize": 248193,
"pageCount": 14,
"chunkCount": 37,
"sourceType": "api",
"status": "succeeded",
"isEncrypted": false,
"createdAt": "2026-08-19T14:32:10Z",
"updatedAt": "2026-08-19T14:32:10Z",
"processedAt": "2026-08-19T14:32:10Z"
}{
"type": "https://vaquill.ai/docs/workspace-api/errors/insufficient-scope",
"title": "Insufficient scope",
"status": 403,
"detail": "This credential carries matters:read. This operation needs matters:write.",
"instance": "/workspace/v1/matters/mat_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"requestId": "req_5f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6"
}{
"type": "https://vaquill.ai/docs/workspace-api/errors/insufficient-scope",
"title": "Insufficient scope",
"status": 403,
"detail": "This credential carries matters:read. This operation needs matters:write.",
"instance": "/workspace/v1/matters/mat_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"requestId": "req_5f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6"
}{
"type": "https://vaquill.ai/docs/workspace-api/errors/insufficient-scope",
"title": "Insufficient scope",
"status": 403,
"detail": "This credential carries matters:read. This operation needs matters:write.",
"instance": "/workspace/v1/matters/mat_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"requestId": "req_5f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6"
}{
"type": "https://vaquill.ai/docs/workspace-api/errors/insufficient-scope",
"title": "Insufficient scope",
"status": 403,
"detail": "This credential carries matters:read. This operation needs matters:write.",
"instance": "/workspace/v1/matters/mat_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"errors": [
{
"location": "<string>",
"message": "<string>",
"type": "<string>"
}
],
"requestId": "req_5f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6"
}{
"type": "https://vaquill.ai/docs/workspace-api/errors/insufficient-scope",
"title": "Insufficient scope",
"status": 403,
"detail": "This credential carries matters:read. This operation needs matters:write.",
"instance": "/workspace/v1/matters/mat_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"requestId": "req_5f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6"
}{
"type": "https://vaquill.ai/docs/workspace-api/errors/insufficient-scope",
"title": "Insufficient scope",
"status": 403,
"detail": "This credential carries matters:read. This operation needs matters:write.",
"instance": "/workspace/v1/matters/mat_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"requestId": "req_5f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6"
}{
"type": "https://vaquill.ai/docs/workspace-api/errors/insufficient-scope",
"title": "Insufficient scope",
"status": 403,
"detail": "This credential carries matters:read. This operation needs matters:write.",
"instance": "/workspace/v1/matters/mat_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"requestId": "req_5f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6"
}Move a document between folders
Move a document between folders.
folderId is the only field a document accepts. Send null to unfile it.
The folder must belong to your organization.
Filename and matter cannot be changed. Both are part of the stored file’s identity, so changing either would leave the original unreadable; moving a document to another matter is an upload into the new matter followed by a delete from the old one.
curl --request PATCH \
--url https://api.vaquill.ai/workspace/v1/matters/{matterId}/documents/{documentId} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"folderId": "fld_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6"
}
'import requests
url = "https://api.vaquill.ai/workspace/v1/matters/{matterId}/documents/{documentId}"
payload = { "folderId": "fld_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6" }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.patch(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'PATCH',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({folderId: 'fld_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6'})
};
fetch('https://api.vaquill.ai/workspace/v1/matters/{matterId}/documents/{documentId}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.vaquill.ai/workspace/v1/matters/{matterId}/documents/{documentId}"
payload := strings.NewReader("{\n \"folderId\": \"fld_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6\"\n}")
req, _ := http.NewRequest("PATCH", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}{
"id": "doc_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"matterId": "mat_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"folderId": "fld_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"filename": "msa-acme-v3.docx",
"contentType": "application/pdf",
"fileSize": 248193,
"pageCount": 14,
"chunkCount": 37,
"sourceType": "api",
"status": "succeeded",
"isEncrypted": false,
"createdAt": "2026-08-19T14:32:10Z",
"updatedAt": "2026-08-19T14:32:10Z",
"processedAt": "2026-08-19T14:32:10Z"
}{
"type": "https://vaquill.ai/docs/workspace-api/errors/insufficient-scope",
"title": "Insufficient scope",
"status": 403,
"detail": "This credential carries matters:read. This operation needs matters:write.",
"instance": "/workspace/v1/matters/mat_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"requestId": "req_5f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6"
}{
"type": "https://vaquill.ai/docs/workspace-api/errors/insufficient-scope",
"title": "Insufficient scope",
"status": 403,
"detail": "This credential carries matters:read. This operation needs matters:write.",
"instance": "/workspace/v1/matters/mat_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"requestId": "req_5f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6"
}{
"type": "https://vaquill.ai/docs/workspace-api/errors/insufficient-scope",
"title": "Insufficient scope",
"status": 403,
"detail": "This credential carries matters:read. This operation needs matters:write.",
"instance": "/workspace/v1/matters/mat_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"requestId": "req_5f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6"
}{
"type": "https://vaquill.ai/docs/workspace-api/errors/insufficient-scope",
"title": "Insufficient scope",
"status": 403,
"detail": "This credential carries matters:read. This operation needs matters:write.",
"instance": "/workspace/v1/matters/mat_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"errors": [
{
"location": "<string>",
"message": "<string>",
"type": "<string>"
}
],
"requestId": "req_5f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6"
}{
"type": "https://vaquill.ai/docs/workspace-api/errors/insufficient-scope",
"title": "Insufficient scope",
"status": 403,
"detail": "This credential carries matters:read. This operation needs matters:write.",
"instance": "/workspace/v1/matters/mat_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"requestId": "req_5f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6"
}{
"type": "https://vaquill.ai/docs/workspace-api/errors/insufficient-scope",
"title": "Insufficient scope",
"status": 403,
"detail": "This credential carries matters:read. This operation needs matters:write.",
"instance": "/workspace/v1/matters/mat_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"requestId": "req_5f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6"
}{
"type": "https://vaquill.ai/docs/workspace-api/errors/insufficient-scope",
"title": "Insufficient scope",
"status": 403,
"detail": "This credential carries matters:read. This operation needs matters:write.",
"instance": "/workspace/v1/matters/mat_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"requestId": "req_5f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6"
}Authorizations
Workspace credential issued from the automation console at /automation. Send it as Authorization: Bearer vq_ws_.... This is NOT a Data API key: a vq_key_ credential is refused here and names the other product in the error.
Path Parameters
mat_ identifier of the matter to work inside. Everything in this API hangs off a matter, and the matter in the path is what the authorization boundary is checked against. Take it from GET /v1/matters.
doc_ identifier of the document. Take it from the matter's document list, or from the operation that completed its upload.
Body
Move a document between folders. That is the whole of it.
The web app's PATCH /documents/{id} also accepts filename, matterId
and metadata, and none of the three can be published here.
filename and matterId are both INSIDE the R2 object key.
r2_storage._generate_file_key composes
orgs/{org}/matters/{matter}/docs/{doc}/{filename}, and both
r2_storage.download_document and adapters/document_content._object_key
REGENERATE that key from the row rather than reading a stored path. So
changing either one makes the original permanently unreadable, with no error
at write time and no error until somebody asks for the file. The web app has
that defect today; publishing the fields here would inherit it, and a moved
document is a copy-then-delete job rather than a PATCH.
metadata is free-form JSONB whose keys the ingest pipeline owns:
{"handwritten": ...} is written by upload_store._insert_placeholder and
read back by the finalize task. Publishing it lets a customer overwrite a
key a worker depends on.
fld_ identifier of the folder to file this document under. Must belong to your organization. Send null to unfile it. Filename and matter cannot be changed: both are part of the stored object's identity.
"fld_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6"
Response
Successful Response
One file in a matter, and how far its ingestion got.
status is the FIVE-value public vocabulary, not the four-label
document_status Postgres enum. A document's status is the status of the
job that ingested it, and publishing completed here beside succeeded on
the operation that produced it would make a customer map two spellings of
one event. adapters/status_map does the translation and refuses anything
it has not been told about, so a new enum label fails loudly in CI rather
than leaking a sixth value.
Public identifier, doc_ followed by 32 hex characters.
"doc_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6"
mat_ identifier of the matter this document lives in.
"mat_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6"
Original filename as uploaded, including its extension.
"msa-acme-v3.docx"
Ingestion status, using the same five public values as an operation. Retrieval, drafting and review can only see the document once this is succeeded.
queued, running, succeeded, failed, cancelled "succeeded"
When the document row was created (RFC 3339).
"2026-08-19T14:32:10Z"
fld_ identifier of the folder it is filed under. Absent when unfiled.
"fld_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6"
IANA media type detected at upload, for example application/pdf.
"application/pdf"
Size of the stored original in bytes.
248193
Pages the pipeline read. Absent or zero until ingestion has read the file. This is the number to size a review or matrix run against.
14
How many retrieval chunks the document was split into. Absent until ingestion finishes.
37
How the document entered the workspace. api for everything created through this surface; rows made in the web app carry upload, email, workflow or chat_artifact. Published as a plain string, so do not branch on it without a fallback.
"api"
Whether the stored original is encrypted at rest with a per-document key. This is why downloads stream bytes rather than handing back a storage URL.
false
When the document was last modified (RFC 3339).
"2026-08-19T14:32:10Z"
When ingestion finished (RFC 3339). Absent while the document is still processing.
"2026-08-19T14:32:10Z"
Was this page helpful?

