Rendex
Recipe

Auto-Generate Invoice PDFs from Stripe in n8n

Turn every Stripe payment into a branded, print-ready PDF invoice with one n8n workflow. Stripe fires, n8n maps the line items, Rendex renders the HTML template to PDF, and the customer gets their receipt — automatically.

Last updated 2026-07-21

The Problem

Stripe sends a payment confirmation, but its built-in invoices are rigid — you can't fully control the layout, branding, or the fields, and customers often ask for a proper PDF you can't easily produce on the fly. Rolling your own means hosting Puppeteer or wrestling with wkhtmltopdf inside an n8n worker, which breaks fonts, drops modern CSS, and adds infrastructure you have to babysit.

The Solution

Design the invoice once as an HTML template with {{customerName}}, {{invoiceNumber}}, {{lineItems}}, and {{total}} placeholders. On each Stripe payment, n8n maps the event into a data object and posts it to Rendex, which renders the template to a print-ready A4 PDF with full modern CSS — Flexbox, Grid, web fonts, print backgrounds. Email the PDF directly, or request a hosted signed link to drop into the email body. All on Rendex's free tier.

How the Workflow Runs

Stripe Trigger

Fires on checkout.session.completed / invoice.paid

Set / Function

Build customer, invoice number, and line-item data from the event

Rendex

Render the HTML invoice template to a print-ready PDF (and/or a hosted link)

Gmail / SMTP

Email the PDF to the customer (+ archive to Drive or S3)

Input → Rendered Output

Left: a raw Stripe payment event JSON with customer, amount, and invoice number. Right: the same data rendered as a clean, branded PDF invoice with a PAID badge, itemized line items, and a total — ready to email.

Rendered by Rendex

What You Need

  • A Rendex API key — the free tier gives you 100 renders/month with no card required
  • n8n (cloud or self-hosted) with the Rendex community node installed and Stripe credentials connected
  • An invoice HTML template with {{customerName}}, {{invoiceNumber}}, {{lineItems}}, and {{total}} placeholders (line items rendered as table rows)
  • An email node (Gmail, SMTP, or your provider) and optionally a Drive/S3 node to archive the PDF

What This Recipe Uses

HTML to PDF

Render your invoice template to a print-ready A4 PDF with full modern CSS — Flexbox, Grid, custom fonts, and print backgrounds. No headless browser to host.

Mustache data{} templating

Send one reusable HTML template plus a data object. {{customerName}}, {{invoiceNumber}}, and {{lineItems}} are filled in server-side per payment.

Hosted invoice links

Call /v1/render/link to get a signed URL for the invoice and email a short link instead of an attachment — first crawl renders and caches, repeats are free.

Free-tier ready

PDF output, data templating, and hosted links are all available on the free plan. 100 renders/month covers most small-business billing volume.

Build It

rendex-invoice-node.json
{
  "parameters": {
    "resource": "screenshot",
    "operation": "capture",
    "source": "html",
    "html": "<!doctype html><html><head><meta charset=\"utf-8\"><style>body{font-family:system-ui,Segoe UI,Roboto,sans-serif;color:#0f172a;margin:0;padding:48px;}h1{color:#ea580c;font-size:24px;margin:0 0 4px;}table{width:100%;border-collapse:collapse;margin-top:28px;}th,td{padding:10px 12px;text-align:left;}thead th{border-bottom:2px solid #e2e8f0;font-size:12px;text-transform:uppercase;color:#64748b;}tbody td{border-bottom:1px solid #f1f5f9;}td.amt,th.amt{text-align:right;}tfoot td{font-weight:700;font-size:16px;padding-top:16px;}</style></head><body><h1>Invoice {{invoiceNumber}}</h1><p>Billed to {{customerName}}</p><table><thead><tr><th>Description</th><th class=\"amt\">Amount</th></tr></thead><tbody>{{#lineItems}}<tr><td>{{description}}</td><td class=\"amt\">{{amount}}</td></tr>{{/lineItems}}</tbody><tfoot><tr><td>Total</td><td class=\"amt\">{{total}}</td></tr></tfoot></table></body></html>",
    "templateData": {
      "customerName": "={{ $json.customer_details.name }}",
      "invoiceNumber": "={{ 'INV-' + $json.id.slice(-8).toUpperCase() }}",
      "total": "={{ '$' + ($json.amount_total / 100).toFixed(2) }}",
      "lineItems": "={{ $json.line_items.map(li => ({ description: li.description, amount: '$' + (li.amount_total / 100).toFixed(2) })) }}"
    },
    "format": "pdf",
    "pdfFormat": "A4",
    "pdfPrintBackground": true,
    "pdfMargin": { "top": "24px", "right": "24px", "bottom": "24px", "left": "24px" }
  },
  "name": "Rendex — Render Invoice PDF",
  "type": "n8n-nodes-rendex.rendex",
  "typeVersion": 1
}
render-invoice.sh
curl -X POST https://api.rendex.dev/v1/screenshot \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  --output invoice-INV-9X2K7Q.pdf \
  -d '{
    "html": "<!doctype html><html><head><style>body{font-family:system-ui,sans-serif;color:#0f172a;padding:48px;}h1{color:#ea580c;margin:0 0 4px;}table{width:100%;border-collapse:collapse;margin-top:28px;}th,td{padding:10px 12px;}td.amt,th.amt{text-align:right;}thead th{border-bottom:2px solid #e2e8f0;}tbody td{border-bottom:1px solid #f1f5f9;}tfoot td{font-weight:700;padding-top:16px;}</style></head><body><h1>Invoice {{invoiceNumber}}</h1><p>Billed to {{customerName}}</p><table><thead><tr><th>Description</th><th class=\"amt\">Amount</th></tr></thead><tbody>{{#lineItems}}<tr><td>{{description}}</td><td class=\"amt\">{{amount}}</td></tr>{{/lineItems}}</tbody><tfoot><tr><td>Total</td><td class=\"amt\">{{total}}</td></tr></tfoot></table></body></html>",
    "data": {
      "customerName": "Acme Robotics, Inc.",
      "invoiceNumber": "INV-9X2K7Q",
      "lineItems": [
        { "description": "Rendex Pro — May 2026", "amount": "$49.00" },
        { "description": "Priority support", "amount": "$6.00" }
      ],
      "total": "$55.00"
    },
    "format": "pdf",
    "pdfFormat": "A4",
    "pdfPrintBackground": true,
    "pdfMargin": { "top": "24px", "right": "24px", "bottom": "24px", "left": "24px" }
  }'
hosted-invoice.mjs
import Rendex from "@copperline/rendex"

const rendex = new Rendex(process.env.RENDEX_API_KEY)

// Same template + data — but ask for a hosted, signed URL instead of bytes.
// Email this short link to the customer; the first crawl renders and caches.
const { url, expiresAt } = await rendex.renderLink({
  html: INVOICE_TEMPLATE, // your HTML with {{customerName}} {{invoiceNumber}} {{lineItems}} {{total}}
  data: {
    customerName: session.customer_details.name,
    invoiceNumber: "INV-" + session.id.slice(-8).toUpperCase(),
    lineItems: session.line_items.map((li) => ({
      description: li.description,
      amount: "$" + (li.amount_total / 100).toFixed(2),
    })),
    total: "$" + (session.amount_total / 100).toFixed(2),
  },
  format: "pdf",
  pdfFormat: "A4",
  pdfPrintBackground: true,
  expiresIn: 2592000, // 30 days
})

console.log("Invoice ready:", url, "expires", expiresAt)
// -> send `url` in your Gmail/SMTP node as the "Download your invoice" link

100 free API calls/month — no credit card required. Get your API key and start building.

Reach for this workflow when you sell through Stripe Checkout or Stripe-hosted invoices but need a branded PDF that matches your own template — not Stripe's default receipt. It is the right fit for solo founders, agencies billing clients, and SaaS teams who want every successful payment to produce a clean, archivable document without standing up a PDF microservice. Because the invoice is just an HTML template with Mustache placeholders, your designer can iterate on it in the browser, and n8n only has to map the Stripe event into a flat data object. You get deterministic layout, real fonts, and a print-ready A4 file on each payment. Use the direct PDF render when you want to attach the file to an email or archive it to Drive or S3; use a hosted render link when you would rather email a short URL the customer can open or re-download. Both paths run on the free 100 renders per month, no card required.

Frequently Asked Questions

Related Resources