Protecting and unlocking documents
Sequence the security actions — encrypt, set restrictions, remove a known password, clear restrictions — for a real document workflow.
PDF security is two separate things, and getting the order right depends on knowing which is which. A password encrypts the file and controls who can open it. Restrictions are permission flags that control what a reader can do once it is open — print, copy, edit. This guide sequences the five security actions into a protect-then-distribute flow and an unlock flow.
The security actions
| Action | Route | What it does |
|---|---|---|
| Add password | add_password |
Encrypts the PDF and sets the password needed to open it. |
| Add restrictions | add_restrictions |
Sets permission flags (print, copy, edit) behind an owner password; can also set an open password. |
| Remove password | remove_password |
Decrypts a PDF when you supply its current password. |
| Remove restrictions | remove_restrictions |
Clears all permission flags. |
| Remove signatures | remove_signatures |
Strips cryptographic signatures and timestamps. |
Passwords vs. restrictions. add_password takes a single password — the
password a reader types to open the file. add_restrictions takes an
owner_password that guards the permission flags, plus an optional
user_password that acts as the open password. Set user_password and the
document needs a password to open; leave it out and anyone can open it but is
still bound by the permission flags.
The one rule that governs the order
Encrypt last. An encrypted PDF can’t be opened by any other action, because there is no field to supply its password. So run every content and permission step first — merge, watermark, restrictions — and add the open password as the final step. To modify a document that is already password-protected, remove the password first, process it, then re-protect.
Because of that rule, don’t stack add_password and then add_restrictions —
the restrictions call can’t open the just-encrypted file. When you need both
an open password and permission flags, set them in a single add_restrictions
call using owner_password and user_password together.
Protect a document for distribution
Lock down a report so it requires a password to open, and can’t be printed, copied, or edited — all in one call.
Do any merging, watermarking, or page work now, while the document is still unencrypted. See Chaining actions.
POST /v1/add_restrictions with an owner_password (guards the permissions),
a user_password (the open password), and the permission flags set to false
for what you want to forbid.
curl https://api.pdfblocks.com/v1/add_restrictions \
-H 'X-API-Key: your_api_key' \
-F file=@input.pdf \
-F owner_password='owner-secret' \
-F user_password='open-secret' \
-F allow_print=false \
-F allow_copy_content=false \
-F allow_change_content=false \
-o protected.pdf# pip install requests
import requests
with open('input.pdf', 'rb') as file:
response = requests.post(
'https://api.pdfblocks.com/v1/add_restrictions',
headers={'X-API-Key': 'your_api_key'},
files={'file': file},
data={
'owner_password': 'owner-secret',
'user_password': 'open-secret',
'allow_print': 'false',
'allow_copy_content': 'false',
'allow_change_content': 'false',
},
)
response.raise_for_status()
with open('protected.pdf', 'wb') as output:
output.write(response.content)// Node.js 18+
import { readFile, writeFile } from 'node:fs/promises';
const body = new FormData();
body.set('file', new Blob([await readFile('input.pdf')]), 'input.pdf');
body.set('owner_password', 'owner-secret');
body.set('user_password', 'open-secret');
body.set('allow_print', 'false');
body.set('allow_copy_content', 'false');
body.set('allow_change_content', 'false');
const response = await fetch('https://api.pdfblocks.com/v1/add_restrictions', {
method: 'POST',
headers: { 'X-API-Key': 'your_api_key' },
body,
});
if (!response.ok) throw new Error(`Request failed: ${response.status}`);
await writeFile('protected.pdf', Buffer.from(await response.arrayBuffer()));<?php
$ch = curl_init('https://api.pdfblocks.com/v1/add_restrictions');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['X-API-Key: your_api_key'],
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => [
'file' => new CURLFile('input.pdf', 'application/pdf'),
'owner_password' => 'owner-secret',
'user_password' => 'open-secret',
'allow_print' => 'false',
'allow_copy_content' => 'false',
'allow_change_content' => 'false',
],
]);
$pdf = curl_exec($ch);
if (curl_getinfo($ch, CURLINFO_HTTP_CODE) === 200) {
file_put_contents('protected.pdf', $pdf);
}# gem install http
require 'http'
response = HTTP
.headers('X-API-Key' => 'your_api_key')
.post('https://api.pdfblocks.com/v1/add_restrictions', form: {
file: HTTP::FormData::File.new('input.pdf'),
owner_password: 'owner-secret',
user_password: 'open-secret',
allow_print: 'false',
allow_copy_content: 'false',
allow_change_content: 'false',
})
File.write('protected.pdf', response.body) if response.status.success?package main
import (
"bytes"
"io"
"mime/multipart"
"net/http"
"os"
)
func main() {
var buf bytes.Buffer
form := multipart.NewWriter(&buf)
file, _ := os.Open("input.pdf")
defer file.Close()
part, _ := form.CreateFormFile("file", "input.pdf")
io.Copy(part, file)
form.WriteField("owner_password", "owner-secret")
form.WriteField("user_password", "open-secret")
form.WriteField("allow_print", "false")
form.WriteField("allow_copy_content", "false")
form.WriteField("allow_change_content", "false")
form.Close()
req, _ := http.NewRequest("POST",
"https://api.pdfblocks.com/v1/add_restrictions", &buf)
req.Header.Set("Content-Type", form.FormDataContentType())
req.Header.Set("X-API-Key", "your_api_key")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
out, _ := os.Create("protected.pdf")
defer out.Close()
io.Copy(out, res.Body)
}using var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Key", "your_api_key");
using var form = new MultipartFormDataContent
{
{ new ByteArrayContent(File.ReadAllBytes("input.pdf")), "file", "input.pdf" },
{ new StringContent("owner-secret"), "owner_password" },
{ new StringContent("open-secret"), "user_password" },
{ new StringContent("false"), "allow_print" },
{ new StringContent("false"), "allow_copy_content" },
{ new StringContent("false"), "allow_change_content" },
};
var response = await client.PostAsync(
"https://api.pdfblocks.com/v1/add_restrictions", form);
response.EnsureSuccessStatusCode();
await File.WriteAllBytesAsync(
"protected.pdf", await response.Content.ReadAsByteArrayAsync());If you only need an open password and no permission flags, add_password is the
simpler call:
curl https://api.pdfblocks.com/v1/add_password \
-H 'X-API-Key: your_api_key' \
-F file=@input.pdf \
-F password='pa$$word' \
-o encrypted.pdfBoth add_password and add_restrictions accept an encryption_algorithm of
AES-128 (the default) or AES-256. See Add
password and Add
restrictions for every field.
Unlock a document
To lift protection you hold the credentials for, decrypt first, then clear the permission flags.
POST /v1/remove_password with the document’s current password. The result
no longer requires a password to open, so later steps can read it.
POST /v1/remove_restrictions strips the permission flags, leaving an
unrestricted PDF.
# 1. Decrypt with the known password.
curl -sS https://api.pdfblocks.com/v1/remove_password \
-H 'X-API-Key: your_api_key' \
-F file=@protected.pdf \
-F password='open-secret' |
# 2. Clear the permission flags from the decrypted bytes.
curl -sS https://api.pdfblocks.com/v1/remove_restrictions \
-H 'X-API-Key: your_api_key' \
-F 'file=@-;filename=unlocked.pdf;type=application/pdf' \
-o unrestricted.pdf# pip install requests
import requests
BASE = 'https://api.pdfblocks.com'
HEADERS = {'X-API-Key': 'your_api_key'}
# 1. Decrypt with the known password.
with open('protected.pdf', 'rb') as file:
unlocked = requests.post(
f'{BASE}/v1/remove_password',
headers=HEADERS,
files={'file': file},
data={'password': 'open-secret'},
)
unlocked.raise_for_status()
# 2. Clear the permission flags from the decrypted bytes.
unrestricted = requests.post(
f'{BASE}/v1/remove_restrictions',
headers=HEADERS,
files={'file': ('unlocked.pdf', unlocked.content, 'application/pdf')},
)
unrestricted.raise_for_status()
with open('unrestricted.pdf', 'wb') as output:
output.write(unrestricted.content)remove_restrictions has no password field, so it works on a document that is
restricted but not password-protected. If a document has an open password, run
remove_password first — as the flow above does — so the restrictions call can
read it.
Remove signatures
A cryptographic signature locks a PDF: any edit invalidates it. If you must
re-process a signed document, strip the signatures and timestamps first with
remove_signatures. This does invalidate the signatures — which is unavoidable
once the content changes.
curl https://api.pdfblocks.com/v1/remove_signatures \
-H 'X-API-Key: your_api_key' \
-F file=@signed.pdf \
-o unsigned.pdf