1. Opening Problem Statement
Meet Sarah, the finance manager at a growing tech startup. Every month, she spends close to 10 hours manually creating and formatting invoices for clients, often juggling multiple versions and correcting errors due to copy-pasting between spreadsheets and design tools. The repetitive nature of the task leads to delayed payments and client complaints because invoices sometimes arrive late or contain mistakes in billing details. Sarah’s team wants a more efficient, error-free way to automate the creation of professional invoice PDFs directly from structured invoice data—saving time and improving accuracy.
This is precisely the problem this n8n workflow solves by taking raw invoice details, formatting them into a beautiful HTML layout, converting it to a PDF, and delivering the final file ready to be sent out, automatically and reliably.
2. What This Automation Does
This workflow automates the generation of invoice PDFs from structured invoice data. When triggered by a webhook with invoice information, the workflow:
- Processes raw invoice data (bill to, issuer info, line items) and formats them into clean HTML markup.
- Generates a professional invoice PDF using the HTML to PDF node from the PDF Toolkit with custom CSS styling.
- Responds to the initial webhook request by sending back the generated PDF file as a binary response.
- Calculates totals automatically from line item quantities and prices.
- Embeds client and company contact details dynamically into the PDF layout.
- Can be triggered externally via a webhook URL from any service or app collecting invoice data.
By automating these steps, Sarah saves hours of monotony and eliminates human errors with consistent, brand-friendly invoices ready instantly.
3. Prerequisites ⚙️
- n8n account for building and running the workflow.
- PDF Toolkit node (HTML to PDF) installed in n8n, configured with valid CustomJS API credentials (required for PDF generation).
- Webhook access, since this workflow is triggered externally.
- Basic knowledge of JavaScript inside n8n for the
Code(Preprocess) node.
4. Step-by-Step Guide
Step 1: Create the Webhook Trigger
Navigate to Triggers → Webhook in n8n.
Set the webhook path to 526fd864-6f85-4cde-97aa-39b61a3e5b83 (or a custom unique ID).
You should see a ready-to-use URL displayed after saving.
This webhook URL is where the invoice data will be posted externally to kick off the workflow.
Common mistake: Forgetting to activate the webhook or changing the path after connecting it externally.
Step 2: Set Initial Invoice Data
Click + Add Node → Set to create a Set data node.
Under Assignments, assign fields exactly as follows (this example populates invoice info manually):
Invoice No: “1”Bill To: A multiline string with the client’s name and address, e.g., “John Doen1234 Elm St, Apt 567nCity, Country, 12345”From: Your company’s info similarly multiline.Details: An array of line item objects, each withdescription,price, andqty. For example:[ { "description": "Web Hosting", "price": 150, "qty": 2 }, { "description": "Domain", "price": 15, "qty": 5 } ]Email: Contacts email used for the invoice footer.
Save this data as the starting point for invoice processing.
Common mistake: Misformatting the JSON array in the Details field will cause the next nodes to fail.
Step 3: Preprocess Invoice Data with Code Node
Add a Code node called Preprocess connected to Set data.
Set the mode to runOnceForEachItem.
Copy the following JavaScript code into the editor:
const input = $input.item.json
const bill_to = input['Bill To'].split('n').map(item => '' + item + '
')
const from = input['From'].split('n').map(item => '' + item + '
')
const details = input['Details'].map(item => {
const price = item.price*item.qty
return `
${item.description}
$${item.price}
${item.qty}
$${price}
`
})
const total = input['Details'].reduce((val, next) => {
return val+next.price*next.qty
}, 0)
return {
bill_to: bill_to.join('n'),
from: from.join('n'),
details: details.join('n'),
total
}This code splits multiline text into HTML paragraphs, transforms line items into table rows, and sums the total invoice amount.
Expected outcome: HTML snippets ready for injection into the PDF template.
Common mistake: Failing to adjust field names if Set data names are changed.
Step 4: Generate PDF from HTML Template
Add the HTML to PDF node from the PDF Toolkit connected to Preprocess.
The htmlInput parameter holds a comprehensive HTML template styled with CSS and dynamic placeholders.
The placeholders use expressions like {{ $json.bill_to }} and {{ $('Set data').item.json['Invoice No'] }} for dynamic content.
This node converts the styled invoice into a final PDF file.
Expected outcome: PDF binary data ready to be returned.
Common mistake: Missing or incorrect CustomJS API credentials break PDF generation.
Step 5: Respond to the Webhook with PDF
Add a Respond to Webhook node connected to HTML to PDF.
Set Respond With option to binary to send the PDF file back as the webhook response.
This completes the workflow by delivering the invoice PDF directly to the requester.
Common mistake: Setting response mode incorrectly leads to no or wrong data sent back.
5. Customizations ✏️
- Change Invoice Styling: Modify CSS inside the
HTML to PDFnode’shtmlInputfield to match your branding colors and fonts. - Add QR Code or Barcode: Integrate a
Codenode before the PDF generation to produce a base64 image of a QR code and embed it in the HTML template. - Additional Fields: Expand the
Set datanode to include payment terms or invoice dates, then update preprocessing and template accordingly. - Send Email with Attachment: After PDF generation, add an
Email Sendnode to automatically mail the invoice to clients (requires configuring SMTP).
6. Troubleshooting 🔧
Problem: "HTML to PDF node fails with authentication error."
Cause: Missing or invalid CustomJS API credentials.
Solution: Go to Credentials → Custom JS API in n8n, add or correct your API key linked to the PDF Toolkit node.
Problem: "Invoice details aren’t properly formatted in PDF output."
Cause: The Preprocess code node references wrong field names or has syntax errors.
Solution: Verify field names in Set data node and ensure code syntax matches exactly.
Problem: "Webhook returns no data or errors on triggering."
Cause: Incorrect webhook URL or not activating the workflow.
Solution: Double-check webhook path and ensure the workflow is active to receive requests.
7. Pre-Production Checklist ✅
- Test webhook trigger by sending sample JSON data matching
Set datastructure. - Verify API credentials for PDF generation are correct and active.
- Preview generated PDF from the
HTML to PDFnode in isolation with sample data. - Backup your workflow JSON configuration before deployment.
8. Deployment Guide
After completing and testing your workflow, activate it by clicking the activate button in the top right corner.
Use the webhook URL with your external system to trigger invoice PDF creation automatically.
Monitor execution via the Executions tab in n8n to catch any errors or unexpected behavior.
9. FAQs
Q: Can I use other PDF generation nodes instead of the PDF Toolkit’s HTML to PDF?
A: This workflow specifically uses PDF Toolkit's HTML to PDF node for styled output, but alternatives could be integrated with custom adjustments.
Q: Does this workflow consume API credits?
A: Yes, PDF Toolkit nodes require API usage which might consume credits based on volume.
Q: How secure is my invoice data?
A: Data processed only inside your n8n instance and through your configured credentials. Use HTTPS for webhook security.
10. Conclusion
You’ve now built a robust, scalable invoice PDF generator in n8n that takes structured invoice data, formats it beautifully, and outputs ready-to-send PDFs automatically. This automation can save Sarah—and you—hours per month, reduce costly billing errors, and improve professionalism in your communications.
Next, consider extending this workflow to automatically email invoices, integrate with payment gateways, or archive your invoices in cloud storage for record keeping.
Start automating your invoicing process today, and enjoy the freed-up time and peace of mind!