DocuForge logoDocuForge

blog

How to Generate Invoices Programmatically with an API

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 invoice 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 backend worker needs to render documents idempotently from queue events and avoid duplicate PDFs during retries.

Treat rendering as a backend job pipeline: idempotent requests, queue control, and deterministic templates.

Typst playground

A backend worker needs to render documents idempotently from queue events and avoid duplicate PDFs during retries.

Queue receipt 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(),
)

Queue render receipt

Idempotent

Queue render receipt

Idempotent

CustomerFulfillment Worker
ReferenceJOB-2026-8831
Date2026-06-16
Rows2
POST /v1/render

Solution & copyable API code

Feed render jobs through queues partitioned by document type or priority. This prevents a noisy workload from starving critical documents. Attach stable idempotency keys per business document event so retries do not create duplicate outputs.

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_generate_invoices_programmatically_with_an_api", data: payload }),
});

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

Customization tips

  • Attach an idempotency key to each business event before rendering.
  • Keep retry metadata outside the PDF payload so document output stays deterministic.
  • 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.

Punti chiave

  • Queue partitioning and idempotency are mandatory at scale.
  • Template version IDs should be explicit in production render events.
  • Monitor usage and quotas by plan and document family.
  • Keep failure handling deterministic with typed error branches.

Why teams choose DocuForge for this workflow

  • Render API is queue-friendly with predictable request/response envelopes.
  • Usage and billing endpoints support operational visibility.
  • No headless Chrome dependency in the main render engine path.

Risorse correlate

FAQ

How do we avoid duplicate documents on retries?

Use business-event idempotency keys and persist render outcomes before acknowledging queue completion.

What should we put in dead-letter queues?

Jobs with non-retryable schema/template failures and jobs that exceeded bounded retry policy.

Risorse correlate

Rilascia piu velocemente con un'API template-first

Prova i template nel playground e usa la stessa richiesta in produzione.