Guide · 2026-10-10

PDF invoices from HTML

One HTML template, a dictionary of values, a PDF per customer. Rendered in a real browser, so your CSS prints the way it previews.

1. Save the template

The html engine takes a whole document or a fragment, with {{placeholders}} wherever a value goes (escaped) and {{{raw}}} for markup you build yourself, such as the line-item rows.

curl -X POST https://api.imageapis.com/v1/templates \
  -H 'X-API-Key: YOUR_KEY' -H 'Content-Type: application/json' \
  -d @- <<'JSON'
{
  "name": "Invoice", "engine": "html", "width": 794, "height": 1123,
  "html": "<h1>Invoice {{number}}</h1><div class=muted>Issued {{issued}} · due {{due}}</div><table><tr><th>Item</th><th>Qty</th><th>Amount</th></tr>{{{rows}}}</table><p class=total>Total {{total}}</p>",
  "css": "body{font-family:Inter,sans-serif;color:#0f172a} h1{font-size:28px;margin:0 0 4px} .muted{color:#64748b} table{width:100%;border-collapse:collapse;margin-top:24px} th,td{text-align:left;padding:8px 0;border-bottom:1px solid #e2e8f0} .total{font-weight:700;font-size:20px;margin-top:20px}"
}
JSON

2. Render in the background

curl -X POST https://api.imageapis.com/v1/pdfs \
  -H 'X-API-Key: YOUR_KEY' -H 'Content-Type: application/json' \
  -d '{
    "template": "tpl_…",
    "variables": {
      "number": "#1042", "issued": "10 Oct 2026", "due": "9 Nov 2026", "total": "$3,320.00",
      "rows": "<tr><td>Design sprint</td><td>1</td><td>$3,200.00</td></tr><tr><td>Hosting</td><td>1</td><td>$120.00</td></tr>"
    },
    "format": "A4", "margin": "20mm",
    "webhook_url": "https://example.com/hooks/invoices",
    "metadata": { "customer_id": 8812 }
  }'

The answer is 202 with status: "pending" and the final url. When the PDF is done, the webhook receives { "event": "render.completed", "pdf": { … "pages": 1, "units": 1, "metadata": { "customer_id": 8812 } } }. The record's pages is what you were charged.

Page breaks and paper

format is A4, A3, A5, Letter, Legal or Tabloid, with landscape and a single margin for all sides. Use page-break-before: always in your CSS to start a new page, and @page { size: … } rules apply when you leave format out. A layer template without format prints at its own pixel size on one page, which is how certificates are done.

Keeping copies

PDFs are hosted 30 days on the free plan and a year on paid plans. Download them into your own storage when they are delivered, or send "response": "binary" to get the file in the same request.

Get a free key: 150 images a month API reference