Guide
Generate a branded invoice PDF from a Stripe payment in n8n
A customer pays in Stripe, and you want a finished invoice PDF — with your logo and layout, not Stripe’s. This is the honest, working version: a real n8n workflow that fires on a paid Stripe invoice, maps the data, and renders a branded PDF with pdfmill. First, the part most guides skip.
Doesn’t Stripe already make invoice PDFs?
It does — and you should know that before building anything. Stripe hosts an invoice page and a downloadable Stripe-branded PDF for every invoice, and can email it for you. If that’s all you need, you don’t need this workflow at all.
You build your own when Stripe’s fixed template isn’t enough:
- Your branding and layout — your logo, colours, footer, and structure, not Stripe’s.
- Fields Stripe’s invoice doesn’t show — a PO number, custom terms, per-line metadata, a second language.
- A different document — a receipt, a quote, or a delivery note off the same payment, which Stripe won’t generate.
If none of those apply, use Stripe’s built-in PDF and move on — that’s the honest answer. If they do, here’s the workflow.
What you’ll need
- A Stripe account and an n8n instance that can install community nodes.
- The Generate PDF community node — Settings → Community Nodes → Install:
n8n-nodes-generate-pdf- A pdfmill API credential — create a key in the pdfmill dashboard (
pk_live_…), add a credential of type pdfmill API, and leave the Base URL ashttps://api.pdfmill.dev. The full setup is the five-minute quickstart.
The workflow, end to end
Four nodes: Stripe Trigger → Code (map the invoice) → Generate PDF (Invoice) → Gmail. Copy it and paste it onto the canvas (or Workflows → Import from File).
{
"name": "Stripe payment → branded invoice PDF with pdfmill",
"nodes": [
{
"parameters": {
"events": [
"invoice.paid"
]
},
"id": "11111111-1111-4111-8111-111111111111",
"name": "When a Stripe invoice is paid",
"type": "n8n-nodes-base.stripeTrigger",
"typeVersion": 1,
"position": [
-40,
320
],
"webhookId": "7a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d"
},
{
"parameters": {
"jsCode": "// Map a Stripe `invoice.paid` event into pdfmill Invoice data.\n// Stripe amounts are in the smallest currency unit (cents) — divide by 100.\nconst inv = $json.data?.object ?? $json; // the Stripe Invoice object\nconst cents = (n) => Math.round(n ?? 0) / 100;\nconst iso = (unix) => (unix ? new Date(unix * 1000).toISOString().slice(0, 10) : '');\n\nconst items = (inv.lines?.data ?? []).map((l) => {\n const quantity = l.quantity ?? 1;\n const unitPrice = cents(l.price?.unit_amount ?? (l.amount / (l.quantity || 1)));\n return { description: l.description ?? 'Item', quantity, unitPrice, lineTotal: cents(l.amount) };\n});\n\nconst a = inv.customer_address ?? {};\nconst address = [a.line1, a.line2, a.city, a.state, a.postal_code, a.country].filter(Boolean).join(', ');\n\nreturn [{\n json: {\n invoiceNumber: inv.number ?? inv.id,\n issuedAt: iso(inv.created),\n dueAt: iso(inv.due_date) || iso(inv.created),\n paymentTerms: inv.due_date ? 'Net 30' : 'Paid',\n currency: (inv.currency ?? 'usd').toUpperCase(),\n from: { name: 'Your Company LLC', address: '123 Main Street, Your City, ST 00000', email: 'billing@your-company.com' },\n billTo: { name: inv.customer_name ?? 'Customer', email: inv.customer_email ?? '', address },\n items,\n subtotal: cents(inv.subtotal),\n taxRate: 0,\n // Stripe moved aggregate tax to total_taxes[]; sum it, fall back to the legacy `tax` field.\n taxAmount: cents((inv.total_taxes ?? []).reduce((s, t) => s + (t.amount ?? 0), 0) || inv.tax || 0),\n total: cents(inv.total ?? inv.amount_paid),\n notes: 'Thank you for your business.',\n },\n}];"
},
"id": "22222222-2222-4222-8222-222222222222",
"name": "Map the Stripe invoice",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
200,
320
]
},
{
"parameters": {
"operation": "template",
"template": "invoice",
"data": "={{ $json }}",
"format": "pdf",
"binaryPropertyName": "data",
"options": {}
},
"id": "33333333-3333-4333-8333-333333333333",
"name": "Generate the invoice PDF",
"type": "n8n-nodes-generate-pdf.generatePdf",
"typeVersion": 1,
"position": [
440,
320
]
},
{
"parameters": {
"sendTo": "={{ $('Map the Stripe invoice').item.json.billTo.email }}",
"subject": "=Invoice {{ $('Map the Stripe invoice').item.json.invoiceNumber }}",
"message": "=Hi {{ $('Map the Stripe invoice').item.json.billTo.name }},\n\nThanks for your payment. Your invoice {{ $('Map the Stripe invoice').item.json.invoiceNumber }} is attached as a PDF.\n\nThank you,\n{{ $('Map the Stripe invoice').item.json.from.name }}",
"options": {
"attachmentsUi": {
"attachmentsBinary": [
{
"property": "data"
}
]
}
}
},
"id": "44444444-4444-4444-8444-444444444444",
"name": "Email the invoice to the customer",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2.1,
"position": [
680,
320
]
},
{
"parameters": {
"content": "## 🧾 Stripe payment → your branded invoice PDF\nStripe already emails a Stripe-branded PDF. Use this when you want **your own** layout and branding: on every paid invoice, map the Stripe data → render a branded invoice PDF with pdfmill → email it.\n\n**Setup**\n1. Add a **Stripe** credential (Stripe Trigger). This node subscribes to the `invoice.paid` event.\n2. Add a **pdfmill** credential to the Generate node (API key at pdfmill.dev → API Keys) and pick the **Invoice** template.\n3. Add a **Gmail** credential.\nThen activate the workflow — the next paid Stripe invoice produces a branded PDF.",
"height": 300,
"width": 520
},
"id": "99999999-9999-4999-8999-999999999999",
"name": "Overview",
"type": "n8n-nodes-base.stickyNote",
"typeVersion": 1,
"position": [
-80,
-60
]
}
],
"connections": {
"When a Stripe invoice is paid": {
"main": [
[
{
"node": "Map the Stripe invoice",
"type": "main",
"index": 0
}
]
]
},
"Map the Stripe invoice": {
"main": [
[
{
"node": "Generate the invoice PDF",
"type": "main",
"index": 0
}
]
]
},
"Generate the invoice PDF": {
"main": [
[
{
"node": "Email the invoice to the customer",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1"
},
"pinData": {},
"meta": {
"templateCredsSetupCompleted": false
},
"tags": [
{
"name": "pdfmill"
}
]
}- When a Stripe invoice is paid — a Stripe Trigger subscribed to
invoice.paid. n8n registers the webhook with Stripe for you; every paid invoice runs the workflow. - Map the Stripe invoice — a Code node that turns Stripe’s invoice object into the fields the template expects (see the mapping below).
- Generate the invoice PDF — the pdfmill node: Generate from Template, template Invoice, Data
{{ $json }}, Format PDF. Out comes the invoice as a binary ondata. - Email the invoice — Gmail sends the
databinary to the customer. Swap it for SMTP, Outlook, Google Drive, or S3 without touching the Generate step.
Mapping Stripe’s data to the invoice
Stripe’s invoice object doesn’t match pdfmill’s field names one-to-one, and its amounts are in the smallest currency unit (cents). The Code node bridges the two — divide amounts by 100, and map:
number→invoiceNumber;created→issuedAt;due_date→dueAt.customer_name/customer_email/customer_address→billTo.lines.data[]→items[](description,quantity,unitPrice,lineTotal), each amount ÷ 100.subtotal→subtotal;total_taxes[]summed →taxAmount;total→total, each ÷ 100.
For a one-off Checkout payment instead of a Stripe invoice, trigger on checkout.session.completed and map that object (amount_total, customer_details) into the same shape — the rest of the workflow is unchanged.
Going further
Store it as well as send it — point the data binary at Google Drive, S3, or Dropbox. Want a receipt or a quote off the same payment? Keep the trigger and mapping and switch the pdfmill template. The plain-invoice version (with a manual trigger you can run once) is the n8n invoice generator guide.
pdfmill is flat-priced with a hard cap: a permanent Free tier of 75 documents a month, then $9/500, $19/2,000, $49/10,000 — one document = one count, and at the limit the node returns a clear QUOTA_EXCEEDED (402), never a silent overage. Current numbers on the pricing page.
FAQ
Does Stripe generate invoice PDFs?
Yes. Stripe hosts an invoice page and a downloadable PDF for every invoice, and can email it automatically. The catch is that it is Stripe-branded with a fixed layout — you get Stripe’s template, not yours. Generate your own only when you need custom branding, a different layout, extra fields, or a document type Stripe does not produce (a quote or a delivery note).
How do I create a branded invoice PDF from a Stripe payment in n8n?
Use a Stripe Trigger on the invoice.paid event, a Code node to map the Stripe invoice into pdfmill’s invoice fields, then a Generate PDF node (Invoice template) and an email node. The workflow on this page does exactly that — copy it onto your canvas, add your Stripe, pdfmill, and Gmail credentials, and activate it.
Which Stripe event should trigger the invoice?
invoice.paid for customers you invoice through Stripe Billing. For one-off Checkout payments, trigger on checkout.session.completed instead, and map that object’s fields (amount_total, customer_details) into the same invoice shape. (A raw payment_intent.succeeded carries amount but not the same customer/line-item detail, so prefer the Checkout session or the invoice.)
Are Stripe amounts in cents?
Yes — Stripe returns amounts in the smallest currency unit (e.g. cents), so 4200 means 42.00. The Code node in this workflow divides every amount by 100 before passing it to the invoice template.
Can I use this for receipts or quotes too?
Yes. Keep the same Stripe trigger and mapping and switch the pdfmill template — invoice, quote, or receipt — since they read the same data shape. One rendered document counts as one toward your plan.
The workflow above is a real, working template — checked August 5, 2026. Stripe’s API and pdfmill’s prices change; this guide links the pricing page rather than repeating numbers that go stale.
See for yourself
Render your first PDF from an n8n workflow in about five minutes — the Free plan is 75 documents a month, no card required.