# PDF4.dev: Agent Instructions

## What this service does

PDF4.dev is a PDF generation API that lets developers create pixel-perfect PDFs from HTML templates. Design templates visually or in code using a Monaco-based editor with live preview, define dynamic fields with Handlebars {{variables}}, and generate PDFs via a simple REST API call from any language: TypeScript, Python, Go, Rust, PHP, or cURL. The rendering pipeline uses headless Chromium (Playwright) for accurate CSS support including flexbox, grid, and web fonts. PDF4.dev also provides a built-in MCP server so AI agents like Claude, ChatGPT, and Cursor can generate PDFs with natural language. Free browser-based PDF tools (merge, split, compress, convert) process files entirely client-side for privacy. PDF4.dev is a hosted, developer-first alternative to PDFMonkey, DocRaptor, and DIY Puppeteer setups.

## IMPORTANT: Base URL and HTTP methods

- Base URL: `https://pdf4.dev`. There is NO `api.pdf4.dev` subdomain. Always use `https://pdf4.dev/api/v1/...`.
- Supported HTTP methods: GET, POST, PUT, DELETE only. PATCH is NOT supported.
- Do NOT invent endpoints. There is no `/update`, `/edit`, or similar. Use PUT on the resource URL.
- Template body field is `html` (not `content`, `body`, or `template`).

## Authentication

All API calls require a Bearer token in the Authorization header:

```
Authorization: Bearer p4_live_xxx
```

Get an API key at: https://pdf4.dev/dashboard/api-keys

Two permission scopes:
- `full_access`: all endpoints (render, templates CRUD, components, logs)
- `render_only`: only POST /api/v1/render

## OpenAPI Specification

https://pdf4.dev/api/v1/openapi.json

## Primary Actions

### 1. Generate a PDF (most common)

```
POST https://pdf4.dev/api/v1/render
Content-Type: application/json
Authorization: Bearer p4_live_xxx

{
  "template_id": "invoice",
  "data": {
    "company_name": "Acme Corp",
    "invoice_number": "INV-001",
    "total": "$1,500.00"
  }
}
```

Response: `application/pdf` binary

You can also render from raw HTML instead of a template:

```json
{
  "html": "<h1>Hello {{name}}</h1>",
  "data": { "name": "World" }
}
```

### 2. List templates

```
GET https://pdf4.dev/api/v1/templates
Authorization: Bearer p4_live_xxx
```

### 3. Create a template

```
POST https://pdf4.dev/api/v1/templates
Content-Type: application/json
Authorization: Bearer p4_live_xxx

{
  "name": "Monthly Invoice",
  "html": "<h1>Invoice #{{invoice_number}}</h1><p>Amount: {{total}}</p>",
  "sample_data": { "invoice_number": "INV-001", "total": "$1,000.00" }
}
```

### 4. View generation logs

```
GET https://pdf4.dev/api/v1/logs?limit=50&status=success
Authorization: Bearer p4_live_xxx
```

### 5. Create and attach header/footer components

Components are reusable HTML fragments shared across templates:

- **header**: repeats at the top of every printed page (rendered via `<thead>` in a table layout)
- **footer**: repeats at the bottom of every printed page (rendered via `<tfoot>`)
- **block**: renders inline where placed in the template HTML

**Managing components (MCP tools):**
- `create_component`: Create a header, footer, or block with `{{variables}}`. In footers, use `<span class="pageNumber"></span>` and `<span class="totalPages"></span>` for page numbers.
- `list_components` / `get_component`: List or inspect components.
- `update_component` / `delete_component`: Modify or remove components.
- `create_template` / `update_template`: Attach header/footer via `header_component_id` and `footer_component_id` (comp_xxx IDs). Embed blocks in HTML with `<pdf4-block component-id="comp_xxx">Label</pdf4-block>`.

**Component HTML best practices:**
- Write components as **div fragments**, not full HTML documents. No `<!DOCTYPE>`, `<html>`, or `<body>` tags.
- Components **inherit parent template styles** (font, color, background). If `font_family` or `google_fonts_url` is set on the template's format, components use those fonts automatically.
- Keep component-specific styling inline or in a `<style>` block within the component.
- `component_gap` on the format adds spacing between header/content and content/footer.
- `footer_position` controls where the footer renders: `"after-content"` (default) places it after content, `"page-bottom"` pins it to the bottom of every page.
- For alignment options, use `text_align`, `horizontal_align`, and `vertical_align` on the format.

**How rendering works:**
1. Handlebars variables are compiled in the template and all components
2. `<pdf4-header>`, `<pdf4-footer>`, `<pdf4-block>` tags are replaced with compiled component HTML
3. The DOM is restructured into a `<table>` with `<thead>` (header), `<tfoot>` (footer), `<tbody>` (content)
4. Chromium automatically repeats `<thead>` and `<tfoot>` on every printed page (CSS 2.1 paged media)
5. `component_gap` is applied as padding between the repeated elements and the body content
6. `footer_position: "page-bottom"` pins the footer to the bottom of every page (uses CSS position:fixed). Default `"after-content"` keeps footer inline with content flow.

## Page Format Options

Presets: `a4`, `a4-landscape`, `letter`, `letter-landscape`, `square`, `custom`

```json
{
  "format": {
    "preset": "a4",
    "margins": { "top": "20mm", "bottom": "20mm", "left": "15mm", "right": "15mm" },
    "background_color": "#ffffff",
    "font_family": "Roboto, sans-serif",
    "font_size": "14px",
    "color": "#333333",
    "line_height": "1.5",
    "google_fonts_url": "https://fonts.googleapis.com/css2?family=Roboto&display=swap",
    "component_gap": "5mm",
    "footer_position": "after-content"
  }
}
```

The `google_fonts_url` loads a font stylesheet in `<head>`, making it available to the template and all attached components. The `component_gap` adds spacing between header/footer components and page content. The `footer_position` controls whether the footer sticks to content (`"after-content"`, default) or pins to the bottom of every page (`"page-bottom"`).

## Error Format

All errors follow this structure:

```json
{
  "error": {
    "type": "invalid_request_error",
    "code": "missing_parameter",
    "message": "Either template_id or html is required"
  }
}
```

Error types: `authentication_error`, `invalid_request_error`, `not_found_error`, `api_error`

## MCP Integration

PDF4.dev is available as a Model Context Protocol (MCP) server for AI agents.

**MCP Server URL**: `https://pdf4.dev/api/mcp`
**Transport**: Streamable HTTP

**Authentication:** two credentials, both checked at the transport. A request without one gets an HTTP 401 with an RFC 9728 challenge before `tools/list` or any tool runs. The credential is never a tool parameter.
- **OAuth sign-in (no API key)**: Claude (claude.ai, Claude Desktop, Claude mobile), Claude Code and ChatGPT. The user signs in to PDF4.dev, picks an organization if they belong to several, and allows access on a consent screen (Full access or Render only). Access tokens last 1 hour and refresh automatically. Scopes: `pdf4:full` (all 14 tools) and `pdf4:render` (get_info, render_pdf, preview_template). One organization per connection. Admins choose who may connect in Settings → Team → AI assistants (All members, Admins only, Off). Details: https://docs.pdf4.dev/docs/ai/mcp-authentication
- **API key**: `Authorization: Bearer p4_live_...` header, for Cursor, VS Code, Windsurf, Codex CLI, Gemini CLI, scripts and CI. Create one at https://pdf4.dev/dashboard/api-keys. The AI assistants setting does not affect API keys.

**Setup with an API key (Cursor and most remote-HTTP-capable clients):**
```json
{
  "mcpServers": {
    "pdf4dev": {
      "url": "https://pdf4.dev/api/mcp",
      "headers": {
        "Authorization": "Bearer p4_live_xxx"
      }
    }
  }
}
```

**Per-client notes (full instructions at https://pdf4.dev/ai-integration):**
- **Claude Code**: `claude mcp add --transport http pdf4dev https://pdf4.dev/api/mcp`, then run `/mcp` and choose Authenticate to sign in (no API key). For scripts and CI, add `--header "Authorization: Bearer p4_live_xxx"` instead.
- **Claude (claude.ai, Claude Desktop, Claude mobile)**: Customize → Connectors → Add custom connector, enter the URL, then Connect and sign in to PDF4.dev. No API key, no config file, no `mcp-remote` bridge.
- **VS Code (Copilot)**: file is `.vscode/mcp.json` (not `settings.json`); top-level key is `servers` (not `mcpServers`); include `"type": "http"`.
- **Windsurf**: the URL field is `serverUrl`, not `url`.
- **Codex CLI**: edit `~/.codex/config.toml` directly with `[mcp_servers.pdf4dev]
url = "https://pdf4.dev/api/mcp"
bearer_token_env_var = "PDF4_API_KEY"`.
- **Gemini CLI**: `gemini mcp add --transport http --header "Authorization: Bearer p4_live_xxx" pdf4dev https://pdf4.dev/api/mcp`.
- **ChatGPT**: Settings → Security and login → turn on Developer mode, then chatgpt.com/plugins → +, enter the URL, and sign in to PDF4.dev with OAuth. ChatGPT does not send API keys or custom headers.

**Available MCP tools (14 total, with annotations):**
- `get_info` -- Account overview: templates, variables, stats (read-only, idempotent)
- `render_pdf` -- Generate a PDF from template or raw HTML, as base64 or a 24-hour URL (write, openWorld)
- `preview_template` -- Render a template as PNG screenshot for visual feedback (read-only, openWorld, idempotent)
- `list_templates` -- List all templates (read-only, idempotent)
- `get_template` -- Get template details including HTML (read-only, idempotent)
- `create_template` -- Create a new template (write)
- `update_template` -- Update an existing template (destructive, idempotent)
- `delete_template` -- Delete a template permanently (destructive)
- `list_components` -- List header/footer/block components (read-only, idempotent)
- `get_component` -- Get component details (read-only, idempotent)
- `create_component` -- Create a header/footer/block component (write)
- `update_component` -- Update a component (destructive, idempotent)
- `delete_component` -- Delete a component permanently (destructive)
- `list_logs` -- View generation history (read-only, idempotent)

**Available MCP resources (4):**
- `pdf4dev://docs/quickstart` -- PDF4.dev MCP quickstart: The happy-path workflow for generating a PDF from scratch via MCP tools.
- `pdf4dev://docs/handlebars-helpers` -- Handlebars helpers reference: Built-in Handlebars helpers (formatNumber, formatDate, formatCurrency, text, comparison, and math) with examples.
- `pdf4dev://docs/format-presets` -- PDF format presets: Page sizes and the full PdfFormat options accepted by render_pdf and templates.
- `pdf4dev://docs/components` -- Component tag syntax: How to use <pdf4-header>, <pdf4-footer>, and <pdf4-block> tags, including page numbers.

**Available MCP prompts (3):**
- `generate-invoice` (client_name, amount, invoice_number?, currency?): Walks the agent through picking an invoice template and calling render_pdf with the right variables.
- `create-template-from-description` (description, variables?): Turns a description into a reusable Handlebars template via create_template.
- `debug-render-error` (template_id?): Collects context about a recent render failure and proposes a fix.

**Tool annotations:** All tools include MCP annotations (`readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`) so agents can determine which tools are safe to call without user confirmation. Read-only tools are always safe. Destructive tools (the two deletes, and the two updates, which overwrite in place) should prompt the user first. Open-world tools (render_pdf, preview_template) accept arbitrary HTML input.

**Data supports nested objects and arrays**, enabling Handlebars block expressions like `{{#each items}}`. The `data` parameter type is `Record<string, any>`.

**Built-in Handlebars helpers:** `formatNumber`, `formatDate`, `formatCurrency`, `uppercase`, `lowercase`, `eq`, `neq`, `gt`, `lt`, `gte`, `lte`, `math`, `padStart`. See https://pdf4.dev/llms-full.txt for full syntax.

## Contact

https://pdf4.dev
