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.
| Field | Type | What it is |
|---|---|---|
markdown | string | Markdown. Headings, bold, lists, tables, links, images, quotes and code fences. Set as real text, not a picture of it. |
html | string | The same closed vocabulary as Markdown, if you already have HTML. |
image | base64 string | A PNG or JPEG. Or post the bytes as the request body with the matching Content-Type. |
pdf | base64 string | An existing PDF, imported page by page so it becomes editable. "mode": "image" keeps each page as a picture instead. |
model | object | A document model of your own — the same shape .json gives back. |
Or post the bytes as the request body with a matching
Content-Type — image/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.
title | optional | What to call it. Defaults to Untitled PDF, or use ?title= when posting raw bytes. |
markdown / html / image / pdf / model | one required | The source. See what you can send. |
images | optional | For Markdown and HTML: {"chart.png": "<base64>"}, matching the names used in . |
mode | optional | For a pdf source: image keeps each page as a picture instead of importing its text. |
unlock | optional | For 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. |
password | optional | The 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 -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."
}'
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);
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
{
"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 -s "https://docubend.com/api/v1/pdfs?title=Traffic%20dashboard" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: image/png" \
--data-binary @dashboard.png
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();
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
{
"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 -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]"}'
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();
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
{
"ok": true,
"document": {
"id": "6b35e4159e2e4b0e80ebae85f41cd6fd",
"title": "Release pipeline v2",
"kind": "diagram",
"pages": 1,
"shapes": 2,
"version": 3,
"your_role": "owner",
"links": {
"…": "as above"
}
}
}