curl --request POST \
--url https://api.vaquill.ai/workspace/v1/templates/from-document \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"documentId": "doc_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6"
}
'import requests
url = "https://api.vaquill.ai/workspace/v1/templates/from-document"
payload = { "documentId": "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({documentId: 'doc_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6'})
};
fetch('https://api.vaquill.ai/workspace/v1/templates/from-document', 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/templates/from-document"
payload := strings.NewReader("{\n \"documentId\": \"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": "tpl_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"title": "Master Services Agreement",
"description": "Master services agreement with Acme for the 2026 platform rollout.",
"category": "commercial",
"sourceFilename": "msa-acme-v3.docx",
"createdAt": "2026-08-19T14:32:10Z",
"updatedAt": "2026-08-19T14:32:10Z",
"variables": [
{
"id": "party_a_name",
"name": "Counterparty name",
"label": "Counterparty name",
"kind": "matrix",
"hint": "value",
"defaultValue": "value",
"position": 0
}
],
"sections": [
{
"heading": "8. Limitation of Liability",
"level": 1,
"text": "Neither party shall be liable for indirect or consequential damages."
}
]
}{
"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"
}Create a template from an uploaded document
Turn a document you already uploaded into a template.
Upload the file first through uploads.initiate, then name the documentId
the completed upload produced. The source document stays yours: you can
list, download and delete it, which is what makes “which contract did this
template come from” answerable months later.
A .docx keeps its headings, lists and tables. .txt and .md are
converted paragraph by paragraph. Anything else is refused rather than
degraded, because an import that silently lost its structure is one nobody
notices until they open the result.
curl --request POST \
--url https://api.vaquill.ai/workspace/v1/templates/from-document \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"documentId": "doc_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6"
}
'import requests
url = "https://api.vaquill.ai/workspace/v1/templates/from-document"
payload = { "documentId": "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({documentId: 'doc_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6'})
};
fetch('https://api.vaquill.ai/workspace/v1/templates/from-document', 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/templates/from-document"
payload := strings.NewReader("{\n \"documentId\": \"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": "tpl_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6",
"title": "Master Services Agreement",
"description": "Master services agreement with Acme for the 2026 platform rollout.",
"category": "commercial",
"sourceFilename": "msa-acme-v3.docx",
"createdAt": "2026-08-19T14:32:10Z",
"updatedAt": "2026-08-19T14:32:10Z",
"variables": [
{
"id": "party_a_name",
"name": "Counterparty name",
"label": "Counterparty name",
"kind": "matrix",
"hint": "value",
"defaultValue": "value",
"position": 0
}
],
"sections": [
{
"heading": "8. Limitation of Liability",
"level": 1,
"text": "Neither party shall be liable for indirect or consequential damages."
}
]
}{
"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.
Body
Turn a document you already uploaded into a template.
Takes a documentId rather than a file. Bytes reach this API exactly one
way, through uploads.initiate and the presigned part PUTs, so there is one
media allowlist, one size ceiling and one answer to encryption at rest. The
cost is stated: importing a template is four calls rather than one, and the
source file stays as a document you own, can list, can download and can
delete, which is how "which contract did this come from" is answerable
months later.
A DOCX-sourced template keeps its Word structure; a later
templates.update does not. Replacing the body through this API replaces
it with what markdown can express, so duplicate first if the original
formatting matters.
doc_ identifier of a document in this organization to build the template from.
"doc_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6"
Display name. Defaults to the document's filename.
1 - 300"Master Services Agreement"
What this template is for, in your own words.
2000"Master services agreement with Acme for the 2026 platform rollout."
How you classify this template. Defaults to custom.
1 - 100"commercial"
Response
Successful Response
One template with everything needed to run it without guessing.
variables is the reason this model exists at all, and sections is here
for the same reason a draft publishes them: a caller deciding WHICH template
to run needs to read what it says, and the editor's own document model does
not cross this boundary in either direction.
Public identifier, tpl_ followed by 32 hex characters. Name it when starting a run.
"tpl_9f2c8b1e4a7d43c9b6e0f1a2c3d4e5f6"
The template's display name.
"Master Services Agreement"
The template's category, as the customer classified it on upload.
"commercial"
When the template was created (RFC 3339).
"2026-08-19T14:32:10Z"
When the template was last modified (RFC 3339).
"2026-08-19T14:32:10Z"
Every fillable span, in document order. The id of each is what slotOverrides on a run is keyed by.
Show child attributes
Show child attributes
The template body, section by section, with the placeholder text left in place. Send a replacement as contentMarkdown.
Show child attributes
Show child attributes
Free-text description of what the template is for.
"Master services agreement with Acme for the 2026 platform rollout."
Filename of the document this template was created from. Published because it is how a person recognizes which template a run used, months later.
"msa-acme-v3.docx"
Was this page helpful?

