curl --request PATCH \
--url https://api.vaquill.ai/workspace/v1/matters/{matterId}/matrices/{matrixId}/columns/{columnId} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"label": "Liability cap",
"question": "What is the liability cap, and what does it apply to?",
"columnType": "free_text",
"options": [
"yes",
"no",
"not addressed"
],
"instructions": "House paper. Cap liability at 12 months of fees; never accept uncapped indemnity.",
"dependsOnColumnId": "col_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"gateExpression": "yes",
"injectUpstream": false
}
'import requests
url = "https://api.vaquill.ai/workspace/v1/matters/{matterId}/matrices/{matrixId}/columns/{columnId}"
payload = {
"label": "Liability cap",
"question": "What is the liability cap, and what does it apply to?",
"columnType": "free_text",
"options": ["yes", "no", "not addressed"],
"instructions": "House paper. Cap liability at 12 months of fees; never accept uncapped indemnity.",
"dependsOnColumnId": "col_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"gateExpression": "yes",
"injectUpstream": False
}
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({
label: 'Liability cap',
question: 'What is the liability cap, and what does it apply to?',
columnType: 'free_text',
options: ['yes', 'no', 'not addressed'],
instructions: 'House paper. Cap liability at 12 months of fees; never accept uncapped indemnity.',
dependsOnColumnId: 'col_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6',
gateExpression: 'yes',
injectUpstream: false
})
};
fetch('https://api.vaquill.ai/workspace/v1/matters/{matterId}/matrices/{matrixId}/columns/{columnId}', 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}/matrices/{matrixId}/columns/{columnId}"
payload := strings.NewReader("{\n \"label\": \"Liability cap\",\n \"question\": \"What is the liability cap, and what does it apply to?\",\n \"columnType\": \"free_text\",\n \"options\": [\n \"yes\",\n \"no\",\n \"not addressed\"\n ],\n \"instructions\": \"House paper. Cap liability at 12 months of fees; never accept uncapped indemnity.\",\n \"dependsOnColumnId\": \"col_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6\",\n \"gateExpression\": \"yes\",\n \"injectUpstream\": false\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": "mtx_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"matterId": "mat_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"title": "Vendor agreement review",
"description": "Master services agreement with Acme for the 2026 platform rollout.",
"status": "succeeded",
"columns": [
{
"id": "col_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"label": "Liability cap",
"question": "What is the liability cap, and what does it apply to?",
"columnType": "free_text",
"options": [
"yes",
"no",
"not addressed"
],
"instructions": "House paper. Cap liability at 12 months of fees; never accept uncapped indemnity.",
"dependsOnColumnId": "col_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"gateExpression": "yes",
"injectUpstream": false,
"position": 0
}
],
"rows": [
{
"id": "row_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"documentId": "doc_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"filename": "msa-acme-v3.docx",
"position": 0
}
],
"cellCount": 24,
"pendingCellCount": 0,
"errorCellCount": 0,
"createdAt": "2026-08-19T14:32:10Z",
"updatedAt": "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"
}Update a matrix column
Change one column, including making it conditional on another.
Changing question, columnType or options clears every answer in this
column, including answers a person wrote or approved and any verification
on them, and leaves the cells unanswered until the next run. Changing
label or instructions clears nothing.
dependsOnColumnId makes this column conditional: its cells only run when
the named column has answered, and gateExpression says what the answer has
to be. The gate vocabulary is closed (any, not_empty, yes, no,
yes_no:yes, yes_no:no, equals:VALUE, in:A|B|C) because an expression
we cannot parse marks every cell in the column as deliberately skipped with
no error anywhere. injectUpstream prepends the upstream answer to this
column’s prompt.
A dependency naming a column outside this matrix is 404. One that would
close a cycle is 422. There is no position: use PUT .../columns/order.
curl --request PATCH \
--url https://api.vaquill.ai/workspace/v1/matters/{matterId}/matrices/{matrixId}/columns/{columnId} \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"label": "Liability cap",
"question": "What is the liability cap, and what does it apply to?",
"columnType": "free_text",
"options": [
"yes",
"no",
"not addressed"
],
"instructions": "House paper. Cap liability at 12 months of fees; never accept uncapped indemnity.",
"dependsOnColumnId": "col_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"gateExpression": "yes",
"injectUpstream": false
}
'import requests
url = "https://api.vaquill.ai/workspace/v1/matters/{matterId}/matrices/{matrixId}/columns/{columnId}"
payload = {
"label": "Liability cap",
"question": "What is the liability cap, and what does it apply to?",
"columnType": "free_text",
"options": ["yes", "no", "not addressed"],
"instructions": "House paper. Cap liability at 12 months of fees; never accept uncapped indemnity.",
"dependsOnColumnId": "col_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"gateExpression": "yes",
"injectUpstream": False
}
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({
label: 'Liability cap',
question: 'What is the liability cap, and what does it apply to?',
columnType: 'free_text',
options: ['yes', 'no', 'not addressed'],
instructions: 'House paper. Cap liability at 12 months of fees; never accept uncapped indemnity.',
dependsOnColumnId: 'col_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6',
gateExpression: 'yes',
injectUpstream: false
})
};
fetch('https://api.vaquill.ai/workspace/v1/matters/{matterId}/matrices/{matrixId}/columns/{columnId}', 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}/matrices/{matrixId}/columns/{columnId}"
payload := strings.NewReader("{\n \"label\": \"Liability cap\",\n \"question\": \"What is the liability cap, and what does it apply to?\",\n \"columnType\": \"free_text\",\n \"options\": [\n \"yes\",\n \"no\",\n \"not addressed\"\n ],\n \"instructions\": \"House paper. Cap liability at 12 months of fees; never accept uncapped indemnity.\",\n \"dependsOnColumnId\": \"col_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6\",\n \"gateExpression\": \"yes\",\n \"injectUpstream\": false\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": "mtx_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"matterId": "mat_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"title": "Vendor agreement review",
"description": "Master services agreement with Acme for the 2026 platform rollout.",
"status": "succeeded",
"columns": [
{
"id": "col_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"label": "Liability cap",
"question": "What is the liability cap, and what does it apply to?",
"columnType": "free_text",
"options": [
"yes",
"no",
"not addressed"
],
"instructions": "House paper. Cap liability at 12 months of fees; never accept uncapped indemnity.",
"dependsOnColumnId": "col_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"gateExpression": "yes",
"injectUpstream": false,
"position": 0
}
],
"rows": [
{
"id": "row_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"documentId": "doc_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"filename": "msa-acme-v3.docx",
"position": 0
}
],
"cellCount": 24,
"pendingCellCount": 0,
"errorCellCount": 0,
"createdAt": "2026-08-19T14:32:10Z",
"updatedAt": "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.
mtx_ identifier of the matrix. Take it from the matter's matrix list.
col_ identifier of one column of the matrix, which is one question asked of every document. Take it from the matrix's columns.
Body
Change one column, including whether it is conditional on another.
Changing question, columnType or options BLANKS every cell in this
column. MatrixService.update_column bulk-resets them to pending with a
null answer, no citations and no answer_data, which destroys a manually
edited or approved answer and the verifier's verdict along with it. That is
defensible in a browser where a human just typed the new question and is
looking at the grid; it is not something a PATCH should do silently, which is
why it is stated in each of those three field descriptions and not only
here. A generated client shows the field description.
label and instructions do not reset anything: they change how the column
reads and what guidance it carries, not what was asked.
New column heading. Resets nothing.
1 - 200"Liability cap"
New question for this column. WARNING: changing it clears every existing answer in this column, including answers a human wrote or approved and any verification verdict on them, and leaves the cells unanswered until the next run.
1 - 4000"What is the liability cap, and what does it apply to?"
New answer shape: free_text, single_select, date, money, yes_no or party_name. WARNING: changing it clears every existing answer in this column, as changing the question does.
free_text, single_select, date, money, yes_no, party_name "free_text"
New allowed answers for a single_select column. WARNING: changing them clears every existing answer in this column, as changing the question does.
["yes", "no", "not addressed"]
New extractor guidance scoped to this column. Resets nothing.
4000"House paper. Cap liability at 12 months of fees; never accept uncapped indemnity."
col_ identifier of a column in this same matrix that this one becomes conditional on, or null to make it unconditional again. A cycle is refused.
"col_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6"
The condition on the upstream answer that has to hold for this column to run, or null to run whenever the dependency has answered. One of any, not_empty, yes, no, yes_no:yes, yes_no:no, equals:VALUE or in:A|B|C. Anything else is refused: an expression we cannot parse would mark every cell in the column as deliberately skipped with no error anywhere. yes, no and equals: match the WHOLE upstream answer, not part of it, so they belong on a yes_no or single_select upstream column; over a free_text one, use not_empty.
500^(any|not_empty|yes|no|yes_no:(?:yes|no)|equals:.+|in:.+)$"yes"
True prepends the upstream column's answer to this column's prompt, so the question can refer to it. False asks the question on its own.
false
Response
Successful Response
The grid header plus its axes. Cells are a separate, paged read.
Rows and columns are inlined because they are bounded and because a cell is unreadable without them: a cell names a row id and a column id and nothing else, so a client that could not resolve those would have to page the whole grid to interpret one answer.
Public identifier, mtx_ followed by 32 hex characters.
"mtx_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6"
mat_ identifier of the matter this matrix belongs to.
"mat_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6"
Display title for the matrix.
"Vendor agreement review"
Matrix status, using the same five public values as an operation.
queued, running, succeeded, failed, cancelled "succeeded"
When the matrix was created (RFC 3339).
"2026-08-19T14:32:10Z"
When the matrix was last updated (RFC 3339).
"2026-08-19T14:32:10Z"
Free-text description of what the matrix is for.
"Master services agreement with Acme for the 2026 platform rollout."
Every column, inlined. A cell names only ids, so these are what make one readable.
Show child attributes
Show child attributes
Every row, inlined, one per document.
Show child attributes
Show child attributes
Total cells in the grid, which is rows times columns.
24
Cells that have not produced an answer yet. Zero means extraction is complete.
0
Cells whose extraction failed. These are distinct from cells that succeeded with a null answer.
0
Was this page helpful?

