Documents
Everything the API makes is a document. These are the calls that read one back, in whatever shape the far end can take.
.pdf is a rendering, not a
file we are holding. Ask for it twice with an edit in between and you
get two different files — which is the right behaviour for a document and
would be a bug for a blob.List documents#
GET
/api/v1/documents
documents:read
Everything this account can open — what it owns and what has been shared with it — in the order the workspace shows them.
kind | optional | pdf or diagram. |
limit | optional | Up to 200. 50 by default. |
offset | optional | Where to start. total in the response is the count before the window is applied. |
curl -s "https://docubend.com/api/v1/documents?kind=diagram&limit=2" \
-H "Authorization: Bearer $TOKEN"
const r = await fetch(
`${BASE}/api/v1/documents?kind=diagram&limit=2`,
{ headers: { Authorization: `Bearer ${TOKEN}` } });
const { documents } = await r.json();
docs = requests.get(f"{BASE}/api/v1/documents",
headers={"Authorization": f"Bearer {TOKEN}"},
params={"kind": "diagram", "limit": 2},
timeout=30).json()["documents"]
for d in docs:
print(d["updated"], d["kind"], d["title"])
Response
{
"ok": true,
"total": 2,
"documents": [
{
"id": "6b35e4159e2e4b0e80ebae85f41cd6fd",
"title": "Release pipeline",
"kind": "diagram",
"pages": 1,
"shapes": 4,
"updated": 1786717599.9,
"created": 1786717599.5,
"version": 2,
"your_role": "owner",
"links": {
"…": "as above"
}
}
]
}
One document#
GET
/api/v1/documents/{id}
documents:read
What it is, how big it is and when it changed. Add
?include=model for the document's own JSON as well — left
out by default because it can be megabytes and a list view that wanted a
title should not pay for one.
include | optional | model to include the document's JSON under data. |
curl -s https://docubend.com/api/v1/documents/ba59b6ce5221411285daea90f4be16b6 \
-H "Authorization: Bearer $TOKEN"
const r = await fetch(`${BASE}/api/v1/documents/${docId}`,
{ headers: { Authorization: `Bearer ${TOKEN}` } });
const { document: doc } = await r.json();
doc = requests.get(f"{BASE}/api/v1/documents/{doc_id}",
headers={"Authorization": f"Bearer {TOKEN}"},
timeout=30).json()["document"]
print(doc["kind"], doc["pages"], "pages, version", doc["version"])
Response
{
"ok": true,
"document": {
"id": "ba59b6ce5221411285daea90f4be16b6",
"title": "Nightly build report",
"kind": "pdf",
"pages": 1,
"shapes": 11,
"version": 2,
"created": 1786717599.3359814,
"updated": 1786717599.4855263,
"your_role": "owner",
"links": {
"…": "as above"
}
}
}
Download it in any format#
GET
/api/v1/documents/{id}.{format}
documents:read
The document, said differently. Rendered at the moment you ask, so it always matches the document as it stands — including changes somebody made by hand in the editor after your software created it.
If a PDF could not be drawn exactly — characters outside what the
standard PDF fonts can set, or a picture that could not be read — it is
said in an X-Docubend-Warning header rather than left for
you to notice. Mermaid exports carry
X-Docubend-Shapes, X-Docubend-Dropped and
X-Docubend-Loose-Connectors, because not everything on a
page has a spelling in mermaid.
.pdf | application/pdf | The whole document, rendered now. ?page=2 for one page. |
.png | image/png | One page as a picture. ?page=, and ?dpi= up to 200 (110 by default). |
.svg | image/svg+xml | One page as vector SVG. ?page=. |
.mmd | text/vnd.mermaid | A diagram as mermaid text. Only what mermaid can say — the rest is counted in the response headers. |
.drawio | application/xml | A diagram as a .drawio file, openable in diagrams.net. |
.json | application/json | The document's own model — the same JSON the editor works on. |
# a PDF of the whole document
curl -s https://docubend.com/api/v1/documents/ba59b6ce5221411285daea90f4be16b6.pdf \
-H "Authorization: Bearer $TOKEN" -o report.pdf
# page 1 as a picture, at 150 dpi
curl -s "https://docubend.com/api/v1/documents/ba59b6ce5221411285daea90f4be16b6.png?page=1&dpi=150" \
-H "Authorization: Bearer $TOKEN" -o page-1.png
import { writeFile } from "node:fs/promises";
const r = await fetch(`${BASE}/api/v1/documents/${docId}.pdf`,
{ headers: { Authorization: `Bearer ${TOKEN}` } });
const warning = r.headers.get("x-docubend-warning");
if (warning) console.warn("note:", warning);
await writeFile("report.pdf", Buffer.from(await r.arrayBuffer()));
pdf = requests.get(f"{BASE}/api/v1/documents/{doc_id}.pdf",
headers={"Authorization": f"Bearer {TOKEN}"}, timeout=120)
if pdf.headers.get("X-Docubend-Warning"):
print("note:", pdf.headers["X-Docubend-Warning"])
open("report.pdf", "wb").write(pdf.content)
png = requests.get(f"{BASE}/api/v1/documents/{doc_id}.png",
headers={"Authorization": f"Bearer {TOKEN}"},
params={"page": 1, "dpi": 150}, timeout=120)
open("page-1.png", "wb").write(png.content)
Response
%PDF-1.4
…3,184 bytes of PDF…
A diagram back as mermaid#
GET
/api/v1/documents/{id}.mmd
documents:read
Anything drawn as boxes and arrows comes back out as mermaid text — for a README, a ticket, or a diff that a human can read. What has no spelling in mermaid (a pen stroke, a picture, a connector with a loose end) is counted in the headers rather than dropped silently.
A round trip through mermaid keeps the structure and not the arrangement, because the format has no coordinates to keep.
curl -s https://docubend.com/api/v1/documents/6b35e4159e2e4b0e80ebae85f41cd6fd.mmd \
-H "Authorization: Bearer $TOKEN" -D headers.txt
const r = await fetch(`${BASE}/api/v1/documents/${docId}.mmd`,
{ headers: { Authorization: `Bearer ${TOKEN}` } });
console.log(await r.text());
console.log(r.headers.get("x-docubend-shapes"), "shapes");
r = requests.get(f"{BASE}/api/v1/documents/{doc_id}.mmd",
headers={"Authorization": f"Bearer {TOKEN}"}, timeout=30)
print(r.text)
print(r.headers["X-Docubend-Shapes"], "shapes,",
r.headers["X-Docubend-Dropped"], "could not be said in mermaid")
Response
flowchart TD
Build[Build]
Tests_pass{Tests pass?}
Ship[(Ship)]
Fix[Fix]
Build --> Tests_pass
Tests_pass -->|yes| Ship
Tests_pass -->|no| Fix
Fix --> Build
Delete one#
DELETE
/api/v1/documents/{id}
documents:write
To the bin, where the account's retention setting keeps it — thirty days by default — and from where a person can put it back. Nothing here destroys a document immediately, on purpose.
curl -s -X DELETE https://docubend.com/api/v1/documents/ba59b6ce5221411285daea90f4be16b6 \
-H "Authorization: Bearer $TOKEN"
await fetch(`${BASE}/api/v1/documents/${docId}`, {
method: "DELETE",
headers: { Authorization: `Bearer ${TOKEN}` },
});
requests.delete(f"{BASE}/api/v1/documents/{doc_id}",
headers={"Authorization": f"Bearer {TOKEN}"}, timeout=30).json()
Response
{
"ok": true,
"binned": "ba59b6ce5221411285daea90f4be16b6"
}