Templates & variables
Reusable templates with variables, conditions and tables.
Store the HTML once as a template, then generate PDFs by sending only the
data. Templates use Handlebars syntax: {{variable}}, {{#if}},
{{#each}}.
Create a template
curl -X POST https://api.pdf-forge.dev/v1/templates \
-H "Authorization: Bearer $PDFFORGE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Invoice",
"slug": "invoice",
"html": "<h1>Invoice {{number}}</h1><table>{{#each items}}<tr><td>{{label}}</td><td>{{fmtMoney price \"XOF\"}}</td></tr>{{/each}}</table>",
"schema": { "required": ["number", "items"] }
}'Returns 201 with the template (id, slug, version, ...).
const template = await client.templates.create({
name: "Invoice",
slug: "invoice",
html: `<h1>Invoice {{number}}</h1>
<table>{{#each items}}
<tr><td>{{label}}</td><td>{{fmtMoney price "XOF"}}</td></tr>
{{/each}}</table>`,
schema: { required: ["number", "items"] },
});name and html are required. Optional: slug (derived from the name if
omitted), description, css, schema (validation of the data you send at
generation time), testData, pdfOptions.
Manage templates with GET /v1/templates, GET /v1/templates/:id,
PUT /v1/templates/:id (versioned — the previous version is kept) and
DELETE /v1/templates/:id. The SDK mirrors these as client.templates.list(),
.get(id), .update(id, params), .delete(id).
Generate from a template
POST /v1/generate/template accepts the template id or slug plus your
data, and returns the PDF binary:
curl -X POST https://api.pdf-forge.dev/v1/generate/template \
-H "Authorization: Bearer $PDFFORGE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"template": "invoice",
"data": {
"number": "2026-001",
"items": [{ "label": "Consulting", "price": 250000 }]
},
"options": { "format": "A4" }
}' \
--output invoice.pdfconst pdf = await client.template("invoice", {
number: "2026-001",
items: [{ label: "Consulting", price: 250000 }],
});If data violates the template schema, the API returns
400 schema_validation_failed with the failing fields.
Helpers
Beyond standard Handlebars ({{#if}}, {{#each}}, {{else}}), pdfforge
registers formatting helpers:
| Helper | Example | Output |
|---|---|---|
fmtMoney | {{fmtMoney total "XOF"}} | 250 000 FCFA (FR formatting) |
fmtNumber | {{fmtNumber qty}} | thousands-separated number |
fmtDate | {{fmtDate issued_at}} | ISO date → dd/MM/yyyy |
colTotal | {{colTotal items "price" "money" "XOF"}} | sum of a column, formatted |
uppercase | {{uppercase client.name}} | ACME SARL |
eq | {{#if (eq status "paid")}}...{{/if}} | equality test |
Preview without consuming quota
POST /v1/templates/:id/preview renders the template with data and returns
a watermarked, disposable PDF that does not count against your quota or
appear in your logs — ideal for a live editor preview.
Print-perfect engine (Typst)
Templates built as block documents in the visual editor can be rendered with
"options": { "engine": "typst" } — a deterministic print engine that also
supports ISO output standards via pdfStandard: "a-2b" / "a-3b" (PDF/A
archival) and "ua-1" (PDF/UA accessible). Chrome cannot produce those.
Next steps
- Dynamic tables — variable rows in imported-PDF templates.
- Pagination & fit — how long invoices break across pages.
- Import a PDF — turn a designed PDF into a template instead.