# PDF4.dev: Complete API Documentation > PDF generation API: Create HTML templates with Handlebars variables, preview them live, and render PDFs via REST API from any language. ## IMPORTANT: Base URL and HTTP methods - **Base URL: `https://pdf4.dev`**. There is NO `api.pdf4.dev` subdomain. It does not exist. Always use `https://pdf4.dev/api/v1/...`. - **Supported HTTP methods: GET, POST, PUT, DELETE only.** PATCH is NOT supported on any endpoint. - **Do NOT invent endpoints.** There is no `/update`, `/edit`, or similar path. To update a template, use `PUT /api/v1/templates/{id}`. - **Template body field is `html`**, not `content`, `body`, or `template`. ## Overview PDF4.dev lets you: 1. Create HTML templates in a visual dashboard with Handlebars `{{variables}}` 2. Generate PDFs via REST API by passing template ID + data 3. Use 24 free browser-based PDF tools (compress, merge, split, etc.) Base URL: `https://pdf4.dev` (NOT `api.pdf4.dev`) OpenAPI Spec: `https://pdf4.dev/api/v1/openapi.json` --- ## Authentication All API requests require a Bearer token. Create an API key in the dashboard Settings page. ``` Authorization: Bearer p4_live_xxx ``` API keys have two permission scopes: - `full_access`: all endpoints (render, templates CRUD, logs, stats) - `render_only`: only `POST /api/v1/render` Keys are hashed with SHA-256 server-side. The full token is shown only once at creation. --- ## Endpoints ### POST /api/v1/render Generate a PDF from a saved template or raw HTML. **Request:** ```bash curl -X POST https://pdf4.dev/api/v1/render \ -H "Authorization: Bearer p4_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "template_id": "invoice", "data": { "company_name": "Acme Corp", "invoice_number": "INV-2025-001", "total": "$4,500.00" } }' \ --output invoice.pdf ``` **Parameters:** | Field | Type | Required | Description | |-------|------|----------|-------------| | template_id | string | One of template_id or html | Template ID (tmpl_xxx) or slug | | html | string | One of template_id or html | Raw HTML with {{handlebars}} variables | | data | object | No | Data to replace {{variables}}. Supports nested objects and arrays for `{{#each}}` blocks | | format | PdfFormat | No | Page format override (see below) | **Response:** `application/pdf` binary (Content-Type: application/pdf) **Example with raw HTML:** ```bash curl -X POST https://pdf4.dev/api/v1/render \ -H "Authorization: Bearer p4_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "html": "

Hello {{name}}

Your order #{{order_id}} is confirmed.

", "data": { "name": "John", "order_id": "12345" } }' \ --output document.pdf ``` **Example with custom format:** ```json { "html": "

Landscape Report

", "format": { "preset": "a4-landscape", "margins": { "top": "10mm", "bottom": "10mm", "left": "15mm", "right": "15mm" }, "background_color": "#ffffff", "font_family": "Inter, sans-serif", "font_size": "14px", "text_align": "left" } } ``` --- ### GET /api/v1/templates List all templates belonging to the authenticated user. Requires `full_access` scope. **Request:** ```bash curl https://pdf4.dev/api/v1/templates \ -H "Authorization: Bearer p4_live_xxx" ``` **Response:** JSON array of Template objects. --- ### POST /api/v1/templates Create a new template. A URL-safe slug is auto-generated from the name. Requires `full_access` scope. **Request:** ```bash curl -X POST https://pdf4.dev/api/v1/templates \ -H "Authorization: Bearer p4_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "name": "Monthly Invoice", "html": "

Invoice #{{invoice_number}}

Amount: {{total}}

", "sample_data": { "invoice_number": "INV-001", "total": "$1,000.00" }, "pdf_format": { "preset": "a4", "margins": { "top": "20mm", "bottom": "20mm", "left": "15mm", "right": "15mm" } } }' ``` **Parameters:** | Field | Type | Required | Description | |-------|------|----------|-------------| | name | string | Yes | Template display name | | html | string | No | HTML content with {{variables}} | | sample_data | object | No | Default values for preview | | pdf_format | PdfFormat | No | Page format configuration | | header_component_id | string | No | Header component ID (comp_xxx) | | footer_component_id | string | No | Footer component ID (comp_xxx) | **Response:** 201: Template object. --- ### GET /api/v1/templates/{id} Get a single template by ID (tmpl_xxx) or slug. Requires `full_access` scope. ```bash curl https://pdf4.dev/api/v1/templates/invoice \ -H "Authorization: Bearer p4_live_xxx" ``` --- ### PUT /api/v1/templates/{id} Update a template. Only provided fields are updated. Requires `full_access` scope. ```bash curl -X PUT https://pdf4.dev/api/v1/templates/tmpl_abc123 \ -H "Authorization: Bearer p4_live_xxx" \ -H "Content-Type: application/json" \ -d '{ "html": "

Updated Invoice #{{number}}

", "sample_data": { "number": "INV-002" } }' ``` --- ### DELETE /api/v1/templates/{id} Permanently delete a template. Requires `full_access` scope. ```bash curl -X DELETE https://pdf4.dev/api/v1/templates/tmpl_abc123 \ -H "Authorization: Bearer p4_live_xxx" ``` **Response:** `{ "deleted": true }` --- ### POST /api/v1/templates/{id}/duplicate Create a copy of a template with "(copy)" appended to its name. Requires `full_access` scope. ```bash curl -X POST https://pdf4.dev/api/v1/templates/tmpl_abc123/duplicate \ -H "Authorization: Bearer p4_live_xxx" ``` **Response:** 201: New Template object. --- ## Components (Headers, Footers & Blocks) Components are reusable HTML fragments that can be shared across templates. Three types exist: | Type | Rendering | Use case | |------|-----------|----------| | `header` | Repeats at the top of every printed page via `` | Page headers, letterheads | | `footer` | Repeats at the bottom of every printed page via `` | Page footers, page numbers | | `block` | Rendered inline where placed | Reusable sections, signatures, disclaimers | Attach header/footer to templates via `header_component_id` and `footer_component_id`. Block components are referenced via `` tags directly in the template HTML (multiple blocks per template are allowed). **Components inherit parent styles.** They are div fragments injected into the template body, so they inherit the document's fonts, colors, background, and any CSS. If you set `font_family` or `google_fonts_url` on the template's PdfFormat, the components will use those fonts automatically. No need to re-declare fonts inside components. **Handlebars variables** in components are interpolated with the same data as the parent template. **Page numbers in footers:** use `` and ``. These are filled automatically by the PDF engine during rendering. ### How rendering works At render time, the pipeline: 1. Compiles Handlebars variables in the template and all components 2. Replaces ``, ``, `` tags with compiled component HTML 3. Restructures the DOM into a `` with `` (header), `` (footer), and `` (content) 4. Chromium automatically repeats `` at the top and `` at the bottom of every printed page 5. The `component_gap` setting (if set) adds padding between header/content and content/footer 6. The `footer_position` setting controls where the footer renders: `"after-content"` (default) places it right after the last content row, `"page-bottom"` pins it to the bottom of every page including the last ### Component HTML best practices - Write components as **div fragments**, not full HTML documents. No ``, ``, or `` tags needed. - Keep components **self-contained**: all component styling should be inline or in a `