docubend
Documentation

Documents

Everything the API makes is a document. These are the calls that read one back, in whatever shape the far end can take.

A .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.

Query parameters
kindoptionalpdf or diagram.
limitoptionalUp to 200. 50 by default.
offsetoptionalWhere to start. total in the response is the count before the window is applied.
curl
curl -s "https://docubend.com/api/v1/documents?kind=diagram&limit=2" \
  -H "Authorization: Bearer $TOKEN"
JavaScript
const r = await fetch(
  `${BASE}/api/v1/documents?kind=diagram&limit=2`,
  { headers: { Authorization: `Bearer ${TOKEN}` } });
const { documents } = await r.json();
Python
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

200
{
  "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.

Query parameters
includeoptionalmodel to include the document's JSON under data.
curl
curl -s https://docubend.com/api/v1/documents/ba59b6ce5221411285daea90f4be16b6 \
  -H "Authorization: Bearer $TOKEN"
JavaScript
const r = await fetch(`${BASE}/api/v1/documents/${docId}`,
  { headers: { Authorization: `Bearer ${TOKEN}` } });
const { document: doc } = await r.json();
Python
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

200
{
  "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.

Formats
.pdfapplication/pdfThe whole document, rendered now. ?page=2 for one page.
.pngimage/pngOne page as a picture. ?page=, and ?dpi= up to 200 (110 by default).
.svgimage/svg+xmlOne page as vector SVG. ?page=.
.mmdtext/vnd.mermaidA diagram as mermaid text. Only what mermaid can say — the rest is counted in the response headers.
.drawioapplication/xmlA diagram as a .drawio file, openable in diagrams.net.
.jsonapplication/jsonThe document's own model — the same JSON the editor works on.
curl
# 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
JavaScript
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()));
Python
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

200
%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
curl -s https://docubend.com/api/v1/documents/6b35e4159e2e4b0e80ebae85f41cd6fd.mmd \
  -H "Authorization: Bearer $TOKEN" -D headers.txt
JavaScript
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");
Python
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

200
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
curl -s -X DELETE https://docubend.com/api/v1/documents/ba59b6ce5221411285daea90f4be16b6 \
  -H "Authorization: Bearer $TOKEN"
JavaScript
await fetch(`${BASE}/api/v1/documents/${docId}`, {
  method: "DELETE",
  headers: { Authorization: `Bearer ${TOKEN}` },
});
Python
requests.delete(f"{BASE}/api/v1/documents/{doc_id}",
    headers={"Authorization": f"Bearer {TOKEN}"}, timeout=30).json()

Response

200
{
  "ok": true,
  "binned": "ba59b6ce5221411285daea90f4be16b6"
}