PDF options
Paper sizes, margins, scaling, print behaviour, metadata, accessibility and PDF/UA, PDF/A archiving, attachments, protection and wait strategies for PDF output.
PDF options control how a page is printed: its size, margins, what is waited for and what metadata the file carries. Send them as output.pdf in a render request, or as pdf in the convenience endpoints.
{
"input": { "type": "template", "template_id": "tpl_01J9ZK3M7Q8V5W2X4Y6Z8A0B1C" },
"data": { "number": "2026-0042" },
"output": {
"format": "pdf",
"pdf": {
"paper": { "size": "A4" },
"margin": { "top": "20mm", "right": "15mm", "bottom": "20mm", "left": "15mm" },
"print_background": true
}
}
}Templates store their own defaults, set in the editor's Options tab. Options you send with a request override the template's defaults.
A PDF template can also store a default e-invoice, einvoice: {"profile": "EN16931", "invoice_path": "invoice"} next to pdf in its options, set in the Options tab under E-invoice (ZUGFeRD / Factur-X). Every render of it is then a Factur-X / ZUGFeRD PDF/A-3b unless the request sends its own output.einvoice, or "einvoice": null to render a plain PDF. On plans without e-invoicing it is skipped with the warning einvoice_skipped_plan. See Make a template an e-invoice by default.
Page setup
| Option | Type | Default | Description |
|---|---|---|---|
paper.size | string | "A4" | A named size: A0 to A6, B4, B5, Letter, Legal, Tabloid or Ledger |
paper.width, paper.height | string | — | A custom size instead of size, as CSS lengths, for example "80mm" and "200mm" |
orientation | string | "portrait" | portrait or landscape. Landscape swaps width and height. |
margin.top, margin.right, margin.bottom, margin.left | string | not set | Page margins as CSS lengths. When a header or footer is enabled and margins are not set, the margin follows its height. |
scale | number | 1.0 | Scales the rendered content between 0.1 and 2.0 |
prefer_css_page_size | boolean | false | Take the page size from the CSS @page rule instead of paper, which also allows different sizes per page |
single_page | boolean | false | Produce a single continuous page as tall as the content |
page_ranges | string | all pages | Keep only these pages, for example "1-3,5" |
Paper sizes
| Size | Millimetres | Inches |
|---|---|---|
| A0 | 841 × 1189 | 33.11 × 46.81 |
| A1 | 594 × 841 | 23.39 × 33.11 |
| A2 | 420 × 594 | 16.54 × 23.39 |
| A3 | 297 × 420 | 11.69 × 16.54 |
| A4 | 210 × 297 | 8.27 × 11.69 |
| A5 | 148 × 210 | 5.83 × 8.27 |
| A6 | 105 × 148 | 4.13 × 5.83 |
| B4 | 250 × 353 | 9.84 × 13.90 |
| B5 | 176 × 250 | 6.93 × 9.84 |
| Letter | 215.9 × 279.4 | 8.5 × 11 |
| Legal | 215.9 × 355.6 | 8.5 × 14 |
| Tabloid | 279.4 × 431.8 | 11 × 17 |
| Ledger | 431.8 × 279.4 | 17 × 11 |
Custom sizes are useful for labels and receipts:
{ "paper": { "width": "4in", "height": "6in" } }CSS length units
Sizes and margins are CSS length strings with an explicit unit:
| Unit | Meaning |
|---|---|
mm, cm | Millimetres, centimetres |
in | Inches |
pt | Points, 1/72 inch |
pc | Picas, 12 points |
px | CSS pixels, 1/96 inch |
For example "20mm", "0.75in" and "48px" are valid; a bare number is not.
Content and print behaviour
| Option | Type | Default | Description |
|---|---|---|---|
print_background | boolean | true | Print background colours and images |
emulate_media | string | "print" | print applies your @media print rules; screen renders the page as a browser screen would, which is often what you want for url input |
javascript | boolean | true | Run scripts in the page. Turn it off for untrusted HTML or to speed up renders. |
timezone | string | "UTC" | IANA time zone used by scripts in the page |
locale | string | "en-US" | Locale used by scripts in the page, for example for Intl formatting |
Backgrounds only print when the element also allows it in CSS. Add print-color-adjust: exact (with the -webkit- prefixed property for older engines) to elements with coloured backgrounds.
Headers and footers
header and footer take the same fields. Headers and footers explains both modes in detail.
| Option | Type | Default | Description |
|---|---|---|---|
header.enabled | boolean | false | Draw the header on every page |
header.html | string | none | HTML for the header, with template expressions and the page, pages, date, title and url classes |
header.left, header.center, header.right | string | none | Simple text mode, with the {{page}}, {{pages}}, {{date}} and {{title}} tokens |
header.font_size | string | "9px" | Base font size of the header document |
header.height | string | "15mm" | Height reserved for the header |
Accessibility and archiving
| Option | Type | Default | Description |
|---|---|---|---|
tagged | boolean | false | Produce a tagged PDF with a structure tree, so assistive technology can read the document. Use semantic HTML (headings, lists, table headers, alt text) and set metadata.lang. The render's result.conformance.pdfua holds a PDF/UA-1 report for information. |
pdfua | string | null | "1": deliver a PDF/UA-1 document, validated with veraPDF. The render fails if the check fails. Implies tagged. See PDF/UA. |
outline | boolean | false | Add PDF bookmarks generated from the h1 to h6 headings |
pdfa | string | null | "2b" or "3b": convert the PDF to PDF/A and validate it with veraPDF. See PDF/A. |
attachments | array | [] | Up to 20 files embedded in the PDF. See Attachments. |
Compliance features
PDF/UA and PDF/A output and e-invoices (Factur-X, ZUGFeRD and XRechnung) are available, each with a validation report.
PDF/A
PDF/A (ISO 19005) is the standard for PDFs that must stay readable for decades, such as invoices, contracts and statements. Set pdfa and the engine converts the printed PDF, then validates it with veraPDF:
{
"input": { "type": "template", "template_id": "tpl_01J9ZK3M7Q8V5W2X4Y6Z8A0B1C" },
"data": { "number": "2026-0042" },
"output": {
"format": "pdf",
"pdf": { "pdfa": "2b", "metadata": { "title": "Invoice 2026-0042", "lang": "de-DE" } }
}
}| Level | Standard | Use it for |
|---|---|---|
2b | PDF/A-2b (ISO 19005-2), conformance level B: reliable visual appearance | Archiving business documents |
3b | PDF/A-3b (ISO 19005-3) | The same as 2b, plus embedded files of any type. E-invoices use it to carry their XML. |
The conversion adds an sRGB output intent and writes the PDF/A identification and the document metadata into XMP. It doesn't render the page again, so text, images and layout stay as printed. PDF/A output costs no additional renders. It is included in the Growth plan and higher; on Free and Starter, pdfa fails with 402 plan_feature_unavailable.
What to know:
- Fonts must be embedded. If a font can't be embedded, the render fails with
422 pdfa_conversion_failedand the message names the font. - No encryption. PDF/A forbids it, so
protecttogether withpdfafails with400 validation_error. - Report.
result.conformance.pdfaon the render holds the level, the status (passed,failedorunavailable) and any findings. If veraPDF finds the file non-compliant, the render fails withpdfa_conversion_failed, costs nothing, and the failed rules are listed in the report. See validation report. - Unavailable validator. If veraPDF doesn't answer in time, the file is delivered unvalidated, with the warning
pdfa_validation_unavailableandstatus: "unavailable". - Test renders. The TEST watermark is drawn as vector outlines and stamped before the conversion, so a test PDF is conformant too and the report describes the delivered file.
Convert an existing PDF
POST /v1/pdf-tools/pdfa converts a PDF that wasn't rendered by Dynamic Document API and validates the result:
curl https://api-eu.dynamicdocumentapi.com/v1/pdf-tools/pdfa \
-H "Authorization: Bearer $DYNAMIC_DOCUMENT_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{ "source": { "url": "https://example.com/contract.pdf" }, "level": "2b", "filename": "contract-pdfa.pdf" }'| Field | Description |
|---|---|
source | The PDF. Exactly one of url, render_id (with an optional file_id), upload_id or data_uri |
level | 2b or 3b |
filename | Name of the converted file |
The response is a render with the converted file and the report in result.conformance.pdfa. It accepts the same mode, delivery, webhook, reference, metadata and test fields as the other PDF tools. Conversion is best effort: it can't embed fonts the source doesn't contain, and it can't process encrypted PDFs. Either fails with pdfa_conversion_failed. A conversion is billed as 1 render, and failed ones cost nothing. Like pdfa, this endpoint needs the Growth plan or higher.
PDF/UA
PDF/UA (ISO 14289-1) is the standard for accessible PDFs: screen readers and other assistive technology can read them in the right order, with headings, lists, tables, link texts and image descriptions. Set pdfua to "1" and the render either delivers a PDF that passes veraPDF's PDF/UA-1 checks or fails:
{
"input": { "type": "template", "template_id": "tpl_01J9ZK3M7Q8V5W2X4Y6Z8A0B1C" },
"data": { "number": "2026-0042" },
"output": {
"format": "pdf",
"pdf": { "pdfua": "1", "metadata": { "title": "Invoice 2026-0042", "lang": "en-GB" } }
}
}After rendering, Dynamic Document API fixes what the browser's tagging leaves open: link descriptions, list structure, headers and footers marked as artifacts that screen readers skip, and the PDF/UA identification. What only your template can provide:
- A title and a language. Set
metadata.titleor a<title>element, andmetadata.langor<html lang>. Withpdfuathere is no default language: a document without one fails the check. - Text alternatives.
alttext on meaningful images, andalt=""on decorative ones. - Structure. Real tables with
<th scope>, headings in order, and lists as<ul>or<ol>.
Content that is hidden from the accessibility tree (aria-hidden, alt="", CSS content, borders) becomes a decoration that screen readers skip, so meaningful text belongs in the markup.
What to know:
- Report.
result.conformance.pdfuaholds the standard, the status (passed,failedorunavailable) and the rules that failed. A render that fails the check fails with422 pdfua_validation_failedand the report, and costs nothing. - Unavailable validator. If veraPDF doesn't answer in time, the file is delivered unvalidated, with the warning
pdfua_validation_unavailableandstatus: "unavailable". - Limits of the check. veraPDF checks what a machine can check. Whether alt text is meaningful and the reading order is right is yours to review.
- Combinations.
pdfuaworks withpdfa, e-invoices, attachments andprotect, which keeps access for assistive technology. PDFs from canvas templates can't be PDF/UA (400 validation_error). - Previews and publishing. Editor previews show the report but never fail. Publishing warns when the template's test render fails the check.
- Plan. PDF/UA is included in the Growth plan and higher; on Free and Starter,
pdfuafails with402 plan_feature_unavailable.
Attachments
attachments embeds files in the PDF, for example the supporting documents of an invoice. Each entry has:
| Field | Description |
|---|---|
url, data_uri or upload_id | Exactly one source. A url is fetched like an asset. A data_uri may be at most 5 MB, and all of them together at most 8 MB of base64; use an upload for more. |
name | File name in the PDF: 1 to 255 characters, without /, \ or control characters, and unique in the document, ignoring case |
mime_type | Optional. Derived from the name's extension, else application/octet-stream |
description | Optional description shown by PDF readers |
relationship | Optional: Source, Data, Alternative, Supplement or Unspecified (the default) |
{
"output": {
"format": "pdf",
"pdf": {
"attachments": [
{ "url": "https://example.com/timesheet.csv", "name": "timesheet.csv", "relationship": "Supplement" }
]
}
}
}Up to 20 files, each at most 10 MiB and 25 MiB together. A file that can't be fetched fails the render with 422 attachment_failed, which names it.
Attachments work with plain PDFs and with pdfa: "3b", where they sit next to an e-invoice's XML. pdfa: "2b" can't carry them (400 validation_error), and an attachment can't take the name of the embedded e-invoice (factur-x.xml, xrechnung.xml). With protect, the files are embedded before encryption. Editor previews and test renders leave attachments out. Uploads expire after 24 hours, so a template's default attachments use url or data_uri.
Metadata
| Option | Type | Default | Description |
|---|---|---|---|
metadata.title | string | none | Document title stored in the PDF |
metadata.author | string | none | Author |
metadata.subject | string | none | Subject |
metadata.keywords | array of strings | [] | Keywords |
metadata.creator | string | none | The application that created the document |
metadata.lang | string | "en" | Document language as a BCP 47 tag, such as "de-DE". It sets the lang attribute of the rendered HTML and the document language in the PDF. With pdfua there is no default: set it here or in <html lang>. |
Security
| Option | Type | Default | Description |
|---|---|---|---|
protect.user_password | string | none | Password needed to open the document |
protect.owner_password | string | none | Password needed to change permissions |
protect.permissions.print | boolean | true | Allow printing |
protect.permissions.print_high_res | boolean | true | Allow printing at full quality |
protect.permissions.copy | boolean | true | Allow copying text and images |
protect.permissions.modify | boolean | false | Allow changing the content |
protect.permissions.annotate | boolean | false | Allow adding comments and annotations |
protect.permissions.fill_forms | boolean | true | Allow filling in form fields |
protect.permissions.assemble | boolean | false | Allow inserting, rotating and deleting pages |
Protecting a document restricts editing, not reading: a permission you leave out keeps its default. To protect an existing PDF, use the protect tool.
Documents are encrypted with AES-256. Passwords are part of the request body, so keep them out of logs on your side and use request-log redaction rules if you enable request logging. Protection can't be combined with pdfa or an e-invoice, because PDF/A forbids encryption. With pdfua, protection keeps the permission that lets assistive technology read the text.
Waiting and scripting
Rendering starts once the page is ready. Choose a strategy that matches how your page loads:
wait.until | Ready when |
|---|---|
load | The page's load event has fired |
networkidle | No request has been in flight for 500 ms. The default, and a good fit for pages with remote images or fonts. |
selector | An element matching wait.selector exists |
ready_flag | The page has set window.__DYNAMIC_DOCUMENT_API_READY__ = true |
delay | wait.delay_ms milliseconds have passed after load |
| Option | Type | Default | Description |
|---|---|---|---|
wait.until | string | "networkidle" | Wait strategy, see above |
wait.selector | string | none | CSS selector to wait for, with until: "selector" |
wait.delay_ms | number | 0 | Extra delay in milliseconds, up to 10000 |
wait.timeout_ms | number | 30000 | Maximum time for each wait step |
After the wait condition, the renderer always waits for web fonts to load and images to decode before printing, so a fixed delay is rarely necessary. When a wait step times out, the render continues with the page as it is and records a warning, so check the render's warnings if output looks incomplete. A render that cannot continue fails with 422 wait_timeout.
For pages that build content with JavaScript, such as charts, set the ready flag when you are done:
<script>
renderChart(data).then(() => {
window.__DYNAMIC_DOCUMENT_API_READY__ = true;
});
</script>{ "wait": { "until": "ready_flag", "timeout_ms": 20000 } }Examples
An A4 invoice with a footer and metadata:
{
"paper": { "size": "A4" },
"margin": { "top": "18mm", "right": "16mm", "bottom": "22mm", "left": "16mm" },
"print_background": true,
"footer": {
"enabled": true,
"html": "<div style=\"width:100%;padding:0 16mm;display:flex;justify-content:space-between;font-size:8pt;color:#555\"><span>Example GmbH</span><span>Page <span class=\"page\"></span> of <span class=\"pages\"></span></span></div>",
"height": "14mm"
},
"metadata": { "title": "Invoice 2026-0042", "author": "Example GmbH", "lang": "de-DE" }
}An 80 mm receipt on one continuous page, where the declared height is replaced by the content height:
{
"paper": { "width": "80mm", "height": "200mm" },
"margin": { "top": "4mm", "right": "4mm", "bottom": "4mm", "left": "4mm" },
"single_page": true
}A landscape report with bookmarks and a tagged structure:
{
"paper": { "size": "A3" },
"orientation": "landscape",
"scale": 0.9,
"outline": true,
"tagged": true,
"metadata": { "title": "Quarterly report", "lang": "en-GB" }
}A shipping label at 4 × 6 inches, printed edge to edge:
{
"paper": { "width": "4in", "height": "6in" },
"margin": { "top": "0mm", "right": "0mm", "bottom": "0mm", "left": "0mm" },
"print_background": true
}A protected document with printing allowed but copying and editing blocked:
{
"paper": { "size": "A4" },
"protect": {
"user_password": "…",
"owner_password": "…",
"permissions": { "print": true, "copy": false, "modify": false }
}
}Related pages
- Headers and footers for running headers, page numbers and logos
- Pagination basics for page breaks, repeating table headers and layout problems
- Image and screenshot options for PNG and JPEG output
- E-invoicing for Factur-X, ZUGFeRD and XRechnung
- PDF tools for merging, splitting, protecting and converting existing PDFs
Batches
Render one template for many items with POST /v1/batches, from JSON and CSV items and the combined ZIP or merged PDF to progress, cancel and retry, webhooks, billing and limits.
PDF tools
Merge, split, rotate, protect and unlock existing PDFs and read or change their metadata, from sources and page lists to delivery, encrypted files, errors and billing.