Guide
Automating PDF fills, merges, and invoices in n8n without a Function node
Most n8n workflows that touch PDFs end up in the same place: an HTTP
Request node with a hand-built multipart body, or a Code node
calling pdf-lib and hoping the binary handling survives the next
n8n upgrade. n8n-nodes-pdfops
is a verified community node that turns PDF inspect, fill, merge,
and invoice generation into typed n8n operations, with binary PDFs
flowing in and out like any other node.
Installing it
On n8n Cloud, open the nodes panel and search "PDFops"; it installs in one click because it carries n8n's verified badge. Self-hosted, it's Settings → Community Nodes → Install, or from the CLI:
n8n community-node install n8n-nodes-pdfops
The node works with no credential at all, against the same
100-requests-per-IP-per-month anonymous tier the
playground uses. Add a PDFops
API credential (an API key in the X-API-Key
header) once you want a higher quota or the usage operation
described below.
The five operations
One node, five operations, picked from a dropdown: Inspect Fields lists a PDF's AcroForm field names, types, and current values, plus a fill template you can paste straight into the next node. Fill Form writes values into those fields and can flatten the result so it stops being editable. Merge concatenates every PDF on the incoming items, in item order, into one file. Generate Invoice renders a complete invoice PDF from a JSON object, no template required. Get Usage returns tier, quota, used, and reset date for a key. Fill, merge, and invoice are deterministic: run the same workflow twice on the same input and the output PDF is byte-identical, so a re-run never shows up as a spurious diff in whatever you archive it to.
A worked example: fill from a webhook, catch the typo first
The node ships with three ready-to-import workflow templates. The one worth walking through end to end is fill and flatten PDF forms from webhook data, because it solves the failure mode that makes "just fill the PDF" workflows unreliable: a field name typed wrong in the JSON payload doesn't error, it just fills nothing, and the caller gets back a document that looks correct until someone opens it.
The workflow receives a webhook carrying a PDF URL, an email
address, and a fields object, validates the payload,
then runs Inspect Fields on the blank PDF before
it fills anything. A Code node diffs the requested field names
against the real ones the inspect call returned. Any name that
doesn't match routes straight to an error response, naming the bad
field, instead of silently producing a blank form. Only a payload
that passes the diff reaches Fill Form with
flatten on, so the result can't be edited after it
ships. From there it fans out: Gmail sends the filled PDF, Google
Drive archives a copy, and the caller gets a JSON receipt.
Swap Gmail for SMTP or Slack and Drive for S3 without touching the PDF steps; none of them know or care where the file ends up.
Two more recipes, already built
Merge two forms into one packet. A workflow named onboarding packet from two PDF forms takes two blank PDF URLs and two field objects (a new-hire packet, a new-tenant packet, whatever your business fills in pairs), fills each with its own Fill Form node, waits for both branches, and joins them with Merge so the first form always lands as page one. It refuses to upload a packet unless both branches actually filled, so a partial run never produces a half-finished file that looks whole.
An invoice on every Stripe payment. A Stripe
trigger on payment_intent.succeeded feeds a Code node
that shapes the charge into invoice JSON, and this is the step
where most hand-rolled invoice code breaks quietly: Stripe reports
amounts in the smallest currency unit, but JPY, KRW, and thirteen
other currencies don't have 100 of those units, so a reflexive
amount / 100 issues an invoice a hundred times too
small. The template checks the currency against a zero-decimal
list before it divides. Generate Invoice renders
the deterministic PDF, then it archives to S3, emails the customer
when an address is on file, and logs a row to a spreadsheet ledger.
A failed render posts to Slack with the payment ID, so a missing
invoice gets noticed instead of just not existing.
Keyless works; a key gets you further
Every operation runs without a credential at the shared 100/IP/mo anonymous rate, the same limit the API enforces everywhere else. A free API key (250 requests/month, email in, key out, no card) raises that ceiling and is required for the Get Usage operation, which returns your remaining quota so a workflow can check headroom before it burns the month's last call. Paid tiers exist once a workflow outgrows 250 a month, but the free key runs a real production automation, not just a demo.
Try it
Search "PDFops" in the n8n nodes panel, or install
n8n-nodes-pdfops self-hosted. The three templates
above are importable as-is from the node's
npm page;
swap in your own PDF, Stripe account, or storage provider and the
PDF steps don't change. Building on the raw API instead? Start at
/docs or the
OpenAPI spec.