Generate an Invoice PDF From JSON in Node.js (No Templates, No Puppeteer Setup)
Every app that charges money eventually needs to send an invoice. The usual path is: pick a PDF library, design a layout, fight with page breaks, then maintain it forever. This post shows a shorter route: describe the i
Every app that charges money eventually needs to send an invoice. The usual path is: pick a PDF library, design a layout, fight with page breaks, then maintain it forever.
This post shows a shorter route: describe the invoice as JSON, get a PDF back. I'll use GetInvoicePDF, a small API I built, but I'll also be upfront about when you should not use it.
1. Get an API key
Sign up at getinvoicepdf.dev and copy your key from the dashboard (the free tier covers 20 invoices a month).
2. Send the invoice data
// generate-invoice.mjs (Node 18+, no dependencies)
import { writeFile } from "node:fs/promises";
const res = await fetch("https://getinvoicepdf.dev/api/v1/invoices", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.GETINVOICEPDF_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
template: "modern",
invoiceNumber: "INV-1001",
date: "2026-10-16",
currency: "USD",
accentColor: "#0f766e",
seller: { name: "Acme Studio", email: "[email protected]", address: "12 Market St, Austin, TX" },
buyer: { name: "Jane Client", email: "[email protected]" },
lineItems: [
{ description: "Website design", quantity: 1, unitPrice: 1800 },
{ description: "Revisions (hours)", quantity: 4, unitPrice: 95 },
],
taxRate: 8.25,
notes: "Payment due within 14 days. Thank you!",
}),
});
if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`);
await writeFile("invoice.pdf", Buffer.from(await res.arrayBuffer()));
console.log("saved invoice.pdf");
GETINVOICEPDF_KEY=your_key node generate-invoice.mjs
That's the whole integration. The totals, tax line and layout are computed for you.
3. Store the invoice and fetch it later
Add ?format=url and the API keeps the PDF and returns an id and a url instead of the file:
const res = await fetch("https://getinvoicepdf.dev/api/v1/invoices?format=url", { /* same options */ });
const { id, url } = await res.json();
That URL is private: it only works with the same API key (or when you're signed in to the dashboard), so it isn't a link you can paste into an email to a customer. To get the PDF back later, request it with your key:
const pdf = await fetch(url, { headers: { Authorization: `Bearer ${process.env.GETINVOICEPDF_KEY}` } });
To send an invoice to a customer, attach the PDF from step 2 to your own email.
4. Turn an invoice into a receipt
When the customer pays, send the same data with paid: true (and optionally paidDate and paymentMethod). The document gets a PAID stamp and becomes a receipt.
Python
import os, requests
r = requests.post(
"https://getinvoicepdf.dev/api/v1/invoices",
headers={"Authorization": f"Bearer {os.environ['GETINVOICEPDF_KEY']}"},
json={
"seller": {"name": "Acme Studio"},
"buyer": {"name": "Jane Client"},
"lineItems": [{"description": "Consulting", "quantity": 3, "unitPrice": 150}],
"taxRate": 0,
},
)
r.raise_for_status()
open("invoice.pdf", "wb").write(r.content)
When not to use this
- You need pixel-perfect control of a custom layout: use an HTML-to-PDF service or run Puppeteer yourself.
- You need legally structured e-invoices (Factur-X, ZUGFeRD, PEPPOL, FatturaPA): this produces a normal PDF, not those formats. If that's your market, I'd like to hear about it, since I'd expect it to be the biggest gap.
Under the hood (for the curious)
A Next.js app renders the chosen template to HTML and Puppeteer prints it to PDF. API keys are stored as SHA-256 hashes. It runs on Fly.io with Postgres.
Questions, bugs, or a field you need that's missing? Leave a comment, I read all of them.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.