Changelog
API changes within /v1 are additive: new endpoints, new optional fields, new response fields, new error codes and new event types. Anything that could break a client ships in a new version and is announced here first.
25 October 2026: packages 0.3.20
- n8n node: a refused request names its field and rule, in the node's error and on its error output (see 24 October).
- Plugin for coding agents: formfeed-dev/skills also carries the files Codex, GitHub Copilot, the Gemini CLI and Antigravity read, each naming the hosted MCP server in that agent's words.
- The other packages move to 0.3.20 without changes of their own.
24 October 2026: the n8n node says why a request was refused
- n8n node:
n8n-nodes-formfeed0.3.20 shows the API's own sentence when a request is refused, which names the field and the rule it breaks, instead of n8n's sentence for the status ("Your request is invalid or could not be processed by the service"). With Continue (using error output), the item on the error output carries that sentence aserrorand the API's whole answer asdetails, so a refused e-invoice lists every finding with itspathandrule(see n8n). Until now the error output said only n8n's sentence.
22 October 2026: the n8n node is verified
- n8n: n8n has verified
n8n-nodes-formfeed, so n8n Cloud offers it as well as self-hosted n8n: open the nodes panel, search for Formfeed and click Install node (owners and admins; see n8n). Installing by package name still works on a self-hosted n8n. A current n8n offers the node to its AI Agent as a tool without the settingN8N_COMMUNITY_PACKAGES_ALLOW_TOOL_USAGE, which older self-hosted versions needed.
21 October 2026: packages 0.3.19
- SDKs: the types of
@formfeed/sdkand the Python packageformfeedknow the usage'speriod_start,period_endandhard_cap_units, which the API has always sent (see 20 October). In TypeScript a version'sdata_setsandpartialsare no longernull, which the API never sends, and both SDKs give the range of a watermark'srotationand say that an image stands upright when it is left out. - Template engine:
renderVersionof@formfeed/enginerenderspdf.metadata.author,subjectandkeywordswith the data, as renders do since 19 October, and returns them asmetadata; one that renders empty is left out. - Plugin for coding agents: the plugin in formfeed-dev/skills brings two skills beside the hosted MCP server:
formfeed-templateswrites and checks templates in a project with the command line, andformfeed-apiwrites the code that renders through the API. Both say so before a render with a live key costs units, and change the workspace only when asked. Claude Code installs it as a plugin; any agent that reads Agent Skills takes the skills withnpx skills add formfeed-dev/skills. See Coding agents.
21 October 2026: the Power Automate connector is available
- Power Automate: the Formfeed connector Microsoft certified on 5 October is in makers' environments: add an action or a connection in Power Automate, Power Apps or Copilot Studio and search for Formfeed, published by Formfeed and marked Preview. Where it is not offered, the HTTP action makes the same call. See Power Automate.
21 October 2026: keys, endpoints and members in the search box
- App: the search box on
Ctrl+Kfinds API keys by name and prefix, webhook endpoints by address, and the members of the organisation by name and e-mail, with its open invitations for owners and admins. A pasted API key finds the key it belongs to by its first characters, in the browser only.→on a template lists its actions (open it, its API tab, its renders, copy its slug or id) and←goes back. Owners and admins find Create an API key, Add a webhook endpoint and Invite members there too. See the editor tour. - Changed: Copy template ID in the templates list copies the id the API shows (
tpl_…) instead of the bare UUID. The API takes both, and the search box opens a pastedtpl_…id as the template.
21 October 2026: a batch keeps the order of its items
- Fixed:
GET /jobs/{id}, thejob.completedevent and the batch zip list a batch's items in the order of the request, as documented. They listed them in the order the renders finished, which differs whenever items finish out of order (renders run side by side, documents differ in length, a retried item finishes last), soitems[2]could be another row's document and0003-document.pdfanother item's file. A zip's files are now numbered by their item's position in the request, and a failed item leaves its number out. Batches sent before this fix keep the order they had. See Async and batch.
21 October 2026: a failed render in the apitemplate.io shim, and a password's permissions
- Fixed: an error of the apitemplate.io shim about a render that was recorded before it failed carries the render's id as
transaction_ref, as the error of a render still running already did. Template errors and page limits came back without it, so the failed render could not be looked up. - Fixed: a render with
post.passwordand nopermissionsis recorded with the four permissions its PDF allows (print,copy,modify,annotate). It was recorded with none, which is what[]means, so the render's options in the app said readers may do nothing. The PDFs themselves were right.
21 October 2026: four answers that said less than they should
- Changed:
POST /webhooks/{id}/testfor a disabled endpoint answers409 conflict. It answered202and nothing arrived, because a disabled endpoint receives no deliveries. See Webhooks. - Fixed: a repeated request with the same
Idempotency-Keycarries the first answer'sLocation,Retry-After,X-Units-*andX-Render-*again, not its body alone, so a client that follows theLocationof a202can follow a replayed one too. See Idempotency. - Fixed: a PDF tool that fails after it was recorded names its render as
render_idin the problem, as a failed render does; the render log andGET /renders/{id}show it. - Fixed:
POST /pdf/watermarkwith an image checks the key's scope and plan before it looks for the image in the library, so a key that may not use the tool hears403 forbidden, never whether the library has a file of that name.
21 October 2026: the render log keeps 13 months
- Changed: render log entries are kept for 13 months. Once a month, older entries are deleted with their batch jobs, each once its files are gone: an entry whose file is still stored stays as long as the file does.
GET /renders/{id}andGET /jobs/{id}answer404for what was deleted, andGET /usagekeeps the totals of every billing period but lists the days and templates of a period only while its entries exist. Until now entries stayed until the workspace was deleted; the oldest reach 13 months in October 2027. See Data residency.
20 October 2026: slugs of two characters
- Fixed: a slug of two characters, such as
deorq4, is accepted for templates, organisations and workspaces. The rule took one character or three and more, but never two, in the API, the app and the database alike. Slugs are 1 to 64 characters for templates and 1 to 40 for organisations and workspaces, lowercase letters, digits and dashes, starting and ending with a letter or digit. See Templates.
20 October 2026: the API contract says what the API does
- Changed: the API reference and
/v1/openapi.jsonnow describe every field, header and status code the API sends. A render'serrorhas a schema of its own (code,messageand what says more), no longer that of a problem; a render, a job, an account and the usage list every key they always carry; the problems name their members with the codes they come with; the binary answer ofoutput=binarylists all six file types and its headers; and the apitemplate.io shim's answers, its302and its errors in apitemplate.io's shape are described as they are. The answers themselves change only where an item below says so. - Fixed:
POST /templateskeepsdata_sets, the named sample data sets, in the first version. It dropped them, soformfeed templates pushlost them for a new template. - Fixed: publishing a version of a Word or PowerPoint template (
POST /templates/{id}/versions/{number}/publish) answered with the file's internal storage key, which no other answer shows. It is gone from the answer. - Fixed: a
cursorthat no page of the list handed out answers400 invalid_requestonGET /renders,/templatesand/files, where it answered500; so doGET /usage?period=with a month that does not exist andoutput_image_typevalues the apitemplate.io shim does not know. The shim now also takes apitemplate.io'sjpegOnlyandpngOnly, and answers JSON when its URL carriesoutput=binary, where it answered500after rendering. - Fixed: an id of 36 characters that is no UUID answers
404on the webhook routes, where it answered500; aformfeed listensession's id is unknown toDELETE /webhooks/{id}andPOST /webhooks/{id}/test, as it already was to the list andPUT(end a session atDELETE /webhooks/listen/{id}); andPOST /webhooksalways answers withenvironments. - Changed: the
202ofPOST /renders/batchnames the job inLocationas an absolute URL, as a render's202does, withRetry-After: 5. Arate_limitedproblem of a Free test key's minute sayswindow_seconds: 60, where it said 1.
19 October 2026: metadata from the data, a time limit for printing, images that open
- Changed: in a template render,
pdf.metadata.author,subjectandkeywordsare rendered with the data like the title; one that renders empty is left out. Until now they went into the PDF as written, template syntax and all. See the settings. - Changed: printing a PDF has a time limit, 120 seconds; a print that takes longer fails with
504 render_timeout. Until now printing had none. A document of several hundred pages prints in tens of seconds. - Changed: a rendered image (PNG, JPG, WebP) opens in the browser under its file name when its link is opened, where it used to download; this holds for images rendered before too. PDFs and office files still download, and
download=1on a link makes an image a download as well. See File name.
19 October 2026: packages 0.3.18
- MCP server:
@formfeed/mcp0.3.18 returns the picture of an image render beside its link and starts withoutFORMFEED_API_KEYwith the offline checks (see below), and every tool declares what it returns (outputSchema), so a client can check the structured result. The MCP registry's entry no longer requires the key. - Template engine and command line:
@formfeed/engineandformfeedbring the new defaultwaitFor.networkIdle: "auto"and mark the engine's scripts withdata-formfeed(see 18 October), so aformfeed testsnapshot of a template with fitted text changes by that attribute: approve it again withformfeed test -u.formfeed devbreaks tables in its paged preview as the PDF does (see 18 October),formfeed ci previewnames the key that rendered, and the help of--base-url,--workspace,--ciand--verbosesays what each really does. The examples of theepcQrhelper put the invoice number intotext, the remittance line. - n8n node:
n8n-nodes-formfeed0.3.18 says that only the Free plan marks test renders, and no longer mentions a self-hosted gateway.
19 October 2026: agents see the images they render
- MCP server:
renderandget_renderreturn the picture of an image render (PNG, JPG or WebP, up to 2 MB) beside its link, so an agent can look at what it made before handing it on. A PDF still comes as its link. The hosted server atmcp.formfeed.devhas it now,@formfeed/mcpwith the next release of the packages. See MCP server. - MCP server: the local server starts without
FORMFEED_API_KEYtoo.validate_templatewithhtmlandcheck_invoicework offline then, and every other tool answers with how to get a key, so an agent can try the checks before anyone signs up. With the next release of the packages. - Command line: the summary of
formfeed ci previewnames the key that rendered: until now it said "test key" also under renders a live key made, which counted units. With the next release of the packages. - Fixed: the help of the MCP server, the n8n credential and the Zapier connection said a test key always adds a watermark; only the Free plan marks test renders. The n8n and Make connections no longer mention a self-hosted gateway, which does not exist.
18 October 2026: the paged preview breaks tables as the PDF does
- Editor: a table that runs over several pages gets its header row again at the top of every page in the Paged preview, and its
tfootat the foot of every page it goes on from, with room kept for it, as Chromium prints them. Until now the preview left both out: page 2 of an invoice had no header row, its rows moved by one a page against the PDF, and at 120 line items the preview showed five pages where the PDF has six. A header row with no row under it at the foot of a page now moves on with its table, a row taller than the rest of the page breaks under the repeated header instead of moving on, and aposition: fixedelement stands where Chromium prints it, inside the page margins. The 21 tables of Page breaks in tables come out page for page as their PDFs do, and a test holds them there. - Command line:
formfeed devshows the same with the next release of the packages.
18 October 2026: renders without the half-second wait
- Changed: a document is printed as soon as it has loaded, none of its requests is running and its fonts are ready. Until now every render also waited until the network had been idle for half a second, which for a simple document was most of its render time. A
urlrender and a template with a script of its own, an event handler attribute or a frame still wait for that half second, because their code may start a request later; the scripts Formfeed adds for charts and fitted text do not count. This is the new default ofwaitFor.networkIdle,"auto";truekeeps the old wait for a template that needs it,falsestays as it was. - Template engine: the scripts
@formfeed/enginewrites into a document carrydata-formfeed, as its styles do. Aformfeed testsnapshot of a template with fitted text (data-ff-fit) changes by that attribute; approve it again withformfeed test -u.
18 October 2026: file ids as sources, zips that expire, the webhook endpoints of a plan
- Fixed: a library file's id (
fil_…) now works as a merge source (post.merge_after,/pdf/merge) and as a watermarkimage, as the SDKs' types always said. Until now only the file's name did, and an id answered422 source_not_found. See PDF tools. - Changed: the zip of a batch is deleted when the first file in it expires, so no document is kept in a zip longer than its own retention. Until now a zip stayed until its workspace was deleted. Zips made earlier follow the same rule, and one whose first file has expired already is deleted now. See Retention and deletion.
- Changed: the number of webhook endpoints a plan includes is enforced: one on Free, five on Starter, any number from Pro, counted per organisation. One more answers
403 forbiddenwithfeature: "webhooks"and thelimit; endpoints that exist beyond the number stay. A switched-on trigger of a no-code tool is an endpoint too, aformfeed listensession is not. - Changed: the Enterprise plan's largest output is 250 MB, the size every render is held to; the plan said 1,000 MB, which no render could reach.
15 October 2026: one search box on Ctrl+K
- App:
Ctrl+K(⌘Kon a Mac) finds your templates, the examples, shared partials, files, the app's pages and the sections of settings, billing and brand, and the documentation's main pages, beside the commands of the page you are on. The part of each result that matched is marked, words may come in any order and umlauts need not be typed.#,>,/and?narrow the search to templates, commands, pages or help; a pastedrnd_…ortpl_…id opens that render or template. See the editor tour.
12 October 2026: packages 0.3.17
- n8n node:
n8n-nodes-formfeed0.3.17 brings Render → Create E-Invoice (see 9 October). Its README walks through a workflow step by step: an invoice rendered from the gallery's example and sent by email, and what the trigger hands a workflow when a render finishes. - MCP server:
@formfeed/mcp0.3.17 bringscheck_invoiceand theeinvoiceoption ofrender. With--httpit also answers the server card at/mcp/server-card, as the hosted endpoint does. - Template engine and command line:
@formfeed/engineandformfeedcheck an invoice between two French businesses by France's rules (see 9 October), and the engine exports the list of business terms asEN16931_TERMS. - SDKs: the types of
@formfeed/sdkand the Python packageformfeedknow the report's new fieldsid,versions,xml_sha256andpdf_sha256(see 9 October).
11 October 2026: a GitHub Action for templates
- GitHub Action: Formfeed templates in GitHub's Marketplace runs the command line in a workflow:
validateandteston every run, the templates a pull request changes rendered for review with the summary as one comment that each run updates, and publishing from the default branch. Use it asformfeed-dev/formfeed-action@v1. See GitHub Actions.
11 October 2026: e-invoices from WooCommerce
- Docs: WooCommerce turns every completed order into a ZUGFeRD / Factur-X e-invoice: a function that maps the order of WooCommerce's REST API to the
_invoiceblock, with products, shipping and fees as lines, coupons as allowances and the VAT of each rate as the order states it; the configuration it needs (who you are, where your VAT number plugin keeps the buyer's VAT ID, the category of a rate of 0 % and of a customer WooCommerce exempts from VAT, when an unpaid invoice is due); a webhook handler that checks WooCommerce's signature and makes each order once; and the same in n8n. Like the Stripe recipe, the page's code runs against orders shaped as WooCommerce's API returns them and through the check the API runs before it renders an e-invoice.
11 October 2026: e-invoices from Stripe
- Docs: Stripe turns every invoice Stripe finalises into a ZUGFeRD / Factur-X e-invoice: a function that maps Stripe's invoice to the
_invoiceblock, the configuration it needs (who you are, and which VAT category Stripe's taxability reasons and tax rates are), a webhook handler forinvoice.finalizedand the same in n8n. The function copies Stripe's amounts, keeps discounts as allowances and credits as negative quantities, and refuses what it cannot map, such as shipping costs or a reason without a category, rather than guessing. The page's code is run against invoices shaped as Stripe's API returns them and through the check the API runs before it renders an e-invoice.
10 October 2026: test e-invoices carry the test mark on every plan
- Changed: a test render that makes an e-invoice carries the test mark on every plan, and the paid plans count them per organisation and day as Free does: up to 100 a day on Starter, 500 on Pro and 2,000 on Business, a thirtieth of the plan's monthly units; Enterprise by contract. Until now a paid plan's test e-invoices were unmarked and uncounted, valid invoices that cost no unit. Once the day is used up, a test e-invoice answers
403 forbiddenwithlimit,usedandresets_at. The editor's true render of an invoice template and Send test request in its API tab are test renders too. Test renders of other documents stay unmarked on the paid plans, and live renders are neither counted nor marked. See Limits and usage.
10 October 2026: e-invoices from Zapier
- Zapier: the new action Create E-Invoice makes a ZUGFeRD / Factur-X e-invoice from fields for the invoice itself, grouped as invoice, seller, buyer, lines and VAT, totals and payment. Lines and the VAT breakdown are line items, so the line items of a trigger map line by line. A refused invoice names every field and rule in the Zap's error, and the output carries the validator's report. It comes with version 0.2.0 of the Zapier app. See Zapier.
9 October 2026: e-invoices from n8n and from AI agents
- n8n node: Render → Create E-Invoice makes a ZUGFeRD / Factur-X e-invoice from the fields of the invoice (seller, buyer, lines, VAT breakdown, totals, payment), which the node sends as the
_invoiceblock to a template that prints it, such as the E-invoice example of the gallery. What the fields do not offer goes into a JSON field, and the node's output carries the validator's report. See n8n. - MCP server: the new tool
check_invoicechecks an_invoiceblock offline, as the API does before it renders one, andrendertakeseinvoiceto make the PDF an e-invoice. A refused invoice comes back as a tool error that names every field and rule, so the agent can correct the block. See MCP server. - The hosted endpoint
mcp.formfeed.devhas both now;@formfeed/mcpandn8n-nodes-formfeedbring them with the next package release.
9 October 2026: the e-invoice report names what checked which file
- API: an e-invoice's report,
einvoice.validation, also carries the validator'sidfor the report, theversionsof its parts (Mustang, veraPDF, PDFBox) and the SHA-256 of the XML and of the PDF it checked,xml_sha256andpdf_sha256. For a render that succeeded,pdf_sha256is the delivered file and equals the render'soutput_sha256. Reports of earlier renders stay as they were. See What comes back. - Docs: E-invoice coverage lists the 129 of the 164 business terms of EN 16931 that the
_invoiceblock carries, with the field each comes from, and the versions of the validator and its rules, generated from the code that runs. E-invoices by country says which countries ask for what, with a source for each, and where Formfeed's part ends. - SDKs: the types of
@formfeed/sdkand the Python packageformfeedknow the new fields from the next package release, and@formfeed/engineexports the list of business terms asEN16931_TERMS.
9 October 2026: invoices between French businesses follow France's rules
- Fixed: an e-invoice between two French businesses in the profile
basicis refused with422 einvoice_data_invalid. France's reform takes the profiles EN 16931 and EXTENDED-CTC-FR only (AFNOR XP Z12-012), so such a file was valid by the European rules but not one a French platform accepts.en16931, the default, is unchanged. - Fixed:
operation_codetakes every billing framework the French rules list.S3(public buyers) and the frameworks of invoices with several sellers,B8,S8andM8, are no longer refused. - Changed: such an invoice also needs the three statutory notes (
PMT,PMD,AAB) and the electronic addresses of seller and buyer, which France's rules call mandatory (BR-FR-05,BR-FR-12,BR-FR-13). Until now the validator only warned about a missing one and the file was delivered; now the request is refused before rendering with422 einvoice_data_invalid. formfeed validate,formfeed einvoiceand@formfeed/enginecheck the same from the next package release.
9 October 2026: packages 0.3.16
- All packages move to 0.3.16 without changes of their own. The release carries a copy of the n8n node's credential file at the root of the public repository, where n8n's verification of community nodes looks for it.
9 October 2026: packages 0.3.15
- Template engine:
@formfeed/engine0.3.15 takes only text as a template's source (body, header, footer, PDF title and partials) and requires Handlebars 4.7.10. Handlebars compiles an object as an already parsed syntax tree, and up to 4.7.9 values of such a tree could run as code (GHSA-8r5x-fm3f-whwj). - n8n node:
n8n-nodes-formfeed0.3.15 follows n8n's rules for verified community nodes: option lists in alphabetical order, Get Many returns 50 results when no limit is set (was 25), the default release channel is labelled Published, and the node and its credential carry icons for light and dark themes. formfeed(CLI and Python),@formfeed/sdk,@formfeed/devkit,@formfeed/testing,@formfeed/mcpand the Activepieces piece move to 0.3.15; the CLI and the MCP server bundle the engine and bring its change.
8 October 2026: retention is the plan's limit, and Tailwind for plain HTML
- Fixed: an output is no longer kept for longer than the plan's retention.
expires_inand a workspace's own retention were meant to shorten it, but a longer value simply won, so a request could ask for a year on a plan that keeps 30 days. A longerexpires_inis now cut to the retention, and a longer period in the workspace's settings counts as the plan's. Outputs rendered before this change keep the expiry they were given, and plans without a limit (Business and above) are not affected. - Fixed:
"tailwind": truein the settings compiles the Tailwind classes of ad-hochtmlwithout anenginetoo. Until now only HTML sent with an engine got the stylesheet, and plain HTML was printed unstyled. A complete document gets the stylesheet at the front of its head, before its own styles.
7 October 2026: packages 0.3.14
- SDK:
@formfeed/sdk0.3.14 callsfetchthe way browsers and Cloudflare Workers require. Until now a client on those runtimes failed every request withIllegal invocationunless afetchwas passed in; Node was never affected. - MCP server:
@formfeed/mcp0.3.14 adds the toolget_workspace, and its hosted endpointhttps://mcp.formfeed.dev/mcptakes a sign-in beside the API key, the workspace chosen at consent. The self-hosted--httpserver takes the sign-in on/mcptoo once--auth-serveris set. formfeed(CLI and Python),@formfeed/engine,@formfeed/devkit,@formfeed/testing, the n8n node and the Activepieces piece move to 0.3.14 without changes of their own.
7 October 2026: the hosted MCP server answers tool calls
- Fixed: every tool call on
mcp.formfeed.devfailed withnetwork_error: Illegal invocation, with an API key as with a sign-in, since the hosted endpoint went live on 17 September. The TypeScript SDK, which the endpoint is built on, called the globalfetchas a method of its client, which Node accepts and browsers and Cloudflare Workers refuse. The endpoint answers since the deploy of this day. - SDK: the same fault affected
@formfeed/sdkin a browser or a Cloudflare Worker whenever nofetchwas passed in. Fixed in the sources; the next package release carries it. Until then, passfetch: (input, init) => fetch(input, init)on those runtimes.
7 October 2026: one MCP address, the workspace chosen at consent
- MCP server:
https://mcp.formfeed.dev/mcptakes a sign-in as well as an API key. A chat client opens the Formfeed sign-in itself, and the consent page asks which workspace the agent works in; the choice travels in the agent's token, and the API checks the membership on every call as before.https://mcp.formfeed.dev/mcp/<workspace id>still works and pins another workspace for a second connection. - MCP server: the new tool
get_workspaceanswers the workspace, organisation, plan, environment and units of the period, asGET /accountdoes, so an agent can say where a document lands and how many renders are left. An agent's token may now read the account (account:read) and nothing more than before. - App: Account → Connected agents lists every agent you allowed, with its workspace to change and a revoke that signs the agent out. A client allowed before this change is asked for its workspace the next time it connects.
- Changed:
POST /templates/{id}/validateand the other/v1routes answer403with the reason for an agent whose person chose no workspace yet, where they answered400for the missingX-Formfeed-Workspaceheader.
5 October 2026: packages 0.3.13
- CLI:
formfeed0.3.13 works with accessible PDFs. For a template whosesettings.jsondeclarespdf.ua,formfeed validatereports what the source already says about the file, such as a missing title or an image written withoutalt: as errors where the check is strict, as warnings where it reports.formfeed renderprints the validator's verdict, and a strict render whose file fails is a finding with exit code 1.formfeed devchecks the page it previews. - Engine:
@formfeed/engine0.3.13 exports the static template check thatformfeed validateand the API's validation run:staticAuditover a template's sources, the findings with the rules each one fails (AUDIT_CODES,AUDIT_RULES) andresolveUafor the setting.charttakes analtin its options, which a chart needs under PDF/UA-1. Checking the finished file against the standard happens in the API, not in the package. - Dev kit:
@formfeed/devkit0.3.13 exportsdiagnoseAccessibility, which the CLI builds on. - SDKs:
@formfeed/sdkand the Python packageformfeed0.3.13 know theaccessibilityreport on a render:Accessibility,AccessibilityFailureandAccessibilityWarningin both, andPdfUaOptionsfor the setting in TypeScript. - MCP server:
@formfeed/mcp0.3.13 hands the report on with a render, and tells an agent which rules a strict render failed and in which elements. @formfeed/testing, the n8n node and the Activepieces piece move to 0.3.13 without changes of their own.
5 October 2026: the Power Automate connector is certified
- Power Automate: Microsoft has certified the Formfeed connector and released it on 5 October. It works in Power Automate, Power Apps, Logic Apps and Copilot Studio, as a premium connector marked Preview like every newly certified one. Microsoft now rolls it out region by region, which takes about two weeks; until it appears in your environment, the HTTP action makes the same call. See Power Automate.
5 October 2026: e-invoices that are PDF/UA-1 files too
- API:
settings.pdf.uaand an e-invoice now go together. The render returns one file that is the ZUGFeRD / Factur-X invoice, a PDF/A-3b, and a PDF/UA-1 document. Both are checked in the file that is delivered, and the render carrieseinvoiceandaccessibility. Until now the pair answered400 invalid_request. - API: a strict render whose e-invoice is valid and whose file fails PDF/UA-1 answers
422 pdfua_validation_failedwith both reports and delivers nothing. With{ "check": "report" }the valid e-invoice is delivered without the PDF/UA identifier.einvoice_display_mismatchcarriesaccessibilitytoo where the render setpdf.ua. - Changed:
post.watermark.textis allowed on an e-invoice. It answered400 invalid_requestwhile the watermark's font was not embedded. - Changed: the mark on a test e-invoice is text in the embedded font, as on every other test render. For a few days it was a picture.
- Editor: Accessible PDF (PDF/UA-1) and E-invoice no longer lock each other in the page setup, and a template that declares both is no longer reported as a problem.
- CLI:
formfeed validateandPOST /templates/{id}/validateno longer reportaccessibility-einvoicefor a template that declares both. In the CLI from 0.3.13, the first release with the check.
5 October 2026: accessible PDFs (PDF/UA-1)
- API:
settings.pdf.uamakes a render a tagged PDF in the PDF/UA-1 format. Formfeed repairs what the browser's tagged output gets wrong, checks every file against the machine-checkable rules of the standard and reports the verdict in the render's new fieldaccessibility: whether the file passed, and for each rule that failed what to change in the template and in which elements.trueis the strict check, which fails a render whose file does not pass with the new problempdfua_validation_failed;{ "check": "report" }delivers the file and reports. It is part of every plan and costs no extra unit. A passed check does not make a document accessible: Accessible PDFs says what it proves and what stays with the template. - Editor: the page setup has a Document section with the title, author, subject and keywords of the PDF, its bookmarks and tags, and Accessible PDF (PDF/UA-1). With that on, the previews check the page and the Problems list names what they find at its line, and a True render and the render log show the validator's verdict.
- API:
POST /templates/{id}/validatereports a missing title and an image written withoutaltfor a version that declarespdf.ua. - CLI:
formfeed validatereports the same for a template folder,formfeed renderprints the verdict, andformfeed devchecks the page it previews. The SDKs name the new field, and the MCP server hands it on. All of these come with the packages 0.3.13. - Changed: a tagged PDF (
pdf.tagged,pdf.outline) gets the same repairs whether or not it asks for PDF/UA: page headers, footers and decoration are marked as artifacts, and links carry a description. - Changed: a text watermark and the mark on test renders are set in Noto Sans, embedded in the file, where they were set in Helvetica. A text watermark now takes Greek and Cyrillic letters too.
5 October 2026: test e-invoices on the Free plan
- API: on the Free plan, test renders now make e-invoices: up to 20 a day per organisation, counted from midnight UTC, each a valid ZUGFeRD / Factur-X file with the test mark on its pages. Until now a Free workspace was refused even with a test key. A live render that makes one is still refused with
403 forbiddenand"feature": "einvoice". - Errors: a test render beyond the day's 20 answers the same
403 forbiddenwith"feature": "einvoice", now withlimit,usedandresets_at, the start of the next UTC day. In a batch every item that makes an e-invoice counts, and nothing is queued when they do not all fit. - Editor: on Free the E-invoice section of the page setup works as on a paid plan and says what a test render is. The true render of an invoice template is one of the day's 20, and so is Send test request in the API tab.
5 October 2026: packages 0.3.12
- CLI:
formfeed0.3.12 works with e-invoices offline.formfeed validateholds the_invoiceblock of every data set to the rules, for each template whosesettings.jsondeclares an e-invoice, and names the file, the line, the field and the rule. The newformfeed einvoice <slug>writes the XML a render would embed, without an API key.formfeed rendersends what the template declares and prints the report of the render. - Engine:
@formfeed/engine0.3.12 exports the e-invoice code the API runs before a render: the schema of the_invoiceblock (invoiceSchema), the check that names the rule each finding breaks (checkInvoice), the writer of the XML (invoiceToCii,einvoiceXml) and the check that a page shows what the XML states (displayCheck). The conversion to PDF/A-3 and the validation of the finished file happen in the API, not in the package. The package gainszodas a dependency. - Dev kit:
@formfeed/devkit0.3.12 exports what the CLI builds on:declaredEinvoice,diagnoseEinvoiceandlocalEinvoiceXml. - SDKs:
@formfeed/sdkand the Python packageformfeed0.3.12 knowpost.einvoiceon a render and a batch and theeinvoicereport on a render:EinvoiceOptions,EinvoiceandEinvoiceMessagein TypeScript,Einvoice,EinvoiceValidation,EinvoiceDisplayandEinvoiceMessagein Python. @formfeed/testing, the MCP server, the n8n node and the Activepieces piece move to 0.3.12 without changes of their own.
4 October 2026: e-invoices
- API: a PDF render can be an e-invoice in the ZUGFeRD / Factur-X format: a PDF/A-3b that carries the invoice as
factur-x.xml, in the profiles EN 16931 and Basic, from an HTML or a Word template or from HTML sent with the request. Ask for it withpost.einvoiceon a render or a batch, or declare it once in the template'ssettings.einvoice. The invoice is the_invoiceblock ofdata, and the template prints from the same block. - Validation: the block is held to the rules before anything renders, and the finished file is validated before it is delivered. A render that succeeds carries the report in the new
einvoicefield of the render, which webhook events repeat. Withxml: "both"the XML is also stored beside the PDF and linked aseinvoice.xml_url. A file that fails is not delivered, and no plain PDF is delivered in its place. - Errors: five new codes:
einvoice_data_invalid,einvoice_profile_unsupported,einvoice_display_mismatch,pdfa_conversion_failedandconformance_unavailable. - Plans: e-invoices need the Starter plan or higher and cost 1 unit on top of the render. Test keys, previews and failed renders cost nothing. On Free the API answers
403 forbiddenwith"feature": "einvoice". See plans and units. - Editor: the Page setup of a PDF template has an E-invoice section with the switch, the format and the profile, and a card that says what the data set's
_invoiceblock lacks, each finding with its rule. The preview counts the values of the XML it shows, and a true render and the render log show the validator's report, which downloads as JSON.
2 October 2026: pictures to choose from on the canvas
- Editor: on the canvas, an image layer that takes its picture from the file library named the file in a text field, so you had to know what it was called. File in the inspector now opens the pictures of the workspace's file library, newest first, with a search by name. The picture in use is marked, and Upload an image in the same list adds a file and shows it on the layer at once. A long file name is cut short in the field and shown in full when you point at it.
- Fixed: an image layer took a PDF, through its upload or when one was dropped on the canvas, because the library holds PDFs too. It was uploaded and then stood on the canvas as a broken image. An image layer now takes pictures only, and says which file is none.
- Fixed: the search of the Files page read
_and%in what you typed as wildcards, sohero_2xalso foundhero-2x.png. Both now stand for themselves.
2 October 2026: older versions, and pages that wait for their workspace
- Editor: the Versions panel showed the latest fifty versions of a template and had no way to the ones before, so a template saved more often could not compare with or restore its early versions in the app. Show older versions under the list brings them, fifty at a time, and the line above says
50+ savedwhile there are more. - Fixed: on a slow connection, pages said things about a workspace they did not know yet: the render log read "No renders yet", the keys and webhooks pages "ask an owner", and the billing page showed the Free plan as the current one, until the organisation had loaded. No page of a workspace is shown before its workspace is known now, and the render log no longer reads "No renders match the filters" while its first answer is on the way.
- Fixed: an address whose workspace does not exist (a workspace that was deleted, a mistyped link) showed an empty app. It leads to the organisation's first workspace, or to the list of organisations.
- Fixed: with Edit text on, a click on a text in the instant the preview took its next document was lost, or opened the text only to close it again. The text now opens in the new document without a second click.
2 October 2026: fixes in the editor and on the templates page
- Fixed: with Edit text on, a text clicked within a moment of the edit before it (Enter, then the next text) lost its cursor as soon as the preview showed the first edit, and what was typed into it was gone. The preview now waits with that render until the text is done.
- Fixed: on the canvas, a click into the inspector's text field in the instant a text opened for typing lost the focus to the canvas, and what was typed for the field landed in the text on the canvas. The focus now stays where you put it.
- Fixed: True render failed with "all render slots are busy" while the renderer was at its limit. It now waits for a free slot, for up to 20 seconds.
- Fixed: on a slow connection, the templates page read "No templates yet" until the list of a workspace had arrived, and Create template in the New template dialog did nothing when it was pressed before the workspace had loaded. The page now shows the list as loading, and the button is available once the workspace is there.
2 October 2026: the API panel of a template
- Editor: every template has an API panel: the request that renders it in curl, TypeScript, Python, PHP, Go, C#, Java and Ruby, with the template's slug and its data set as
data.json. The first line says which version a call renders, and a switch adds the latest draft to the request where one is ahead of the published version. See the editor tour. - Editor: owners and admins create a test key from the panel, shown once as the
exportline a shell needs. Send test request sends the request through the API as a test render and shows the answer: the file, the render in the render log and the JSON, or the problem with the page of its code. It is free on every plan. - App:
?panel=apion a template's address opens the panel, as do API request in a row's menu of the templates list and the last step of the getting-started checklist. After Use this template, the editor's notice leads there instead of to the example's page, since the panel names the copy's own slug. - API: the problem
POST /v1/rendersanswers with when a render fails now carries the render'srender_id, as the problem of its result already did. See Errors.
2 October 2026: a render by the template's id
- Fixed:
POST /v1/rendersandPOST /v1/renders/batchanswered404 template_not_foundwhentemplatewas thetpl_…id that the Templates API returns. They knew the slug and the plain UUID only. The slug and thetpl_…id now both work, as the reference has always said. - Fixed: a render named its template by the database's UUID in
template.id, in API answers and in webhook events, where the Templates API hands outtpl_…and these pages have always showntpl_…. Renders and events now carry thetpl_…id, so one template has one id. A UUID you stored keeps working wherever a template is named: a render request, thetemplatefilter ofGET /v1/rendersand the Templates API take the slug, thetpl_…id or the UUID.
2 October 2026: packages 0.3.11
- n8n node:
n8n-nodes-formfeed0.3.11 can be connected to the Tools input of n8n's AI Agent node, which then picks the operation and fills in its fields. A self-hosted n8n offers community nodes as tools only when the instance setsN8N_COMMUNITY_PACKAGES_ALLOW_TOOL_USAGE=true. See n8n. - CLI:
formfeed0.3.11 keeps a design of the visual editor with its template:templates pullwrites it todesign.json,pushsends it back, andvalidatewarns withdesign-stalewhentemplate.htmlorstyle.csswere edited after it, and withdesign-invalidwhen the file is not a design the app can open. See the project layout. - Engine:
@formfeed/engine0.3.11 renders locally what the API has rendered since the end of September. Text markeddata-ff-fit="shrink"is fitted to its box;barcodeprints the digits of an EAN-13, EAN-8 or UPC-A beside the long guard bars instead of through them; andqrcode,qrandbarcodeanswer an empty value with a message that names the helper ("nothing to encode, the value is empty or missing from the data") instead of the code library's own. The package also exports the design format (parseDesign,emitDesign). - Snapshots: because
formfeed testand@formfeed/testingcompare the document the engine assembles, a snapshot that holds one of those three barcodes or a fitted text changes once with 0.3.11. Look at the difference and approve it withformfeed test -u. - The SDKs, the MCP server and the Activepieces piece move to 0.3.11 without changes of their own.
2 October 2026: the canvas after its first testers
- Editor: a text added on the canvas opens for typing at once with its placeholder selected, a selected text takes what you type, and a click on it places the cursor. A round handle under the selection moves the layer, also while its text is being typed into.
- Editor: bold, italics, underline and the new strikethrough are toggles with the usual keys (Ctrl+B, Ctrl+I, Ctrl+U), the weights are listed by name, and a new text starts regular. Every layer has a Shadow in the inspector: behind the letters of a text, behind the box of any other layer.
- Editor: a picture on the clipboard pastes onto the canvas with Ctrl+V (it is uploaded to the file library first), and Upload your own fonts under the font list adds font files to the organisation without leaving the editor.
- Fixed: the add bar's image button closed the canvas with "The design cannot be opened". A new image layer now waits for its picture: the canvas and the Problems list say so, and it renders as an empty box until one is chosen.
- API: in
settings.design, a text layer'sstylemay carrystrike: true, and an image layer'ssourcemay hold an emptyasset,urlorpathfor a picture not chosen yet.
2 October 2026: start from an example, and the template limit
- App: the templates of formfeed.dev/examples are in the app. Templates → Examples lists them with a search and filters for group, kind and engine, and Use this template puts a copy into the workspace: the example's settings, translations and sample data, published as version 1, so a render that names the copy's slug works at once. Every example page of the site has the same button, which leads through sign-in to that example. See the quickstart.
- API: the number of templates a plan includes is now enforced: five on Free, no limit on Starter and above.
POST /v1/templatesbeyond it answers403 forbiddenwithfeature: "templates"and thelimit. Archived templates do not count, and restoring one is refused while the plan is full. Templates an organisation already has stay as they are, and renders are not affected.
30 September 2026: text edited in the preview, and a canvas for image templates
- Editor: with Edit text on, a click on a heading, a paragraph or a translated label in the flow or paged preview edits it where it is. Values from the data stay chips, the change goes into the template (or into the dictionary of the language the preview shows) as one edit that Ctrl+Z undoes, and text the preview cannot edit safely, such as control flow, is shown in the code instead. See the editor tour.
- Editor: image templates can be designed on a canvas, in beta: text, images, shapes, QR codes and barcodes as layers, text bound to fields of the data, brand colours and fonts, text that shrinks to fit its box, rulers and guides to snap to, and a check of every data set for text that does not fit. The editor writes the template's HTML and CSS from the design, so renders, the API and the SDKs see an ordinary template. See the visual editor.
- Templates:
data-ff-fit="shrink"gives the text of a fixed-size box the largest font size at which it fits, in any template: see fit text to its box. - API: a version's
settings.designholds the canvas's layers (settings); it may be at most 256 kB, and renders ignore it. - CLI: from the next release,
formfeed templates pullwrites a design todesign.json,pushsends it back, andformfeed validatewarns whentemplate.htmlorstyle.cssno longer match it.
29 September 2026: deduplication that knows languages and translations
- Fixed: two live renders of the same template and data in different languages (
"locale": "de", then"locale": "en") within the deduplication window answered the second with the first one's document, in the first language. The locale is now part of what makes two renders identical; renders without alocaleare deduplicated as before. - Fixed: a template version that changed only its translations had the same checksum as the version before it, so live renders with the same data could get the old wording from the deduplication cache for up to 24 hours after it was published, and
template_checksumcould not tell the two versions apart. Versions saved from now on include their translations in the checksum; the checksums of existing versions do not change.
28 September 2026: the result of a render
- API: the new
GET /v1/renders/{id}/resultanswers202withRetry-Afterwhile a render is queued or rendering. Once the render has finished, it answers what the synchronous request would have: the render, the file with?output=binary, or the render's error as a problem with itsrender_id. TheLocationof every202fromPOST /v1/rendersnow names this URL, absolute and withoutput=binarywhen the request asked for the file, and the202carriesRetry-After: 5.GET /v1/renders/{id}is unchanged; code that polls it, like the SDKs'waitFor, needs no change. See the automatic fallback. - Power Automate: because Power Automate follows that
Locationby itself, an action whose render outlives the one-minute sync deadline now waits for the document instead of returning an unfinished render.
25 September 2026: packages 0.3.10
- MCP server: the six tools of
@formfeed/mcp0.3.10 describe when to use each one rather than another, what a call costs and returns, and what goes wrong, and every parameter says what it is for.renderstates that it renders a template's published version, so a template that was never published fails withtemplate_not_found, and that test keys carry the watermark only on the Free plan. The hosted server atmcp.formfeed.devhas the new descriptions already. The SDKs, the CLI,@formfeed/engine,@formfeed/testing, the n8n node and the Activepieces piece move to 0.3.10 unchanged.
23 September 2026: packages 0.3.9
- SDKs:
@formfeed/sdkand the Python SDKformfeed0.3.9 carry thestorageadditions listed under bring your own storage. - CLI:
formfeed init --from-workspacealso pulls the organisation's shared partials its templates include, so the firstformfeed validateno longer fails with a missing partial, and that error now namesformfeed partials pullfor projects made before.formfeed templates diffcompares settings and sample data regardless of key order, so a fresh pull shows no changes. The output names files relative to where the command ran;--jsonkeeps absolute paths. - MCP server:
@formfeed/mcp0.3.9 declaresreadOnlyHint,destructiveHintandopenWorldHinton every tool, so clients can tell the four tools that only read fromrenderandconvert_to_pdf, which create a render. The hosted server atmcp.formfeed.devhas them already. - Activepieces: the connection text of
@formfeed/activepieces-piece0.3.9 says to keep a key's default scopes, because the connection check needsaccount:read, and to addfile:writeandwebhook:manage.@formfeed/engine,@formfeed/testingand the n8n node move to 0.3.9 unchanged.
23 September 2026: bring your own storage
- Bring your own storage (Pro plan and above): owners and admins connect an Amazon S3, Cloudflare R2 or S3-compatible bucket (Hetzner, IONOS, Scaleway, Wasabi, Backblaze B2, MinIO, Google Cloud Storage) on the new Your own storage card under Settings. Formfeed writes a test object before it saves the connection, then copies every document of the workspace into the bucket in the background: renders, batch items and zips, PDF tool results and office renders. It only ever writes (
PutObject), never reads, lists or deletes, and keeps its own copy for the workspace's retention, sodownload_urlworks as before. The copy goes wherever your bucket is; see Data residency. See Bring your own storage. - API: renders, batch items and the PDF tools,
/pdf/convertincluded, takestorage:{ "key": "invoices/2026/RE-1001.pdf" }names the object below the connection's prefix,falseleaves the document out. A render with its own key is never deduplicated. Renders and jobs have the new fieldstorage(status,bucket,key,url,reason,error); a synchronous render reportspending. The routes under/v1/workspaces/{id}/storageserve the app and take an owner's or admin's session; no API key can manage a connection yet. - Webhooks: the events
storage.deliveredandstorage.failed, opt-in: no endpoint receives them until its event list names them. Permanent errors such asAccessDeniedfail at once, other errors are retried on the webhook schedule, and five permanent failures in a row pause the connection and email the owners and admins. Redelivery sends failed copies again without rendering. - New error codes:
storage_not_configured(409) andstorage_test_failed(422). - SDKs:
storageon renders and PDF tools,Render.storageand the typesStorageOption,StorageDelivery,StorageDeliveryEventandWebhookEventTypein@formfeed/sdk; thestorage=keyword,render.storageand the modelsStorageDelivery,StorageDeliveryEventandStorageErrorin the Python SDK. With their next release.
23 September 2026: Zap templates
- Zapier: ready-made Zaps for Google Forms, Typeform, Google Sheets, Google Drive, Stripe and Slack are published in Zapier's directory. Starting from one puts the trigger and the Formfeed step in place, so only the two accounts and your own template are left to choose; they are also listed under Integrations in the app. See Zapier.
22 September 2026: packages 0.3.8
- Activepieces: the Formfeed piece is released with the other packages, as
@formfeed/activepieces-piece0.3.8 with provenance; install it by that name under Platform Admin → Setup → Pieces. See Activepieces. - MCP server:
@formfeed/mcp0.3.8 is listed in the official MCP registry asio.github.formfeed-dev/formfeed. The other packages move to 0.3.8 unchanged.
22 September 2026: Formfeed in Zapier's app directory
- Zapier: the Formfeed app is listed in Zapier's app directory; no invite link is needed any more. Its version 0.1.0 brings Convert Document to PDF, DOCX and PPTX output of Create Document and the Version field for release channels to Zapier. Existing Zaps stay on 0.0.0 until they are moved. See Zapier.
22 September 2026: a template schema for Power Automate
- API:
GET /templates/{id}/schematakesformat=swaggerand answers{ "schema": … }in the OpenAPI 2.0 dialect that Power Automate reads for dynamic fields: one type per property, nonullableoroneOf, a label inx-ms-summary. Likeformat=makeit describes a template without a stored schema by its sample data, and one with neither by an object without properties instead of a 404. - API:
POST /webhooksanswers201with aLocationheader, the absolute URL of the new endpoint. - API: the form fields
landscapeandsingle_page_sheetsof aPOST /pdf/convertupload accepttrueandfalsein any case, soTruefrom a .NET client works.
21 September 2026: charts sized for the page they print on
- Rendering: a PDF is laid out in a window as wide and as tall as its paper (A4: 794 × 1123 CSS pixels; landscape and custom sizes likewise) instead of a fixed 1240 × 1754. Printing always laid the page out at the paper's width, but scripts run before that and measured the wider window: a chart library that sizes itself from its container drew charts too large for the page, and an imported apitemplate.io report came out a page longer than it did there. Templates without scripts that measure the window print as before; a template whose script reads
window.innerWidthnow reads the paper's width.
21 September 2026: an editor that says when the PDF will shrink
- Editor: when an element is wider than the page, Problems says so — by how many millimetres, and to what size Chromium will shrink every page of the PDF to fit it — and jumps to the element's tag. Chromium scales the whole PDF down for one such element, which no preview at 100 % can show. The flow and paged previews measure after every layout.
- Editor in German: the template's problems (a name the sample data does not have, an unknown helper, a block that is never closed) are listed in German in the German app; syntax errors from the template parsers stay in English.
21 September 2026: snapshot tests see header and footer
- CLI: the snapshot
formfeed testcompares for an HTML template now holds the rendered header and footer as well as the document, under<!-- formfeed:header -->and<!-- formfeed:footer -->lines; before, a change toheader.htmlorfooter.htmlpassed unnoticed, because Chromium prints them outside the document.formfeed renders pull --snapshotwrites the same form. If your templates have a header or footer, the firstformfeed testafter the update fails once: runformfeed test -uand commit the snapshots. Informfeed0.3.7; the other packages move to 0.3.7 with it,@formfeed/enginewith theargsof its diagnostics and the previews' width measurement.
21 September 2026: no NaN on a document, and an MCP check that knows the schema
- Template engine:
money,numberandnumToWordsstop the render with atemplate_runtime_errorwhen a calculation handed themNaNor infinity — arithmetic on a value that is text ("19 %") or missing — instead of printingNaNon the document. A template that printedNaNbefore now fails where it did. Text that is not a number ("on request") and missing values print as before.POST /templates/{id}/validatereports the same error without rendering. - MCP server:
validate_templatewith a template slug asksPOST /templates/{id}/validatefor the version a render would use, so the data is checked against the template's stored schema and run once with the template; ad-hochtmlis still checked offline. In@formfeed/mcp0.3.6; the other packages move to 0.3.6 with it.
18 September 2026: two-factor authentication
- App: protect your account with a code from an authenticator app (TOTP): Two-factor authentication in the menu under your name. Optional, per person and in every plan; several authenticators can be added, so a second one can stand in for a lost phone. Every way of signing in asks for the code, the password reset included. See Two-factor authentication.
- API: unchanged for API keys, which never ask for a code. A session token of an account with two-factor authentication is accepted only once its session passed the code, and an agent's OAuth token on the hosted MCP endpoint only after the account allowed that agent from such a session; otherwise the API answers
403 forbiddenand says which of the two it is. Turning two-factor authentication on signs connected agents out, so they have to be connected again.
18 September 2026: pushes that conflict, and a CLI that shows its progress
- Fixed: creating a template version with a
base_checksumthat is no longer the latest version (formfeed templates pushafter the template changed in the app or from another machine) never answered; the request hung until the client gave up. It answers409 conflictat once, as documented. Pull and merge, or push with--force. - CLI 0.3.5:
templates pushandpull,partials push,files pushandpull,ci previewandrenderprint each result as it is done instead of all at the end, with a closing line that counts them. On a terminal a live line names the template in progress, what is happening to it and for how long, and says so when the API takes unusually long. Output is coloured on a terminal only (NO_COLORandFORCE_COLORare respected);--json, pipes and CI logs are unchanged. The other packages move to 0.3.5 with it.
17 September 2026: hosted MCP endpoint with sign-in
- MCP:
mcp.formfeed.devis live.https://mcp.formfeed.dev/mcp/<workspace id>signs in with your Formfeed account (OAuth 2.1; the client opens the browser and you allow it on the new consent page),https://mcp.formfeed.dev/mcptakes an API key. A signed-in agent reads templates and renders, and nothing else, whatever your role. See MCP server. - Packages 0.3.4:
@formfeed/mcp --httptakes--auth-serverand--public-originfor the same sign-in against your own Supabase Auth, and@formfeed/sdktakesheaderssent with every request; the other packages move to 0.3.4 with them.
17 September 2026: release channels everywhere a version is named
- API:
POST /templates/{id}/validatetakes a channel name asversion, andGET /templates/{id}/schemareads the schema of the version?version=names (published,latest, a number or a channel).?format=makeadds aversionselect when the template has named channels. - SDKs:
templates.schema(id, { version })in@formfeed/sdkandtemplates.schema(id, version=...)in Python;validatetakes a channel name. With their next release. - n8n, Zapier, Make: a Version field that lists the template's release channels. n8n also takes a version number as an expression; Zapier and Make show the field only for templates with named channels. Zapier and Make get it with the app's next version.
- App: the templates list has a Channels column with each template's named channels.
17 September 2026: parallel sync renders
- Fixed: synchronous renders sent in parallel beyond the free render slots answered
503 region_unavailableand stayed Queued or Rendering in the render log for good. They now wait for a slot, as documented, and one that finds none within 30 seconds becomes an async render (202,X-Render-Async-Fallback: true) that finishes in the background. See Rendering. - apitemplate.io compatibility: the endpoints wait for such a render and answer with the document (before, a render that went to the background answered
successwithout adownload_url), passRetry-Afteron, and answer504 render_timeoutwith thetransaction_refwhen a render runs longer than two minutes. See apitemplate.io compatibility.
17 September 2026: office templates in the validate endpoint
- API:
POST /templates/{id}/validatenow checks Word and PowerPoint templates too: the tags of the stored document, errors that only happen when it is filled with the data, and the data against the stored schema. Their diagnostics carry the new fieldspartandparagraph. - SDKs:
Diagnostichaspartandparagraphin@formfeed/sdkand the Python SDK, with their next release.
17 September 2026: converting to PDF in agents and no-code apps
- Packages 0.3.2:
@formfeed/mcpandn8n-nodes-formfeedwith the changes below; the other packages move to 0.3.2 with them. - MCP: the new tool
convert_to_pdfturns a Word, Excel, PowerPoint, OpenDocument or RTF document, or the render of a Word or PowerPoint template, into a PDF. The local server also reads a file by its path. See MCP server. - n8n: the resource PDF converts a document from a binary field or a render; Output offers DOCX and PPTX, and a downloaded file's name takes the extension of what was rendered. See n8n.
- Zapier: the action Convert Document to PDF, and DOCX and PPTX as outputs of Create Document. They reach Zapier with the app's next version. See Zapier.
- Make: the module Convert a document to PDF, and DOCX and PPTX as outputs of Create a document, with the app's next deploy. See Make.
- Navigation: Usage moved to the organisation section of the sidebar, next to Billing: units are counted for the whole organisation, and the page breaks them down by workspace.
- Packages 0.3.3:
@formfeed/enginewith theroundfix, and the CLI, dev kit and test helpers that render with it locally. - Fixed:
round(x, 0, 'floor')andround(x, 0, 'ceil')rounded to the nearest value; they now round down and up as in Jinja2, also asmethod='floor', in all three template languages. The helper reference now says thatnumberwithout decimals keeps the number's own decimals, at most three;number(0)gives whole numbers.
17 September 2026: packages 0.3.1
- Packages 0.3.1:
@formfeed/sdk,formfeed(Python),formfeed(CLI),@formfeed/engine,@formfeed/devkit,@formfeed/testing,@formfeed/mcpandn8n-nodes-formfeedwith the fixes below. - Word templates: a tag inside a field code (
HYPERLINK,REF, …) stays as typed, and the Tags panel,formfeed validateand the MCP server now warn about it (office-tag-in-field). - CLI:
ignoreinformfeed.jsonnow leaves matching template folders out offormfeed templates pushwithout a slug (it was read but had no effect);formfeed renders pullworks for renders of Word and PowerPoint templates; the true render offormfeed devuses an image template's own format instead of always PNG. See Configuration. - MCP:
list_templatesfilters bydocxandpptx, andvalidate_templatechecks a Word or PowerPoint template's document, naming part and paragraph. See MCP server. - Fixed:
formfeed render,formfeed ci previewand the true render offormfeed devprinted the header and footer templates unfilled ({{ company.name }}); they now send them filled with the same data as the body. - Jinja2:
sum(attribute='total')andsum(attribute=…, start=…)add up the attribute (they returned 0), and{{ values }}prints a list or dict as Python does ([1, 2],{'a': 1}) instead of1,2, so chart scripts can take"data": {{ values }}. - apitemplate.io import:
<script>and<style>blocks of the CSS field that use template syntax go to the end of the template instead of the Head tab, because the head is not filled with data. See Migrating from apitemplate.io.
17 September 2026: packages 0.3.0
- Packages 0.3.0:
@formfeed/sdk,formfeed(Python),formfeed(CLI),@formfeed/engine,@formfeed/devkit,@formfeed/testing,@formfeed/mcpandn8n-nodes-formfeedwith everything from 14 to 16 September: Word and PowerPoint templates and office-to-PDF conversion in the SDKs and the CLI, the apitemplate.io import with an API key,formfeed workspaces delete, andoutputleft to the template. - Templates list: the name column has copy buttons for the slug and the template ID, the two ways an API call names a template, so you no longer need the row menu or the editor for them.
- Flow preview: header and footer stand in the page margins, measured from the page's edges as Chromium prints them, instead of on top of the content. The flow page now matches the paged preview.
- Change password: the user menu has a Change password entry.
- Fixed: the editor's paged and flow previews left page numbers empty (
Page of) in headers and footers; they now show them as the PDF does. - Fixed: the spreadsheet batch dialog showed Output and File name from column empty until you picked a value; they show As the template and row number.
16 September 2026: Word and PowerPoint templates
- Word and PowerPoint templates: a template can be a
.docxor.pptxfile with tags such as{{ customer.name }}in its text, in all three template languages. In Word,qrcode,barcode,epcQr,imageandpageBreakplace pictures and page breaks; on slides, pictures become picture shapes.chart,markdownand includes are refused withunsupported_in_office(422). See Word and PowerPoint templates. - In the app: the create dialog offers a Word template from an example file or an upload, and a PowerPoint template from an example presentation or an upload. The template page shows the file, the tags of each part with their problems, data sets, settings and versions, a quick preview filled in the browser, and the true render.
- API: the template kinds
docxandpptx.POST /templatesand version create take the document asmultipart/form-data; a version without a file keeps the latest document.GET /templates/{id}/versions/{which}/filedownloads it. Renders of these templates produceoutput: "docx"or"pptx", or"pdf", andsettings.office.outputsets the default when a request names none. - Plans: live renders of Word and PowerPoint templates start with Starter. On the Free plan, test renders produce the marked PDF only, and asking for
docxorpptxanswerstest_output_unavailable(403). - Office to PDF:
POST /pdf/convertconverts a Word, Excel, PowerPoint, OpenDocument or RTF upload of up to 20 MB, which is not kept, or the output of a Word or PowerPoint template render, withpage_ranges,landscapeandsingle_page_sheets. HTML is accepted too, because report exports often save it under a.docname; linked images and stylesheets stay empty. Starter plan and above, counted like a PDF render. In the app: Office to PDF on the Renders page. See PDF tools. - New error codes:
file_type_unsupported(415),office_document_invalid,office_document_too_largeandoffice_conversion_failed(422),office_unavailable(503, withRetry-After).file_too_largeandsource_encryptedalso cover template files and conversion uploads. - SDKs:
docxandpptxin the output formats and template kinds;templates.create()andtemplates.versions.create()(Python:create_version()) with afile;templates.versions.file()(Python:version_file());pdf.convert()for an upload or a render. See TypeScript and Python. - CLI: a template folder can hold
template.docxortemplate.pptxinstead of the HTML files.templates pullwrites the pulled version's document andpushuploads it only when it changed;validateanddevlist the findings by part and paragraph;render,ci previewanddevfill the document locally and convert it through the API for PDF;testcompares the filled XML with.xmlsnapshots.formfeed pdf convert <file | render id>converts any accepted office file. QR codes, barcodes and SVG pictures are drawn by the optional@resvg/resvg-js. See Project layout. - apitemplate.io import with an API key: Templates → Import → apitemplate.io → With API key lists every template of the account with its group, and reads and checks the picked PDF templates in one go;
formfeed import apitemplate --all,--group <name>or--template <id…>does the same with--key(orAPITEMPLATE_API_KEY) and--source-region. The JSON of apitemplate.io's editor becomes the draft'sdefaultdata set, margins without a unit are read as pixels (also by the compatibility endpoint), header and footer keep the size apitemplate.io draws them in,<link>and<script>tags of the CSS field move to the head, andcustom_header,custom_footerand the text slots become header and footer. See Migrate from apitemplate.io. - Archive and delete in bulk: select templates in the list and archive them, with undo; in the archived view, restore them or, as owner or admin, delete them permanently after a typed confirmation.
- Header and footer padding:
settings.header.paddingandsettings.footer.padding(default0 10mm) replace the side padding of the box around Chromium's header and footer, so designs can run edge to edge; the settings panel edits them. See Settings. - Previews like the PDF: the flow and paged previews draw header and footer across the whole page width, the paged preview puts the header at the top edge and the footer at the bottom edge, and template elements in the paged preview keep their own box sizing, as in the PDF.
- Zoom in the previews: Ctrl+wheel, pinch and Ctrl+plus, minus and 0 zoom the flow and paged previews and the Word quick preview, which also has zoom buttons and Fit.
- Billing email: the organisation's Billing email is the address Stripe sends invoices to (else the first owner's), in both directions: a change in the app reaches Stripe, and a change in the billing portal comes back. Quota and billing mails go to it as well. Checkout asks for the full billing address.
- Refunds: invoices on the Billing page show Refunded or Partly refunded.
15 September 2026: deleting workspaces
- Delete a workspace in the app: owners and admins delete a workspace under Settings → Workspaces after typing its slug. It disappears at once, its API keys stop working, and its templates, renders, outputs and files are removed after 30 days. The slug is free again right away. The last workspace of an organisation stays.
- Through the API:
DELETE /workspaces/{id}deletes any workspace of the key's organisation, for example per-branch preview workspaces a CI job cleans up.formfeed workspaces delete <id> --yesin the CLI,workspaces.delete()in TypeScript and Python. - New scope:
workspace:delete, not in new keys by default and never in CLI login keys. See Scopes. - New error code:
last_workspace(409).
15 September 2026: key scopes and default output
- A write scope includes its read scope:
template:writeaddstemplate:read,file:writeaddsfile:read, andrender:createaddsrender:read, because writing answers with the object and async renders are polled. Existing keys were completed. The key dialog shows each included scope under its write scope, ticked and locked. See Scopes. - File library scopes in the app: the create-key dialog offers
file:readandfile:write, so keys made in the app can upload files; the Zapier and Make upload actions failed with 403 before. - The template decides the output: a render without
outputproduces a PDF for PDF templates and the template'ssettings.image.format(PNG without one) for image templates; before, image templates rendered a PDF.POST /renders/batchtakes a top-leveloutputfor its items, and the spreadsheet batch dialog defaults to As the template; before, it rendered every batch as PDF. The CLI, MCP, n8n, Zapier and Make leaveoutputout unless you choose one. See Rendering.
14 September 2026: building blocks in the editor
- Blocks panel: the editor's Blocks button opens a panel beside the code with search, a live preview of each block and every block as a palette command. Inserting a block adds its sample data, only the CSS rules your stylesheet lacks, and its labels in English and German to the Translations panel; footer blocks go into the Footer tab. See Building blocks.
- New blocks: letterhead and legal footer from the brand kit, window envelope address (DIN 5008), reference block, invoice heading with service period, payment terms and bank details, small business VAT note (§ 19 UStG), reverse charge note, keep together, page break and key figures. The existing blocks read the brand kit's colours and label their headings with
t. - Charts from plain data:
chartdraws fromlabelswithvalues, or severalserieswithlabel,valuesandcolor, so an integration sends numbers instead of a Chart.js configuration. Series and slices without a colour take a fixed palette, and one unnamed series shows no legend. A fulldataandoptionsconfiguration still works. The chart block uses it with the brand kit's primary colour. - Organisation partials appear in the panel, are inserted as an include and preview with the template's data.
- Fixed: the invoice table block in Handlebars templates rendered empty cells.
- Fixed: the Partials page and the editor's completion wrote Liquid includes as
{% render "name" %}, which gives the partial onlybrandand not the template's data. Both now write{% include "name" %}, and the documentation says so. Liquid templates that already userenderfor a shared partial should switch toincludewhen the partial reads template data.
13 September 2026: local webhooks and reproducible renders
- Keep render requests: a new workspace setting, off by default. With 24 hours (Starter and above) or 7 days (Pro and above), the request of every render, sync and async, is kept after the render completed; the plan caps the choice. Before, async requests were kept for 24 hours on every plan and sync requests not at all. Post-processing passwords are never kept, so a password-protected render is not re-rendered from the render log. See Data residency.
- Reproduce a failed render:
GET /renders/{id}/inputreturns a render's kept request (template and version, data, locale, options), andformfeed renders pull <render id>writes its data into the project as a data set, with--runto render it locally and show the error's line and column,--snapshotto turn a good render into a test, and--redactto replace personal data while keeping the shape. Every read is audited. See Reproduce a failed render. - Develop webhooks locally:
formfeed listen --forward-to http://localhost:3000/webhooksforwards the workspace's webhook events to a local server over a WebSocket the CLI opens, signed with a secret that stays the same for an API key across sessions, with the normal retries and delivery log. Test events only unless--live; three sessions per workspace.formfeed trigger render.completed --template <slug>makes a real test render to get a genuine event. See Develop webhooks locally. - Resend events:
POST /webhooks/events/{id}/resendsends a stored event again to a registered endpoint or a listen session, including events that were delivered;formfeed webhooks resend <evt_id>in the CLI. - Endpoint environments: webhook endpoints take
environments(live,testor both, the default), so a staging receiver no longer sees production renders. See Test and live events. - New scopes:
render:inputandwebhook:listen, not in new keys by default;formfeed logingives them to editors, admins and owners. See Scopes. - New error codes:
render_input_expired(410),listen_session_limit(409) andlisten_session_ended(410). - SDKs:
renders.input(),webhooks.resend()andwebhooks.listen.start()/end()in TypeScript and Python; endpoints takeenvironments.
13 September 2026: release channels and typed templates
- Release channels: named pointers from a template to a version, such as
staging, besidepublished. Renders select one with"version": "staging"(live and test keys), and the render'stemplategainschannelandcanary, in the API and in webhooks. Move, promote, roll back and delete channels in the editor's Versions panel, withPUT /templates/{id}/channels/{name}and itspromoteandrollbackendpoints, or with the CLI. Canary releases send 1 to 50 percent of a channel's renders to a second version, chosen by theIdempotency-Key. Moves reach renders within a minute. Starter has 2 channels per template, Pro and Business 10 and canaries. See Release channels. - Schema check on publish: publishing, moving a channel, promoting and rolling back compare the data schema of the version going live with the one callers use now. When both store a schema, a breaking change (a new required field, a narrower type, a removed enum value) answers
409 schema_breaking_changeunless the request sendsallow_breaking: true; with inferred schemas the result is a warning. Publish and channel responses carryschema_check. The editor publishes through the API now and lists breaking changes before publishing, with Publish anyway. - Store as contract: the editor's schema section infers one schema over all data sets (a field only some sets contain is optional) and stores it with the next version, with a required switch per field.
- Typed template data:
formfeed typesgenerates TypeScript (formfeed.d.ts, augmenting@formfeed/sdk) or Python (formfeed_templates.py, aTypedDictper template andtyped(client)) from stored or inferred schemas, andformfeed types --checkfails CI when the file is out of date. In the TypeScript SDK,renders.createandrenders.batchcheckdatafor the templates the file knows. See Typed template data. - New error codes:
channel_not_found(404),channel_name_invalid(422),channel_in_use(409) andschema_breaking_change(409). A named channel on the Free plan, or a canary below Pro, answers403 forbiddenwithfeature: "channels"orfeature: "canary". - CLI:
formfeed types,formfeed channels list|set|promote|rollback|delete,templates push --channel <name>and--allow-breaking;formfeed.jsontakestypesdefaults. - SDKs:
templates.channels.list(),set(),promote(),rollback()anddelete(),allowBreaking/allow_breakingwhen publishing, and channel names inversions.get/version(), in TypeScript and Python. The TypeScript SDK exportsFormfeedTemplates,TemplateData,TypedRenderRequest,Channel,ChannelListandSchemaCheck.
13 September 2026
- Brand kit: one per organisation, on every plan. Set the company name, legal footer, colour tokens, heading and body fonts, base size, three logos and the paper and margins new templates start with on the new Brand page. Templates read it as
brand({{ brand.logo.primary }},{{ brand.legal_footer }}) and as CSS variables (--brand-color-primary,--brand-font-heading); logos also work in headers and footers. Changes reach published templates without republishing.GET /brandreads it. - Shared partials: letterheads, footers and other fragments for the whole organisation on the new Partials page, included by name like template partials and resolved after them. Editing one shows the templates that change. API:
GET,PUTandDELETE /partials/{name}withbase_versionconflict detection; up to 200 per organisation. See Shared partials. - Recorded per render: every render records the brand kit version and the shared partial versions it used, and deduplication only reuses a result when they are unchanged.
- Fixed: True render in the editor now includes organisation fonts; it sent none before, so a template using fonts uploaded under Fonts printed with fallback fonts there, unlike in API renders.
- CLI:
formfeed brand pullstores the kit in.formfeed/brand.jsonfordev,render,testandci preview;formfeed partials list|pull|pushsyncs shared partials with the project'spartials/folder, with--engine,--dry-runand--force, and exit code 2 on a conflict.formfeed templates pushleaves shared partials out of the version. - SDKs:
brand.get()andpartials.list(),get(),put()anddelete()in TypeScript and Python. - Server-side validation:
POST /templates/{id}/validatechecks a version with data without rendering: syntax, header and footer, missing partials, runtime errors and the data against the stored schema. Free. validate_data: trueon renders and batch items checks the data against the template's stored schema first and answers422 data_validation_errorwith anerrorslist, before anything is rendered or counted. Diagnostics gainpathfor data errors.- No documented endpoint or option answers
501 not_implementedany more. - Jinja2 compatibility:
.format()understands Python format specs ("{:,.2f}", nested fields);namespace()and{% set ns.total = … %}work;truncate,map(attribute=…),selectattrandrejectattrtake their Jinja2 arguments. The editor no longer warns about macro arguments,Trueor a loop variable name used by two loops. - Fixed: templates imported from apitemplate.io with a
namespace()counter failed with "Cannot read properties of undefined", and the visual editor's hidden loop wrappers rendered as empty table rows. - Fixed: saving or importing a template that contains a
<script>tag failed in the app with "Http failure response … 0 undefined". The app now saves versions through the API;POST /templates/{id}/versionsacceptsdata_sets. - Packages 0.2.0:
@formfeed/sdk,formfeed(Python),formfeed(CLI),@formfeed/engine,@formfeed/devkit,@formfeed/testing,@formfeed/mcpandn8n-nodes-formfeedwith everything above and from 12 September.
12 September 2026
- File library: upload images and PDFs with
POST /files, list, read and delete them, and reference them by name withasset('logo.png'); relative image paths resolve against the library too. New scopesfile:readandfile:write;file:deletenow also covers library files. New errorfile_too_large(413). - PDF post-processing: image watermarks (
watermark.image, a PNG or JPEG of the library) and library PDFs as merge sources (merge_after,/pdf/merge), by name orfil_…id. - WebP output:
"output": "webp", withimage.quality(100 is lossless). - SDKs:
client.filesin TypeScript and Python; watermarks takeimage, merges take library files. - CLI:
formfeed files list|push|pull|deletewith afiles/folder in the project,formfeed devserves that folder soasset()works offline, andrenderandci previewresolveasset()against the workspace library.render --output webp. - Integrations: Upload File in n8n and Zapier; Upload a File, List Files and Delete a File in Make; WebP as an output everywhere.
- Fixed: deleted and expired outputs are now removed from storage; the purge had been refused a database permission since 11 September.
Private beta, September 2026
The first version available to beta workspaces.
- API: sync and async renders from templates, HTML and URLs; batches of up to 1,000 renders with an optional zip; webhooks with signatures and retries; idempotency keys, deduplication and the sync-to-async fallback; templates and versions; usage; the apitemplate.io compatibility endpoints.
- PDF post-processing: merge PDFs, password protection (AES-256) and text watermarks, as a render's
postblock or through/pdf/merge,/pdf/protect,/pdf/watermark, plus the free/pdf/info. Starter plan and higher. - Templates: Jinja2-compatible, Liquid and Handlebars with one set of helpers, including QR codes, barcodes, SEPA payment codes, charts and Tailwind.
- Tools: the web editor with autocomplete and paged preview; the
formfeedCLI with local preview, validation, snapshot tests and CI previews; importers for apitemplate.io, PDFMonkey and jsreport. - Packages:
@formfeed/sdk,formfeed(Python),@formfeed/engine,@formfeed/testing,@formfeed/devkit,@formfeed/mcp,n8n-nodes-formfeed. - Region: EU (Frankfurt).