docubend
Documentation

Locked PDFs

A locked PDF is refused rather than silently unlocked. The refusal names the lock, so your code decides rather than having it decided.

Why it refuses#

A locked PDF is somebody else's decision about their own document, and an API that silently strips it the moment one arrives has made that decision for them without being asked. So an ordinary post of a locked file comes back with the lock named, and unlock=1 is the caller saying yes.

Two kinds of lock#

StatusBodyWhat it is
Restrictions409"locked": "restrictions"Opens for everyone and forbids copying and editing. Most PDFs people call locked are this, and it comes off with no password because it is not one.
Password422"locked": "password"Will not open at all. You need the password. There is nothing here that guesses one.
409
{
  "ok": false,
  "detail": "that PDF is locked against copying and editing. Post it again with unlock=1 to have the lock removed first.",
  "locked": "restrictions",
  "opens": true,
  "allows": { "print": true, "copy": false, "modify": false },
  "unlock": "post again with ?unlock=1"
}

The three modes#

What it doesWhat it costs
unlock=1Remove the lock. Keeps the document intact where it can, draws a new PDF from it where it cannot.Nothing, usually. If it had to redraw, the response says so in warnings.
unlock=decryptInsist on keeping the document intact.Fails rather than redrawing.
unlock=rebuildAlways draw a brand new PDF from it.Text stays text. Form fields and annotations become part of the page.

The password#

The X-Doc-Password header, or password in the JSON body. Never in the query string — a URL is written into every access log it passes through, and this endpoint refuses a request that carries one there rather than quietly honouring it. A caller who puts a password in a URL has already written it into their own logs, and finding that out from an error is better than finding it out later.

curl
# refused first, on purpose — the body names the lock
curl -s https://docubend.com/api/v1/pdfs \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/pdf" \
  --data-binary @locked.pdf

# yes, take it off
curl -s "https://docubend.com/api/v1/pdfs?unlock=1&title=Report" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/pdf" \
  --data-binary @locked.pdf
JavaScript
const r = await fetch(`${BASE}/api/v1/pdfs?unlock=1&title=Report`, {
  method: "POST",
  headers: { Authorization: `Bearer ${TOKEN}`,
             "Content-Type": "application/pdf" },
  body: await readFile("locked.pdf"),
});
if (r.status === 409) {
  const { locked, allows } = await r.json();   // "restrictions"
}
Python
r = requests.post(f"{BASE}/api/v1/pdfs",
    params={"unlock": 1, "title": "Report"},
    headers={"Authorization": f"Bearer {TOKEN}",
             "Content-Type": "application/pdf"},
    data=open("locked.pdf", "rb").read(), timeout=120)
if r.status_code == 409:
    print(r.json()["locked"])   # "restrictions"