blog

How to Let Users Design Their Own PDF Templates (Without Code)

When your PDF pipeline cracks at 2 AM and customers are waiting on invoices, debugging headless Chromium is the last thing you want to do. This guide cuts to what actually matters for dynamic PDF templates in production: templates that render the same way every time, render jobs that survive queue spikes, and an API surface with clear failure modes so on-call is boring.

Updated 2026-02-24

Problem

A product team wants customer-specific PDF output without forking layouts for every customer account.

Model each use case with a dedicated template contract, then standardize payload mapping from upstream systems.

Typst playground

A product team wants customer-specific PDF output without forking layouts for every customer account.

Customer handoff Typst template

Sample JSON payload

#set page(paper: "a4", margin: 18mm)
#set text(font: "Inter", size: 10pt)

#let data = sys.inputs

#text(size: 22pt, weight: "bold")[#data.title]
#v(4pt)
#text(fill: rgb("#64748b"))[#data.reference + " - " + data.date]

#v(12pt)
#block(
  fill: rgb("#f8fafc"),
  stroke: 0.5pt + rgb("#e2e8f0"),
  inset: 12pt,
  radius: 4pt,
)[
  #text(weight: "semibold")[#data.customer]
  #linebreak()
  #text(fill: rgb("#64748b"))[#data.status]
]

#v(12pt)
#table(
  columns: (1fr, auto, auto),
  inset: 7pt,
  stroke: 0.5pt + rgb("#e2e8f0"),
  [*Item*], [*Qty*], [*Amount*],
  ..data.line_items.map(item => (
    item.name,
    str(item.qty),
    "$" + str(item.amount),
  )).flatten(),
)

Customer document

Tenant branded

Customer document

Tenant branded

CustomerAtlas Logistics
ReferenceDOC-2026-219
Date2026-06-16
Rows2
POST /v1/render

Solution & copyable API code

Keep branding and tenant-specific values in data, not forked layouts. This preserves consistency while supporting customization. Design a minimal document schema and transform source events into it at ingestion boundaries. Stable schemas simplify multi-team ownership.

const response = await fetch(`${process.env.DOCUFORGE_API_URL}/v1/render`, {
  method: "POST",
  headers: {
    "X-API-Key": process.env.DOCUFORGE_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ template_id: "tpl_how_to_let_users_design_their_own_pdf_templates_without_code", data: payload }),
});

const pdf = Buffer.from(await response.arrayBuffer());

Customization tips

  • Start with the smallest layout that proves the document contract.
  • Use real customer-shaped sample data before publishing a new version.
  • Keep brand colors, spacing, and repeated footer copy in shared template variables.
  • Pass customer-specific values through JSON data instead of forking the template.
  • Store one approved sample payload per template so preview regressions are easy to catch.

Points cles

  • One template family per document intent scales better than per-customer forks.
  • Normalization layers prevent upstream system changes from breaking layout logic.
  • Webhooks and retries should be signed and auditable.
  • Security and compliance requirements should be encoded in routing and key scope.

Why teams choose DocuForge for this workflow

  • API-key and JWT paths are separated by endpoint intent.
  • Webhook signatures and HTTPS validation are enforced in production paths.
  • Version promotion supports controlled rollout across customer segments.

Ressources associees

FAQ

How do we handle per-customer branding without template sprawl?

Store brand variables in payload data and keep the underlying template shared per document class.

What is the safest rollout strategy?

Publish new versions behind a subset of traffic, compare outputs, then promote globally once parity checks pass.

Ressources associees

Livrez plus vite avec une API orientee templates

Testez vos templates dans le playground, puis reutilisez la meme requete en production.