docubend
Documentation

PDFs

Five things a PDF can be made from, one per request.

What you can send#

One of these fields, and only one. Send the wrong thing and the refusal lists what this endpoint takes rather than saying no.

FieldTypeWhat it is
markdownstringMarkdown. Headings, bold, lists, tables, links, images, quotes and code fences. Set as real text, not a picture of it.
htmlstringThe same closed vocabulary as Markdown, if you already have HTML.
imagebase64 stringA PNG or JPEG. Or post the bytes as the request body with the matching Content-Type.
pdfbase64 stringAn existing PDF, imported page by page so it becomes editable. "mode": "image" keeps each page as a picture instead.
modelobjectA document model of your own — the same shape .json gives back.

Or post the bytes as the request body with a matching Content-Typeimage/png, application/pdf, text/markdown, text/html — and put the title in ?title=. A shell pipeline holding a 30 MB PDF should not have to base64 it first.

Make a PDF#

POST /api/v1/pdfs documents:write

Send one source field and get back a document kept in My documents. Markdown and HTML are set as real, selectable text — headings, lists, tables, links and pictures — rather than a screenshot of a page. A picture becomes a page the size of the picture; a PDF is imported page by page so it stays editable.

Answers 201 with the document. It is a real docubend document: it opens in the editor, can be annotated, shared and searched, and it keeps a version history.

Body fields
titleoptionalWhat to call it. Defaults to Untitled PDF, or use ?title= when posting raw bytes.
markdown / html / image / pdf / modelone requiredThe source. See what you can send.
imagesoptionalFor Markdown and HTML: {"chart.png": "<base64>"}, matching the names used in ![](chart.png).
modeoptionalFor a pdf source: image keeps each page as a picture instead of importing its text.
unlockoptionalFor a locked pdf. A locked PDF is refused rather than silently unlocked, and the refusal names the lock in a locked field. 1 removes it — keeping the document intact where it can, drawing a brand new PDF from it where it cannot. decrypt insists on keeping it intact; rebuild always redraws. Also takes as ?unlock=1 in the URL.
passwordoptionalThe password of a PDF that will not open without one. Send it here or as the X-Doc-Password header — never in the query string, which is refused, because a URL is written into every access log it passes through. There is no way to open a PDF whose password you do not have.
curl
curl -s https://docubend.com/api/v1/pdfs \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Nightly build report",
    "markdown": "# Nightly build\n\n**All green.** 412 tests, 0 failures."
  }'
JavaScript
const r = await fetch(`${BASE}/api/v1/pdfs`, {
  method: "POST",
  headers: { Authorization: `Bearer ${TOKEN}`,
             "Content-Type": "application/json" },
  body: JSON.stringify({
    title: "Nightly build report",
    markdown: "# Nightly build\n\n**All green.** 412 tests, 0 failures.",
  }),
});
const { document: doc } = await r.json();
console.log(doc.id, doc.links.open);
Python
doc = requests.post(f"{BASE}/api/v1/pdfs",
    headers={"Authorization": f"Bearer {TOKEN}"},
    json={
        "title": "Nightly build report",
        "markdown": "# Nightly build\n\n**All green.** 412 tests, 0 failures.",
    }, timeout=60).json()["document"]

print(doc["id"], doc["links"]["open"])

Response

201
{
  "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": {
      "self": "https://docubend.com/api/v1/documents/ba59b6ce5221411285daea90f4be16b6",
      "pdf": "https://docubend.com/api/v1/documents/ba59b6ce5221411285daea90f4be16b6.pdf",
      "svg": "https://docubend.com/api/v1/documents/ba59b6ce5221411285daea90f4be16b6.svg",
      "png": "https://docubend.com/api/v1/documents/ba59b6ce5221411285daea90f4be16b6.png",
      "model": "https://docubend.com/api/v1/documents/ba59b6ce5221411285daea90f4be16b6.json",
      "mermaid": "https://docubend.com/api/v1/documents/ba59b6ce5221411285daea90f4be16b6.mmd",
      "open": "https://docubend.com/d/ba59b6ce5221411285daea90f4be16b6"
    }
  }
}

Make a PDF from a picture#

POST /api/v1/pdfs documents:write

The same endpoint, with the file as the request body instead of base64 inside JSON — which is what you want for a dashboard screenshot or anything large. Set Content-Type to the file's type and put the title in the query string.

PNG and JPEG for pictures, application/pdf for a PDF, and text/markdown, text/html or text/vnd.mermaid for text.

curl
curl -s "https://docubend.com/api/v1/pdfs?title=Traffic%20dashboard" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: image/png" \
  --data-binary @dashboard.png
JavaScript
import { readFile } from "node:fs/promises";

const png = await readFile("dashboard.png");
const r = await fetch(
  `${BASE}/api/v1/pdfs?title=${encodeURIComponent("Traffic dashboard")}`, {
    method: "POST",
    headers: { Authorization: `Bearer ${TOKEN}`, "Content-Type": "image/png" },
    body: png,
  });
const { document: doc } = await r.json();
Python
with open("dashboard.png", "rb") as fh:
    doc = requests.post(f"{BASE}/api/v1/pdfs",
        params={"title": "Traffic dashboard"},
        headers={"Authorization": f"Bearer {TOKEN}",
                 "Content-Type": "image/png"},
        data=fh.read(), timeout=120).json()["document"]

Response

201
{
  "ok": true,
  "document": {
    "id": "7c1d0f4a9b2e4c6f8a0d3b5e7f9c1a2b",
    "title": "Traffic dashboard",
    "kind": "pdf",
    "pages": 1,
    "shapes": 1,
    "version": 2,
    "your_role": "owner",
    "links": {
      "…": "as above"
    }
  }
}

Replace what is in one#

PUT /api/v1/pdfs/{id} · /api/v1/diagrams/{id} documents:write

The same body as the two calls above, aimed at a document that already exists. Use it for anything that is regenerated on a schedule — the URL somebody bookmarked, or shared, or embedded keeps working, and the version they were looking at yesterday is still in the history.

Answers 200, with version raised.

curl
curl -s -X PUT https://docubend.com/api/v1/diagrams/6b35e4159e2e4b0e80ebae85f41cd6fd \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"title": "Release pipeline v2", "mermaid": "flowchart LR\n  A[Build] --> B[Ship]"}'
JavaScript
const r = await fetch(`${BASE}/api/v1/diagrams/${docId}`, {
  method: "PUT",
  headers: { Authorization: `Bearer ${TOKEN}`,
             "Content-Type": "application/json" },
  body: JSON.stringify({ title: "Release pipeline v2",
                         mermaid: "flowchart LR\n  A[Build] --> B[Ship]" }),
});
const { document: doc } = await r.json();
Python
doc = requests.put(f"{BASE}/api/v1/diagrams/{doc_id}",
    headers={"Authorization": f"Bearer {TOKEN}"},
    json={"title": "Release pipeline v2",
          "mermaid": "flowchart LR\n  A[Build] --> B[Ship]"},
    timeout=60).json()["document"]

print(doc["version"])   # one higher than it was

Response

200
{
  "ok": true,
  "document": {
    "id": "6b35e4159e2e4b0e80ebae85f41cd6fd",
    "title": "Release pipeline v2",
    "kind": "diagram",
    "pages": 1,
    "shapes": 2,
    "version": 3,
    "your_role": "owner",
    "links": {
      "…": "as above"
    }
  }
}