curl --request POST \
--url https://api.vaquill.ai/workspace/v1/matters/{matterId}/comparisons \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"title": "MSA v2 against v3",
"originalDocumentId": "doc_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"revisedDocumentId": "doc_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6"
}
'import requests
url = "https://api.vaquill.ai/workspace/v1/matters/{matterId}/comparisons"
payload = {
"title": "MSA v2 against v3",
"originalDocumentId": "doc_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"revisedDocumentId": "doc_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
title: 'MSA v2 against v3',
originalDocumentId: 'doc_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6',
revisedDocumentId: 'doc_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6'
})
};
fetch('https://api.vaquill.ai/workspace/v1/matters/{matterId}/comparisons', 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}/comparisons"
payload := strings.NewReader("{\n \"title\": \"MSA v2 against v3\",\n \"originalDocumentId\": \"doc_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6\",\n \"revisedDocumentId\": \"doc_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6\"\n}")
req, _ := http.NewRequest("POST", 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": "op_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"type": "matrix.run",
"status": "succeeded",
"createdAt": "2026-08-19T14:32:10Z",
"completedAt": "2026-08-19T14:32:10Z",
"matterId": "mat_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"resource": {
"kind": "matrix",
"id": "mtx_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"url": "https://storage.vaquill.ai/exports/msa-acme-v3-redline.docx?signature=..."
},
"progress": {
"done": 24,
"total": 128,
"unit": "cells"
},
"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"
}{
"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"
}Compare two documents
Queue a comparison. 202 with an operation to poll.
A retry carrying the same Idempotency-Key returns the original operation
and starts nothing, which is what makes a client timeout free rather than a
second billed comparison.
curl --request POST \
--url https://api.vaquill.ai/workspace/v1/matters/{matterId}/comparisons \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"title": "MSA v2 against v3",
"originalDocumentId": "doc_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"revisedDocumentId": "doc_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6"
}
'import requests
url = "https://api.vaquill.ai/workspace/v1/matters/{matterId}/comparisons"
payload = {
"title": "MSA v2 against v3",
"originalDocumentId": "doc_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"revisedDocumentId": "doc_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
title: 'MSA v2 against v3',
originalDocumentId: 'doc_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6',
revisedDocumentId: 'doc_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6'
})
};
fetch('https://api.vaquill.ai/workspace/v1/matters/{matterId}/comparisons', 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}/comparisons"
payload := strings.NewReader("{\n \"title\": \"MSA v2 against v3\",\n \"originalDocumentId\": \"doc_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6\",\n \"revisedDocumentId\": \"doc_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6\"\n}")
req, _ := http.NewRequest("POST", 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": "op_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"type": "matrix.run",
"status": "succeeded",
"createdAt": "2026-08-19T14:32:10Z",
"completedAt": "2026-08-19T14:32:10Z",
"matterId": "mat_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"resource": {
"kind": "matrix",
"id": "mtx_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"url": "https://storage.vaquill.ai/exports/msa-acme-v3-redline.docx?signature=..."
},
"progress": {
"done": 24,
"total": 128,
"unit": "cells"
},
"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"
}{
"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.
Headers
A unique value of your choosing, so a retried launch returns the FIRST operation instead of starting a second billable job. Reusing one with a different body is refused. Retrying with the same key and the same body is free and is the intended way to recover from a timeout.
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.
Body
Start a comparison between two documents already in the matter.
Both sides are matter documents. The internal API also accepts drafts, draft versions and loose uploads; publishing those would mean publishing three more identifier spaces, and a customer that wants to compare arbitrary bytes uploads them to the matter first, which is one call it was going to make anyway.
There is no matterId field. The matter is the path, which is what makes
the authorization boundary visible in the URL.
Display title for the comparison. Trimmed; whitespace alone is refused.
1 - 300"MSA v2 against v3"
doc_ identifier of the ORIGINAL document, the one being compared against. Must be in this matter.
"doc_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6"
doc_ identifier of the REVISED document. Must be in this matter, and must differ from originalDocumentId.
"doc_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6"
How the diff engine should treat the two inputs. Omit for sensible defaults.
Show child attributes
Show child attributes
Response
Successful Response
One long-running operation, whatever kind of work it is.
Readable for OPERATION_RETENTION_DAYS after creation, per AIP-151.
Public identifier, op_ followed by 32 hex characters. Poll GET /v1/operations/{operationId} with it.
"op_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6"
What kind of work this is, for example matrix.run or draft.generate.
"matrix.run"
One of five values: queued, running, succeeded, failed, cancelled. There is no sixth and there are no synonyms. Stop polling once it is succeeded, failed or cancelled.
queued, running, succeeded, failed, cancelled "succeeded"
When the operation was accepted (RFC 3339).
"2026-08-19T14:32:10Z"
When the operation reached a terminal status (RFC 3339). Present if and only if the status is terminal.
"2026-08-19T14:32:10Z"
mat_ identifier of the matter this work belongs to, when it belongs to one.
"mat_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6"
What the operation produced. Absent until the underlying job row exists, which an idempotent replay can briefly observe, so treat absence as 'not yet' rather than 'never'.
Show child attributes
Show child attributes
Why the work failed. Present only when status is failed. Partial success is succeeded with progress.done < progress.total, never an error.
Show child attributes
Show child attributes
How far along the work is, when the underlying job reports it. Absent does not mean no progress.
Show child attributes
Show child attributes
The X-Request-ID of the request that created this operation. Quote it in a support ticket.
"req_5f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6"
Was this page helpful?

