n8n
n8n-nodes-formfeed is a community node that n8n has verified: two nodes (Formfeed and
Formfeed Trigger) and one credential. No Formfeed-side setup beyond an API key.
Install
From the nodes panel (n8n Cloud and self-hosted n8n): open the nodes panel, search for
Formfeed (it carries n8n's verified badge), open it and click Install node. The owner or an
admin of the instance installs it once, for every member. If the search does not find it, verified
community nodes are switched off: on n8n Cloud in the instance settings of the Cloud console, on a
self-hosted n8n by N8N_VERIFIED_PACKAGES_ENABLED=false.
By package name (self-hosted n8n): Settings → Community nodes → Install, package name
n8n-nodes-formfeed. Alternatively run npm install n8n-nodes-formfeed in the n8n user folder
(~/.n8n/nodes) and restart n8n.
Without the Formfeed node, the HTTP Request node calls the API (see Rendering), and n8n's AI Agent can use the hosted MCP endpoint.
Credential
Create an API key in the app under API keys and paste it into the
Formfeed API credential. Use a ff_test_ key while building: those renders are free (watermarked
on the Free plan). The credential's test reads GET /account.
| Field | Meaning |
|---|---|
| API Key | ff_live_… or ff_test_… |
| Region | Europe (Frankfurt), the default, or United States. Leave it on Europe: every workspace is in the EU today |
| Custom Base URL | Leave empty. An address Formfeed support gives you, such as a staging gateway, with /v1; it wins over the region |
The default scopes of a new key (render:create, render:read, template:read, account:read)
cover the credential test and every render, template and job operation. The others need one more
scope, which a new key has only when you tick it:
| Operation | Scope |
|---|---|
| Render → Delete Outputs | file:delete |
| File → Upload | file:write |
| File → Get Many | file:read (included in file:write) |
| File → Delete | file:delete |
| Formfeed Trigger | webhook:manage |
Formfeed node
| Resource | Operations |
|---|---|
| Render | Create (from a template, raw HTML or a URL), Create E-Invoice, Get, Get Many, Delete Outputs |
| Template | Get Many, Get, Get Schema |
| Job | Get (batch progress and the zip) |
| File | Upload (from a binary field, into the file library), Get Many (optionally one folder), Delete |
| Convert Office Document (a Word, Excel, PowerPoint, OpenDocument or RTF file from a binary field, or the render of a Word or PowerPoint template, to PDF; Starter plan and above) |
The Get Many operations return up to Limit results (at most 100), or every one with Return All.
Render → Create
Source chooses what is rendered: Template, HTML (with Engine: Jinja2, the default, Liquid or Handlebars) or URL (a public page).
With a template, Fields maps the data: add a field, choose its name from the list and set its
value. The list comes from the template's data schema or, when it has
none, from its sample data; nested objects appear as dotted names (invoice.number,
customer.name), * marks a required field and the type follows in brackets. A list or a free-form
object appears as one field whose value comes from an expression, or goes into Data (JSON).
Data (JSON) is merged over the mapped fields value by value: an object adds to the mapped fields
at the same place, a value replaces the mapped one, and a list replaces the whole list. The template
list shows the workspace's templates by name and kind (up to 100); only published templates render.
Output is As the Template by default: PDF for a PDF template, its image format for an image template, the default output of a Word or PowerPoint template, and PDF for HTML and URLs. Word templates render DOCX or PDF, PowerPoint templates PPTX or PDF.
With Download File (on by default) the document is attached as binary data, ready for Gmail,
Google Drive, S3 or an HTTP request. Its name follows the output, so a file name offer becomes
offer.docx or offer.pdf; without a file name it is <render id>.<output>.
The Options:
| Option | Meaning |
|---|---|
| Filename | Name of the document |
| Locale | Overrides the template locale, for example de-DE |
| Mode | Wait for the Document (the default) or Queue and Return Immediately, see below |
| Settings (JSON) | Paper, margins, header and footer, as in Override settings per request |
| Version | The release channel to render (published by default), or a version number as an expression |
| Webhook URL | An address that receives this one render's render.completed or render.failed event, see Per-render webhook URLs |
Mode: Wait for the Document asks for the document synchronously. A render that takes longer
than a minute continues in the background, and the node then returns it with status: queued and
no file. For long documents set Queue and Return Immediately: the node returns the queued render at
once, and either the trigger node or a Get in a later step reports the result.
E-invoices
Render → Create E-Invoice makes a ZUGFeRD / Factur-X e-invoice: a
PDF/A-3 that carries the invoice as XML, checked by the validator before the node receives it.
Instead of a template's fields, the node asks for the invoice itself: number, issue date and
currency, seller and buyer, the lines, the VAT breakdown, the five totals and the payment. It sends
them as the _invoice block, and the template prints the same block, so every value is mapped once.
The E-invoice example of the gallery in the app is such a template.
Nothing is computed: the totals and the VAT breakdown are the ones your system states, and an invoice whose totals do not add up to the cent is refused with every field and the rule it breaks. What the fields do not offer (the invoice a credit note refers to, allowances and charges, the delivery, a direct debit mandate, the French mentions) goes into Invoice (JSON) with the names of the field reference, merged over the fields. The options choose the profile (EN 16931 or Basic), the name of the specification (Factur-X or ZUGFeRD; the file is the same), whether the XML is also stored as a file of its own, and whether a value the PDF does not show fails the render instead of being reported.
The node's output carries the validator's report as einvoice. Live e-invoices need the Starter plan
or higher; test keys make a limited number a day, each with the test mark (on Free up to 20, see
Plans and units).
When the API refuses a request
From version 0.3.20 a refused request stops the node with the API's own sentence, which names the
field and the rule it breaks (_invoice.lines[0].tax.rate is 0: … (BR-S-05)); before, the node
showed n8n's sentence for the status ("Your request is invalid or could not be processed by the
service"). With On Error → Continue (using error output) in the node's settings, the item leaves
by the error output instead, with the fields it came in with and two more: error, that sentence,
and details, the whole problem the API answered with. Its code names the
problem, and for an e-invoice that is refused errors lists every
finding with its path and rule, so a workflow can write them back to where the data came from.
As a tool for an AI agent
From version 0.3.11 the Formfeed node can be connected to the Tools input of n8n's AI Agent node.
The agent then picks the operation and fills in its fields: it lists the templates, reads a
template's schema and renders it. A current n8n offers the node as a tool without any setting (seen
in n8n 2.42); older self-hosted versions did so only when the instance set
N8N_COMMUNITY_PACKAGES_ALLOW_TOOL_USAGE=true. Give the node a credential with a
ff_test_ key while you try it out. Agents outside n8n use the MCP server.
Formfeed Trigger node
Registers a webhook endpoint in your workspace when the workflow is activated and removes it again
when it is deactivated; while it exists, it is listed in the app under Webhooks as n8n.
Events (required, render.completed by default): render.completed, render.failed,
job.completed, job.failed, quota.warning, quota.exceeded. It fires for renders of live and
test keys alike. The event body is the one described in Webhooks.
The node does not check the delivery's signature: the address n8n generates for the webhook is a secret of its own, and a render named in an event can be read back with Render → Get when a workflow must be sure of it.
Example
- Schedule Trigger every morning.
- Postgres node: yesterday's invoices.
- Formfeed: Render → Create, template
invoice-de, mapinvoice.numberandcustomer.namefrom the row, put the line items into the JSON field, Download File on. - Gmail: send the binary as an attachment.
For a batch of hundreds of documents, call POST /renders/batch with the HTTP Request node and let
the Formfeed Trigger wait for job.completed, instead of one render per row. See
Async and batch.