> ## Documentation Index
> Fetch the complete documentation index at: https://pdfbase.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Templates

> Build reusable, data-driven PDF templates with Handlebars and CSS.

## Why templates?

Templates separate layout from data. Define your HTML once, then render it with different data on every API call. This means:

* **No HTML in your application code.** Your app sends JSON data, not markup.
* **Version control.** Templates are stored and versioned by PDFBase.
* **Schema validation.** Bad data is caught before rendering, not after.
* **Faster renders.** Templates are pre-parsed and cached.

## Creating a template

A template has three parts: **HTML** (with Handlebars placeholders), **schema** (expected data shape), and **defaults** (rendering options).

```bash theme={null}
curl -X POST https://api.pdfbase.dev/v1/templates \
  -H "Authorization: Bearer pk_live_xxx" \
  -d '{
    "name": "invoice",
    "html": "<!DOCTYPE html>...",
    "schema": {
      "number": "string",
      "customer": "string",
      "items": "array",
      "total": "string"
    },
    "defaults": {
      "format": "a4",
      "margin": "20mm"
    }
  }'
```

## Handlebars syntax

### Variables

```html theme={null}
<h1>Invoice #{{number}}</h1>
<p>Customer: {{customer}}</p>
```

### Loops

```html theme={null}
<table>
  {{#each items}}
  <tr>
    <td>{{this.name}}</td>
    <td>{{this.qty}}</td>
    <td>{{this.price}}</td>
  </tr>
  {{/each}}
</table>
```

### Conditionals

```html theme={null}
{{#if is_overdue}}
  <div class="overdue-banner">OVERDUE</div>
{{/if}}

{{#unless paid}}
  <p>Payment due by {{due_date}}</p>
{{/unless}}
```

### Built-in helpers

| Helper           | Usage                                   | Output                   |
| ---------------- | --------------------------------------- | ------------------------ |
| `formatDate`     | `{{formatDate due_date "MMM D, YYYY"}}` | `Jun 19, 2026`           |
| `formatNumber`   | `{{formatNumber amount "0,0.00"}}`      | `1,250.00`               |
| `formatCurrency` | `{{formatCurrency total "USD"}}`        | `$1,250.00`              |
| `uppercase`      | `{{uppercase status}}`                  | `PAID`                   |
| `lowercase`      | `{{lowercase email}}`                   | `john@example.com`       |
| `truncate`       | `{{truncate description 50}}`           | `This is a long desc...` |

## Schema validation

The schema defines what data the template expects. When you render, PDFBase validates the data against the schema first:

```json theme={null}
{
  "schema": {
    "number": "string",
    "customer": "string",
    "items": "array",
    "due_date": "date",
    "total": "string",
    "is_overdue": "boolean",
    "notes": "string"
  }
}
```

### Supported types

| Type      | Validates            |
| --------- | -------------------- |
| `string`  | Any string value     |
| `number`  | Integer or float     |
| `boolean` | `true` or `false`    |
| `date`    | ISO 8601 date string |
| `array`   | Any array            |
| `object`  | Any object           |

If the data doesn't match, you get a `422` with specific field-level errors before any rendering happens.

## Templates-as-code (CLI)

Instead of managing templates through the API, use the CLI to push templates from your repo:

### Project structure

```
your-project/
  pdfbase.json
  pdf-templates/
    invoice.html
    invoice.css
    receipt.html
    receipt.css
```

### pdfbase.json

```json theme={null}
{
  "templates": {
    "invoice": {
      "file": "./pdf-templates/invoice.html",
      "style": "./pdf-templates/invoice.css",
      "schema": {
        "number": "string",
        "customer": "string",
        "items": "array",
        "total": "string"
      },
      "defaults": { "format": "a4", "margin": "20mm" }
    },
    "receipt": {
      "file": "./pdf-templates/receipt.html",
      "style": "./pdf-templates/receipt.css",
      "schema": {
        "order_id": "string",
        "items": "array",
        "total": "string"
      },
      "defaults": { "format": "a5" }
    }
  }
}
```

### Push and preview

```bash theme={null}
# Push all templates to PDFBase
pdfbase templates push

# Preview a template with sample data
pdfbase templates preview invoice --data sample.json

# Diff local vs. remote
pdfbase templates diff
```

## Rendering a template

```bash theme={null}
curl -X POST https://api.pdfbase.dev/v1/templates/tpl_abc123/render \
  -H "Authorization: Bearer pk_live_xxx" \
  -d '{
    "data": {
      "number": "INV-042",
      "customer": "Acme Corp",
      "items": [
        { "name": "Widget", "qty": 10, "price": "$50.00" }
      ],
      "total": "$500.00"
    },
    "output": "url"
  }'
```

## Versioning

Every time you update a template (via API `PATCH` or CLI `push`), the version increments. Each rendered PDF records which version was used:

```json theme={null}
{
  "id": "pdf_rendered1",
  "template": {
    "id": "tpl_abc123",
    "name": "invoice",
    "version": 3
  }
}
```

This means you can update a template without worrying about existing PDFs — they were rendered with the version that was current at the time.
