Import a PDF
Turn an existing PDF into a variable template.
A PDF template is any existing PDF — an exported invoice, a scanned form,
a Canva certificate — with some of its text marked as {{variables}}. Fill
it with data through one API call and
the design stays pixel-identical; only the marked spans change. No HTML to
rewrite, no font to re-embed.
Two template systems
This is not the HTML/blocks template system. That one renders a design you authored in HTML. A PDF template starts from a PDF that already exists and reuses its exact layout.
The visual way (recommended)
The fastest path is the dashboard editor: upload the PDF, click a text span, turn it into a variable, and save. The editor detects the span geometry, colour and font for you, so a later fill masks the original text and redraws your value in place.
Once saved, the template gets an id and a slug and appears in your
templates list.
The programmatic way
You can also create a template with the API. A variable maps a name to a
span located by span_id — the stable element id returned by the
perception layer. So the flow is: perceive the PDF, pick the spans, create.
Perceive the PDF
POST /v1/pdf/perceive returns every text element with a stable id, its
text, bbox and style. Pick the ones you want to turn into variables.
const doc = await client.pdf.perceive(pdfBytes, { detail: "full" });Create the template
POST /v1/pdf-templates stores the base PDF (base64) plus the variable map.
Each variable carries the span_id and, ideally, the geometry/style from
perceive so the fill can mask-and-redraw on any font.
PDF=$(base64 -w0 invoice.pdf) # macOS: base64 -i invoice.pdf
curl -X POST https://api.pdf-forge.dev/v1/pdf-templates \
-H "Authorization: Bearer $PDFFORGE_API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"name\": \"Invoice\",
\"pdf\": \"$PDF\",
\"variables\": [
{ \"name\": \"client_name\", \"span_id\": \"p1:span:42\", \"default\": \"ACME Corp\" },
{ \"name\": \"total\", \"span_id\": \"p1:span:88\", \"default\": \"250,000 FCFA\" }
]
}"const { id, slug } = await client.pdfTemplates.create({
name: "Invoice",
pdf: pdfBytes,
variables: [
{ name: "client_name", span_id: "p1:span:42", default: "ACME Corp" },
{ name: "total", span_id: "p1:span:88", default: "250,000 FCFA" },
],
});The response is { "id": "...", "slug": "..." } (HTTP 201). Store the id —
it is what you render against.
Let an agent do it
The MCP server ships a create_pdf_template tool
that runs this whole loop — perceive, pick spans, resolve geometry, create —
from a natural-language prompt. It fills in span_id, bg, color,
size, font and member_ids from the perception automatically.
List your templates
GET /v1/pdf-templates returns your templates (metadata only — no PDF bytes):
const templates = await client.pdfTemplates.list();
// [{ id, name, slug, variables, page_count, size_bytes, updated_at }]Next steps
- Render via API — fill the template and get the PDF.
- Dynamic tables — variable-length rows from an array.
- MCP server — create and fill templates from an agent.