DocuForge logoDocuForge

blog

Por que construimos DocuForge: la generacion PDF no deberia ser tan dificil

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 PDF generation API 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 finance team needs every invoice preview to match the production PDF, even when the template changes over time.

Use a Typst template contract, keep preview and production paths separate, and make version promotion explicit.

Typst playground

A finance team needs every invoice preview to match the production PDF, even when the template changes over time.

Invoice 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(),
)

Invoice preview

Ready for review

Invoice preview

Ready for review

CustomerNorthwind Studio
ReferenceINV-2026-1042
Date2026-06-16
Rows2
POST /v1/render

Solution & copyable API code

Treat each document family as a versioned template with a stable JSON schema. This prevents layout drift and keeps changes reviewable. Use preview rendering for iteration and production rendering for published versions only. That split avoids accidental draft leakage.

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_why_we_built_docuforge_pdf_generation_shouldnt_be_this_hard", 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.

Puntos clave

  • Typst templates render the same way every time — no surprises from print CSS quirks.
  • Versioned publish flow keeps document behavior auditable.
  • Typed responses and stable error payloads reduce debugging time.
  • Queue-friendly render routes keep throughput predictable under load.

Why teams choose DocuForge for this workflow

  • Rust + Typst engine avoids headless browser overhead in production.
  • Clear API contract for auth, render, usage, and template lifecycle.
  • Security controls include hashed API keys and signed webhook events.

Recursos relacionados

FAQ

How should we start migrating existing PDF workflows?

Move one high-volume document first, lock the data contract, and run both systems in parallel until render parity is verified.

What usually breaks first in production?

Unversioned template edits and weak payload validation. Put both under explicit release and schema checks.

Recursos relacionados

Entrega mas rapido con una API orientada a plantillas

Prueba plantillas en el playground y reutiliza la misma solicitud en produccion.