The single biggest reason PDF renders fail is not the API call. It is data mismatch between template variables and JSON payload.
If your template expects {{customer.name}} and your payload sends client.name, the output is incomplete even though the request itself succeeds.
This guide explains how to place Handlebars variables in a way that stays readable, testable, and stable as your templates grow.
1) Define one data contract before editing HTML
Before touching template markup, decide your payload shape.
Example contract:
{
"invoice_number": "INV-2026-0042",
"customer": {
"name": "Northwind Labs",
"address": "42 Market Street"
},
"items": [
{
"description": "Pro Plan",
"quantity": 1,
"line_total": "$99.00"
}
],
"total": "$108.90"
}
Only after this shape is agreed, place template variables.
2) Use explicit variable paths
Good:
<p>{{customer.name}}</p>
<p>{{customer.address}}</p>
Risky:
<p>{{name}}</p>
<p>{{address}}</p>
Why explicit paths help:
- Fewer naming collisions
- Easier code review
- Faster debugging when payload changes
3) Reserve #each for true arrays only
Use #each only when the JSON field is always an array.
<tbody>
{{#each items}}
<tr>
<td>{{description}}</td>
<td>{{quantity}}</td>
<td>{{line_total}}</td>
</tr>
{{/each}}
</tbody>
If items can be empty, decide UI behavior in advance (empty row, note message, or hidden section).
4) Keep formatting strategy consistent
Do not mix raw values and display-ready values randomly.
Pick one strategy per field:
- Send display-ready strings in payload (
"$108.90"). - Or send raw numbers and transform before render request.
Most teams keep template logic minimal and send display-ready values from backend.
5) Practical variable map for an invoice
| Template field | Expected payload path |
|---|---|
{{invoice_number}} |
invoice_number |
{{customer.name}} |
customer.name |
{{customer.address}} |
customer.address |
{{#each items}} |
items[] |
{{total}} |
total |
When a render looks wrong, this table is usually enough to find mismatch in minutes.
6) Add a variable QA pass before calling API
Use this quick pass for every template update:
- List all
{{...}}variables in template. - Compare each path to a sample JSON payload.
- Test with realistic long values (long company name, long address).
- Test with 1 item and many items in
itemsarray. - Run a preview render before production use.
Use this template for free now
Start with this template in HookPDF and render your first PDF for free.
Use this template freeRecommended next reads
- Template design checklist with HTML + CSS examples
- Invoice walkthrough with JSON and API request
- HookPDF homepage
Ready to move from prototype to production?
Create your account and generate your first API-driven PDF with HookPDF.
Get your API key