Skip to main content

HTML template

What this guide covers

This guide walks you through rendering a single HTML invoice in n8n using invoice_sample_1.html and invoice_sample_data_1.json. The template uses mustache syntax ({{recipient.name}}, {{#each subInfo}}). HTML is the only template type that supports pasting raw markup in HTML Code. Output is HTML only (ideal for email bodies or web previews).

Before You Start

HTML template with mustache tags
The sample invoice_sample_1.html is a full invoice layout with nested paths ({{recipient.postalAddress.street1}}) and a line-item loop ({{#each subInfo}}). Paths must match your JSON structure exactly.
Matching data file
Use invoice_sample_data_1.json (recommended), test.xml, or test.csv. JSON uses nested objects and a subInfo array. CSV flattens fields and uses one row per line item.
PDF4me credential in n8n
Create a PDF4me credential in n8n and paste your API key. The same key works across all PDF4me nodes in your instance.

Sample File Pack

Download all four files below. One HTML template plus three data formats with the same invoice content.

What's Inside the Sample Files

Template layout (invoice_sample_1.html)

The sample is a styled HTML invoice (tables, logo, PDF4me seller block). Dynamic sections:

Document sectionStatic content (fixed in template)Mustache placeholder (filled from data)
Recipient address(layout only){{recipient.name}}, {{coText}}, {{recipient.postalAddress.*}}
Seller blockPDF4me, Zurich, dev.pdf4me.com(static)
Buyer / deliveryBuyer label{{buyer.name}}, {{coTextDelivery}}, {{buyer.postalAddress.*}}
Invoice metaDue Date, Invoice Number, Issued On labels{{dueDate}}, {{invoiceNumber}}, {{issuedOn}}
Line items tableProduct, Period, Quantity, Price, VAT, Total headers{{#each subInfo}}{{plan}}, {{quantity}}, {{vat}}, {{totalPrice}}
Period column(single range per invoice){{periodStart}} - {{periodEnd}}
Total rowTotal balance label{{totalAmount}}
Mustache syntax (HTML only)

HTML templates use {{fieldName}} and {{object.nestedKey}}. Repeat line items with {{#each subInfo}}{{/each}}. This is different from Word templates, which use <<[fieldName]>>. See the HTML template setup guide for more patterns.

HTML Code vs file upload

HTML Code is only valid when Template File Type is HTML. For Word, Pdf Form, Mail Merge, or Google Docs, use Binary Data, Base64, or URL instead.

Field mapping across all data files

HTML placeholderJSON path (invoice_sample_data_1.json)XML element (test.xml)CSV column (test.csv)Sample value
{{recipient.name}}recipient.nameRecipient/Namerecipient_nameTest AG
{{coText}}coTextCoTextcoTextcotext
{{recipient.postalAddress.street1}}recipient.postalAddress.street1Recipient/PostalAddress/Street1recipient_street1Test Street1
{{recipient.postalAddress.street2}}recipient.postalAddress.street2Recipient/PostalAddress/Street2recipient_street2Test Street2
{{recipient.postalAddress.zipCode}} {{city}}recipient.postalAddress.zipCode, .cityRecipient/PostalAddress/ZipCode, Cityrecipient_zipCode, recipient_city8000 Zurich
{{recipient.postalAddress.country}}recipient.postalAddress.countryRecipient/PostalAddress/Countryrecipient_countrySwitzerland
{{buyer.name}}buyer.nameBuyer/Namebuyer_namebuyer name
{{coTextDelivery}}coTextDeliveryCoTextDeliverycoTextDeliveryDelivery text
{{buyer.postalAddress.street1}}buyer.postalAddress.street1Buyer/PostalAddress/Street1buyer_street1Test Street1
{{buyer.postalAddress.street2}}buyer.postalAddress.street2Buyer/PostalAddress/Street2buyer_street2Test Street2
{{dueDate}}dueDateDueDatedueDate2025-04-30
{{invoiceNumber}}invoiceNumberInvoiceNumberinvoiceNumber123456
{{issuedOn}}issuedOnIssuedOnissuedOn2025-04-01
{{periodStart}} - {{periodEnd}}periodStart, periodEndPeriodStart, PeriodEndperiodStart, periodEnd2025-03-01 to 2025-03-31
{{#each subInfo}} {{plan}}subInfo[].planSubInfo/Item/PlanplanPDF4me base 1000
{{quantity}}subInfo[].quantitySubInfo/Item/Quantityquantity1
{{vat}}subInfo[].vatSubInfo/Item/Vatvat2.83
{{totalPrice}}subInfo[].totalPriceSubInfo/Item/TotalPricetotalPrice37.83
{{totalAmount}}totalAmountTotalAmounttotalAmount124.31

The sample JSON includes two objects in subInfo (PDF4me base 1000 and Call Package 2000). The {{#each subInfo}} blocks in the HTML render one table cell row per item.

The Workflow at a Glance

Fastest test: paste HTML and JSON directly in the PDF4me node (no file nodes required).

Manual Trigger → PDF4me Generate Document (Single) → (optional) Send Email / Write Binary File
Stepn8n nodePurpose
1Manual TriggerStart the workflow on demand
2PDF4me → Generate Document (Single)Paste HTML Code + JSON Text, output rendered HTML
3Send Email or Write Binary File (optional)Deliver or save generated_document_output.html
Generate Document (Single) n8n node configured with Template File Type HTML, Output Type HTML, Template File Input Type HTML Code, Document Data Input Type Text, Document Data Type JSON

Target configuration: pasted HTML Code + JSON in Document Data Text + HTML output


Step 1: Download the Sample Files

  1. Download invoice_sample_1.html and invoice_sample_data_1.json from the Sample File Pack above.
  2. Open both files in a text editor so you can copy the full contents into the n8n node.
  3. Optionally download test.xml or test.csv to test XML or CSV data types later.

Step 2: Create the n8n Workflow

  1. In n8n, create a new workflow.
  2. Add a Manual Trigger node.
  3. Add PDF4me → Generate Document (Single) after the trigger.
  4. Connect your PDF4me credential.

For production you can add a Read Binary File node instead of HTML Code (see Alternative template input below).


Step 3: Configure the HTML Template (HTML Code)

Open the PDF4me node and set the template fields:

FieldValue
Template File TypeHTML
Output TypeHTML (only option for HTML templates)
Template File Input TypeHTML Code
Template File Nameinvoice_sample_1.html
HTML CodePaste the entire contents of invoice_sample_1.html
Partial HTML is OK

If your snippet omits <html> or <head>, the node wraps it in a basic HTML shell automatically. The sample file is a complete document, so paste it as-is.


Step 4: Configure Document Data (JSON)

FieldValue
Document Data Input TypeText
Document Data TypeJSON
Document Data TextPaste the full contents of invoice_sample_data_1.json (see block below)
{
"recipient": {
"name": "Test AG",
"postalAddress": {
"street1": "Test Street1",
"street2": "Test Street2",
"zipCode": "8000",
"city": "Zurich",
"country": "Switzerland"
}
},
"coText": "cotext",
"buyer": {
"name": "buyer name",
"postalAddress": {
"street1": "Test Street1",
"street2": "Test Street2"
}
},
"coTextDelivery": "Delivery text",
"dueDate": "2025-04-30",
"invoiceNumber": "123456",
"issuedOn": "2025-04-01",
"subInfo": [
{
"plan": "PDF4me base 1000",
"quantity": 1,
"price": 35,
"vat": 2.83,
"totalPrice": 37.83
},
{
"plan": "Call Package 2000",
"quantity": 1,
"price": 80,
"vat": 6.48,
"totalPrice": 86.48
}
],
"totalAmount": 124.31,
"periodStart": "2025-03-01",
"periodEnd": "2025-03-31"
}

Core node settings (summary)

FieldValueNotes
Credential to connect withYour PDF4me credentialGet API key
ResourceGenerate
Generate OperationsGenerate Document (Single)
Binary Data Output Namedata (default)Output is HTML in this binary property

Step 5: Execute and Verify the HTML

  1. Click Execute workflow.
  2. Open the PDF4me node output. Under Binary, open property data (generated_document_output.html).
  3. Confirm the rendered invoice shows:
    • Recipient → Test AG, cotext, Test Street1/2, 8000 Zurich, Switzerland
    • Buyer → buyer name, Delivery text, Test Street1/2
    • Invoice meta → Due Date 2025-04-30, Invoice Number 123456, Issued On 2025-04-01
    • Line items → PDF4me base 1000 and Call Package 2000 with quantities and VAT
    • Total balance → 124.31

If a placeholder renders blank, check the mustache path against your JSON. recipient.postalAddress.street1 in the template requires the same nested structure in data.


Step 6: Send or Save the Output (Optional)

Send Email: map the PDF4me binary data as the HTML body (enable HTML email).

Write Binary File:

FieldValue
File Nameinvoice-output.html
Data Property Namedata
Output PathFolder where n8n should write the file

Alternative Template Input: Load HTML from File

If you prefer not to paste HTML into the node:

  1. Add Read Binary File before PDF4me and point it to invoice_sample_1.html.
  2. In PDF4me set Template File Input Type to Binary Data, Template Binary Property to data, Template File Name to invoice_sample_1.html.
  3. Keep Document Data Input Type as Text and paste the JSON as in Step 4.

You can also host the .html at a public URL and use Template File Input Type: URL with Template File URL.


Alternative Data Formats

Option A: JSON file as binary (invoice_sample_data_1.json)

  1. Add Read Binary File for the JSON file (or use a Merge node if the template is also binary).
  2. Set Document Data Input Type to Binary Data, Document Data Type to JSON, Document Binary Property to your data field, Document Data File Name to invoice_sample_data_1.json.

Option B: XML (test.xml)

Set Document Data Type to XML and paste the contents of test.xml:

<?xml version="1.0" encoding="UTF-8"?>
<Invoice>
<Recipient>
<Name>Test AG</Name>
<PostalAddress>
<Street1>Test Street1</Street1>
<Street2>Test Street2</Street2>
<ZipCode>8000</ZipCode>
<City>Zurich</City>
<Country>Switzerland</Country>
</PostalAddress>
</Recipient>
<CoText>cotext</CoText>
<Buyer>
<Name>buyer name</Name>
<PostalAddress>
<Street1>Test Street1</Street1>
<Street2>Test Street2</Street2>
</PostalAddress>
</Buyer>
<CoTextDelivery>Delivery text</CoTextDelivery>
<DueDate>2025-04-30</DueDate>
<InvoiceNumber>123456</InvoiceNumber>
<IssuedOn>2025-04-01</IssuedOn>
<SubInfo>
<Item>
<Plan>PDF4me base 1000</Plan>
<Quantity>1</Quantity>
<Price>35</Price>
<Vat>2.83</Vat>
<TotalPrice>37.83</TotalPrice>
</Item>
<Item>
<Plan>Call Package 2000</Plan>
<Quantity>1</Quantity>
<Price>80</Price>
<Vat>6.48</Vat>
<TotalPrice>86.48</TotalPrice>
</Item>
</SubInfo>
<TotalAmount>124.31</TotalAmount>
<PeriodStart>2025-03-01</PeriodStart>
<PeriodEnd>2025-03-31</PeriodEnd>
</Invoice>

Option C: CSV (test.csv)

Set Document Data Type to CSV and paste the CSV body. The sample has two data rows (one per subInfo line item); header columns use flattened names like recipient_name and plan:

recipient_name,recipient_street1,recipient_street2,recipient_zipCode,recipient_city,recipient_country,coText,buyer_name,buyer_street1,buyer_street2,coTextDelivery,dueDate,invoiceNumber,issuedOn,periodStart,periodEnd,totalAmount,plan,quantity,price,vat,totalPrice
"Test AG","Test Street1","Test Street2","8000","Zurich","Switzerland","cotext","buyer name","Test Street1","Test Street2","Delivery text","2025-04-30","123456","2025-04-01","2025-03-01","2025-03-31",124.31,"PDF4me base 1000",1,35,2.83,37.83
"Test AG","Test Street1","Test Street2","8000","Zurich","Switzerland","cotext","buyer name","Test Street1","Test Street2","Delivery text","2025-04-30","123456","2025-04-01","2025-03-01","2025-03-31",124.31,"Call Package 2000",1,80,6.48,86.48

Production Workflow Patterns

Common ways to supply HTML template and data in productionOnce the sample test works, replace the Manual Trigger with these patterns.
Email invoice from webhook
  1. Webhook receives billing payload.
  2. PDF4me runs with fixed HTML Code template.
  3. Map webhook JSON to Document Data Text.
  4. Send Email uses output HTML as the message body.
Template from cloud storage
  1. Google Drive / S3 stores invoice_sample_1.html.
  2. Download node loads HTML as binary.
  3. PDF4me: Template File Input Type = Binary Data.
  4. Data built from Sheets or CRM as JSON.
Public URLs for template and data
  1. Host .html and .json at public HTTPS URLs.
  2. Set Template File Input Type = URL and Document Data Input Type = URL.
  3. No Read Binary File nodes required.

Frequently Asked Questions

Why use HTML Code instead of uploading a file?+
HTML Code lets you test instantly without file nodes. It is only available when Template File Type is HTML. For large templates or CI-managed markup, use Binary Data or URL instead.
Can HTML templates output PDF?+
No. When Template File Type is HTML, Output Type is HTML only. To get a PDF from HTML content, chain a separate PDF4me Convert node after this step.
How do line items work in the sample?+
The subInfo array in JSON drives {{#each subInfo}} blocks in the HTML table. Each object renders plan, quantity, vat, and totalPrice in aligned columns. Add or remove array items to change row count.
Why does test.csv have two rows?+
CSV flattens nested data. Each row represents one line item (plan, quantity, price, vat, totalPrice) while repeating shared invoice fields (recipient, buyer, dates). Both rows share the same totalAmount.
JSON vs XML vs CSV: which should I use?+
JSON is best for n8n because it matches nested mustache paths (recipient.postalAddress.street1) and the subInfo array. XML uses PascalCase elements in test.xml. CSV suits spreadsheet exports but uses flattened column names.