formfeed
OfficialServer provides Formfeed API to manage and render documents from templates: list templates, inspect expected data schemas, validate templates, render PDFs/images/office docs, convert office files to PDF, and track render results.
List and filter templates by name, tag, kind, or engine, including published version status.
Get the JSON Schema and sample data describing what data a template expects.
Validate a stored template or ad-hoc HTML with data, returning diagnostics before rendering.
Render documents from stored templates or ad-hoc HTML to PDF, image formats (PNG/JPG/WEBP), DOCX, or PPTX, with configurable data, locale, output, filename, and wait behavior.
Convert existing office/HTML documents (up to 20 MB) to PDF, with options like page ranges, landscape, and single-page spreadsheet sheets.
Fetch render status and fresh download links by render ID, including page count, units, and errors.
Formfeed packages
Client libraries, command line tools and integrations for Formfeed, the API for generating PDFs and images from templates. Documentation lives at docs.formfeed.dev.
Package | Registry | What it is |
TypeScript client: renders, batches, templates, webhooks. No dependencies, Web APIs only | ||
Python client, sync and async | ||
CLI: templates as files, offline preview and validation, renders through the API | ||
The template engine (Jinja2, Liquid, Handlebars) the API and the editor use | ||
Template folders, project config and local rendering | ||
Vitest and Jest matchers for templates | ||
Model Context Protocol server for AI agents | ||
n8n community node |
Every npm package is built and published from this repository by GitHub Actions with provenance, and the Python package with trusted publishing, so each release can be traced to the commit and workflow run it came from.
Development
Node 22 or newer and pnpm (the version in package.json, corepack enable picks it up):
pnpm install
pnpm nx run-many -t lint test --exclude sdk-python # TypeScript packages
pnpm nx run-many -t bundle smoke # build, pack and try every npm package
cd packages/sdk-python && pip install pytest httpx pydantic && python -m pytest -qThis repository is generated from Formfeed's main repository, where the packages are developed together with the API they talk to. See CONTRIBUTING.md for how issues and pull requests are handled.
Related MCP server: docjet-mcp
Licence
MIT, see LICENSE. Bundled third-party code keeps its own licence; the CLI and MCP packages
list it in THIRD_PARTY_LICENSES.md.
Available Tools
10 toolscheck_invoiceCheck an e-invoiceARead-only
Checks the _invoice block of an e-invoice offline, as the API does before it renders one: the fields EN 16931 asks for, the totals to the cent, VAT categories and code lists, and France's rules for an invoice between two French businesses. Free, no units, nothing sent. Returns ok and problems, each with the field, the rule (BR-…) where one applies and a message. Nothing is computed for you: change the values a problem names, check again, then call render with einvoice and the block as data._invoice. The validator that runs during the render holds the file to the full rule set and to PDF/A-3b as well.
| Name | Required | Description | Default |
|---|---|---|---|
| invoice | Yes | The _invoice block as it goes into data._invoice: number, issue_date, currency, seller, buyer, lines, tax.breakdown, totals and payment, as https://docs.formfeed.dev/templates/e-invoices#invoice-block describes | |
| profile | No | The profile the render will ask for; en16931 when left out. basic cannot carry a rounding amount and is refused between two French businesses |
Output Schema
| Name | Required | Description |
|---|---|---|
| ok | Yes | |
| problems | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well past readOnlyHint/destructiveHint by disclosing cost ('free, no units'), that nothing is transmitted ('nothing sent'), that nothing is auto-fixed ('nothing is computed for you'), the return shape (ok plus problems carrying field, BR-… rule and message), and the stricter validation that awaits at render. These are exactly the traits an agent needs to plan a retry loop.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads what it validates, then the cost/offline guarantees, then the problem-then-render workflow, then the caveat about the render validator. Dense but every clause carries information an agent would otherwise have to infer.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-parameter validator with an output schema, the description covers purpose, cost, iteration loop, and the boundary against render-time validation. Nothing an agent needs in order to call it correctly and interpret the result is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the param details (block contents, profile enum, en16931 default, basic restrictions) are already carried by the schema. The description adds the tie that matters: the same block is what gets passed to render as data._invoice, which clarifies identity and reuse rather than restating the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource (checks the _invoice block) plus the scope: offline, mirroring the API's pre-render validation, covering EN 16931 fields, totals, VAT categories, code lists and French domestic rules. It is clearly separable from the render sibling, which it names and contrasts with the stricter full rule set plus PDF/A-3b.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly positions the tool before render in a workflow: validate, change the values a problem names, check again, then call render with einvoice. It also names the condition that distinguishes its checks from the render-time validator, so the agent knows when this suffices and when it does not.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
convert_to_pdfConvert an office document to PDFA
Converts an existing Word, Excel, PowerPoint, OpenDocument, RTF or HTML document (up to 20 MB) to a PDF as it is, without filling in data, and returns the render with status and download_url. To make a document from a template and data, use render instead. Pass exactly one source: render_id, the render of a Word or PowerPoint template (output docx or pptx); path, a file on this machine; or file_base64 with file_name. Every call is a new render that counts units like a PDF; the Free plan cannot convert (Starter and above). The document is converted and not kept.
| Name | Required | Description | Default |
|---|---|---|---|
| path | No | Absolute path of the document on this machine | |
| filename | No | Name of the PDF behind download_url, e.g. report.pdf | |
| file_name | No | Name of the document, e.g. report.xlsx; the type is read from the content | |
| landscape | No | Landscape for spreadsheets and documents without their own page setup | |
| render_id | No | rnd_ id of a render whose output is docx or pptx, to get that document as a PDF | |
| file_base64 | No | The document to convert, base64 encoded; needs file_name | |
| page_ranges | No | Pages to convert, e.g. 1-3,5; all pages when left out | |
| single_page_sheets | No | Each spreadsheet sheet on one page |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | rnd_ id of the render |
| pdfa | No | The PDF/A verdict (PDF/A-2b or PDF/A-3b), when the template declares it |
| error | Yes | Why a failed render failed |
| units | Yes | Units the render counted |
| status | Yes | |
| einvoice | No | The e-invoice report, when the render made one |
| template | Yes | The template and version rendered; null for html and conversions |
| expires_at | Yes | When download_url stops working; get_render issues a fresh one |
| page_count | Yes | |
| environment | Yes | |
| download_url | Yes | Signed link to the file; null until the render succeeded |
| accessibility | No | The PDF/UA-1 verdict, when the template declares it |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond annotations: discloses unit consumption ('Every call is a new render that counts units like a PDF'), plan gating, the fact that the source is not retained, the 20 MB cap, and that the response carries status/download_url. Annotations only say readOnly=false, idempotent=false, destructive=false; the description adds the material cost and retention behavior. Minor gap: no mention of failure/error behavior or how long download_url stays valid.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads what it does, then the alternative, then the input rule, then the cost/limits. Dense and nearly every clause carries information, though the second and third sentences are long run-ons that could be split for scanning.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For an 8-parameter, zero-required conversion tool, the description covers inputs, exclusivity rule, size limit, plan eligibility, cost model, retention, and the sibling alternative; return values are covered by the existing output schema. Nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3, but the description adds meaning the schema lacks: the exactly-one-source constraint (required list is empty and there is no oneOf), and clarification that render_id must be a docx/pptx render. It does not explain landscape, page_ranges, single_page_sheets or filename beyond the schema, but those are already well documented.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (converts) and enumerates the exact resource types accepted (Word, Excel, PowerPoint, OpenDocument, RTF, HTML) plus size limit, and explicitly contrasts with the sibling 'render' for the template+data case. An agent can distinguish this from render without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Names the alternative tool and the condition that selects it ('To make a document from a template and data, use render instead'), and gives an explicit rule for mutually exclusive inputs ('Pass exactly one source') with the three valid variants. It also flags a hard eligibility constraint (Free plan cannot convert).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_renderGet a renderARead-only
Returns the current state of a render: status (queued, rendering, succeeded or failed), download_url with expires_at, page_count, units and error, and the picture of a finished image render up to 2 MB. Use it to follow a render started with wait false, or to get a fresh link once the old one has expired; render and convert_to_pdf already return this for the render they just made. Read-only and free; an unknown id returns an error.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | rnd_ id of the render, as render or convert_to_pdf returned it |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | rnd_ id of the render |
| pdfa | No | The PDF/A verdict (PDF/A-2b or PDF/A-3b), when the template declares it |
| error | Yes | Why a failed render failed |
| units | Yes | Units the render counted |
| status | Yes | |
| einvoice | No | The e-invoice report, when the render made one |
| template | Yes | The template and version rendered; null for html and conversions |
| expires_at | Yes | When download_url stops working; get_render issues a fresh one |
| page_count | Yes | |
| environment | Yes | |
| download_url | Yes | Signed link to the file; null until the render succeeded |
| accessibility | No | The PDF/UA-1 verdict, when the template declares it |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well beyond the readOnly/openWorld annotations: it discloses cost ('free'), the failure mode for an unknown id ('returns an error'), the 2 MB cap on image payloads, and the fact that download_url is time-limited via expires_at. That is exactly the operational context an agent needs before polling.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two dense sentences with no waste: what comes back first, then when to call it, then the cost/error caveats. Every clause (2 MB cap, expires_at, wait false) maps to a real agent decision.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, yet the description still flags the practically important pieces — the status enum and the expiring link — and adds polling guidance and error behavior. For a one-parameter read tool, an agent has everything needed to call it correctly on the first try.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the single id parameter is fully documented in the schema, including the rnd_ prefix convention, so the description adds little. The 'unknown id returns an error' clause is the only extra semantic value. Baseline 3 applies when the schema carries the parameter burden.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Returns the current state of a render') and enumerates the concrete fields returned (status values, download_url/expires_at, page_count, units, error, image preview cap). It also explicitly distinguishes itself from the siblings render and convert_to_pdf, which already return this payload for their own renders.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives two explicit when-to-use triggers ('follow a render started with wait false' and 'get a fresh link once the old one has expired') and an explicit when-not-to-use case ('render and convert_to_pdf already return this'). Nothing about tool selection is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_template_schemaGet the data schema of a templateARead-only
Returns the JSON Schema of the data object a template expects and the sample data it was designed with, from its published version, else its latest draft. Call it after list_templates and before render or validate_template, to build data that fits; ad-hoc html has no stored schema to read. When the template stores no schema, one is inferred from the sample data: treat that as a guide, not a contract. Read-only and free; an unknown slug returns an error.
| Name | Required | Description | Default |
|---|---|---|---|
| template | Yes | Slug (e.g. invoice) or tpl_ id, as list_templates returns them |
Output Schema
| Name | Required | Description |
|---|---|---|
| schema | Yes | JSON Schema of data; inferred from sample_data when none is stored |
| status | Yes | published, or draft when nothing is published yet |
| version | Yes | The version the schema comes from |
| template | Yes | |
| sample_data | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnly annotations it discloses the published-vs-draft fallback, that an inferred schema from sample data is a guide rather than a contract, that the call is free, and that an unknown slug errors. These are behaviors an agent cannot get from the structured fields.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three tight sentences: what is returned, when to call it, and the edge-case caveat. The return value is front-loaded and no sentence is filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return shapes need not be spelled out, and the description covers the remaining ambiguities: version resolution, inference fallback quality, and error behavior. Nothing needed to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the sole `template` parameter already documents slug-or-tpl_ id format. The description adds no parameter-level detail beyond naming the slug context implicitly, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: returns the JSON Schema of a template's `data` object plus its sample data. It also pins the resolution rule (published version, else latest draft), which no sibling tool does.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit sequencing ('after list_templates and before render or validate_template') and a clear exclusion ('ad-hoc html has no stored schema to read'). The failure case for an unknown slug is also named.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_template_sourceRead the source of a templateARead-only
Returns what a template version is made of: the body markup (html), css, head, settings (paper, margins, header, footer, fonts), sample data, data sets, data schema, translations and partials, with the version's checksum. Call it before save_template to change a template the user named, and save what you edited with that checksum as base_checksum. Reads the latest version by default, the newest draft a change builds on; published reads what renders use. Word and PowerPoint templates are documents: for them it returns the settings and data, not the document. Read-only and free.
| Name | Required | Description | Default |
|---|---|---|---|
| version | No | latest (default), published, a version number or a channel name | |
| template | Yes | Slug (e.g. invoice) or tpl_ id, as list_templates returns them |
Output Schema
| Name | Required | Description |
|---|---|---|
| css | Yes | |
| head | Yes | |
| html | Yes | |
| i18n | Yes | |
| version | Yes | |
| partials | Yes | |
| settings | Yes | |
| template | Yes | |
| data_sets | Yes | |
| data_schema | Yes | |
| sample_data | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnlyHint, destructiveHint, and openWorldHint annotations, it discloses important behavior: the full set of returned components, checksum usage for subsequent saves, version resolution semantics, and the Word/PowerPoint limitation. It also notes that the operation is read-only and free.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but well-structured and front-loaded with what is returned. Every sentence contributes operational information: return contents, save workflow, version semantics, and the document-format caveat.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the presence of an output schema, full schema description coverage, and clear read-only annotations, the description is complete enough for an agent to invoke the tool correctly. It covers return scope, version behavior, downstream save workflow, and an important edge case for Word and PowerPoint templates.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema fully documents both parameters, so the baseline is 3, but the description adds meaningful semantics for the version parameter: what latest means, what published means, and how draft/build versions are selected. It does not further explain the template slug or tpl_ id beyond the schema, but the added version behavior is valuable.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific resource and verb: it returns the source composition of a template version, enumerating body markup, css, head, settings, data, translations, partials, and checksum. This is specific enough to distinguish it from siblings like get_template_schema and list_templates.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives clear workflow guidance: call before save_template when editing a user-named template and pass the returned checksum as base_checksum. It also explains version selection behavior for latest, published, version numbers, and channels, though it does not explicitly name alternative read tools such as get_template_schema.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_workspaceWhich workspace this server works inARead-only
Returns the workspace and organisation this server acts in, the plan, the environment (live or test) and the units of the current period: included, used and the balance of prepaid units. Call it when the user asks where a document will land or how many renders are left, or when list_templates returns nothing you expected. A signed-in agent works in the workspace chosen when it was connected, which the person changes in Formfeed under Account → Connected agents; an API key works in the workspace it was made for. Read-only and free.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| plan | Yes | |
| units | Yes | |
| period | Yes | The current billing period |
| workspace | Yes | |
| environment | No | live renders count units; test renders are free |
| organization | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint and destructiveHint=false, so 'Read-only and free' is partly redundant, but 'free' is new. The genuinely additive content is the scoping model: a signed-in agent sees the workspace chosen at connect time (changeable under Account → Connected agents) while an API key is pinned to the workspace it was created for. That explains why the answer might differ from user expectation, which annotations never convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The payload is front-loaded in the first sentence (what is returned), then triggers, then the scoping caveat. Four sentences with little waste, though 'Read-only and free' partly restates the annotations and could be trimmed.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need no elaboration, yet the description still summarizes the fields an agent would reason about. With zero parameters and full annotation coverage, nothing an agent needs in order to call this correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes no parameters, so the baseline of 4 applies. The description correctly offers no parameter guidance and instead spends its words on what the fixed result contains.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a concrete resource and enumerates exactly what comes back: workspace, organisation, plan, environment, and unit totals (included, used, prepaid balance). That is specific enough that an agent can distinguish it from every sibling in the list, none of which report account or workspace state.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives concrete triggers ('where a document will land', 'how many renders are left') and a diagnostic case tied to a named sibling: when list_templates returns unexpected results. It stops short of stating when not to call it or naming alternatives for the diagnostic case, so it is clear context without exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_templatesList templatesARead-only
Lists the templates of the workspace this server works in (the one chosen at sign-in, or the API key's), most recently changed first: id, slug, name, kind (pdf, image, docx for Word, pptx for PowerPoint), engine, tags and published_version. Call it first to find the slug that get_template_schema, validate_template and render take; skip it when the user already named the template. Returns every match in one answer, without archived templates, and an empty list when nothing matches. A template whose published_version is null has never been published, so render cannot use it yet. Read-only and free.
| Name | Required | Description | Default |
|---|---|---|---|
| q | No | Text to find in the name or slug, case-insensitive (e.g. invoice) | |
| tag | No | Only templates with this tag; exact match, tags are lowercase with dashes (e.g. billing) | |
| kind | No | Only templates that produce this kind of document | |
| engine | No | Only templates written in this template language |
Output Schema
| Name | Required | Description |
|---|---|---|
| templates | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint/destructiveHint, but the description adds real behavioral facts: results are most-recently-changed first, archived templates are excluded, all matches come back in one answer (no pagination), an empty list signals no match, and a null published_version means render cannot use it. It also notes the call is free, which matters for an agent deciding whether to probe.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with purpose and scope, and every clause carries information an agent would otherwise have to guess. The single dense block runs long and chains several ideas, but there is no filler sentence to cut.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With an output schema present the description needn't enumerate return values, yet it still calls out ordering, archived exclusion, and the published_version null edge case that gates render. Combined with 100% schema coverage and clear annotations, an agent has everything needed to call this correctly or decide not to.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so q, tag, kind and engine are already fully documented, including case-insensitivity, exact-match tag format, and the kind/engine enums. The description's brief gloss of kind values repeats the enum rather than adding filtering semantics, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Specific verb (Lists) plus resource (templates) plus scope (the workspace this server works in, chosen at sign-in or the API key's), ordering rule, and the returned field set. It also names the siblings that consume the slug (get_template_schema, validate_template, render), so an agent can place it precisely in the workflow.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit when: "Call it first to find the slug that get_template_schema, validate_template and render take." Explicit when-not: "skip it when the user already named the template." Both the trigger and the exclusion are stated, not implied.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
renderRender a documentA
Creates a document by filling a stored template, or ad-hoc html, with data: a PDF or image, or DOCX, PPTX or PDF from Word and PowerPoint templates. Returns the render with status and download_url; for an image (png, jpg or webp) up to 2 MB the result also carries the picture itself, so look at what you made before handing it on (a PDF comes as its link). Use it to make a document from data; to turn an existing office file into a PDF, use convert_to_pdf. Stored templates render their published version, so one whose published_version is null fails with template_not_found. Every call is a new render: a live key or a signed-in agent consumes units, a test key is free (watermarked on the Free plan). A render that fails returns status failed and an error with code and message; validate_template finds most causes beforehand. With einvoice the PDF becomes a ZUGFeRD / Factur-X e-invoice built from data._invoice (check the block with check_invoice first): the render then carries einvoice, the validator's report, and a refused invoice names the fields and rules to fix. The link expires at expires_at; get_render issues a fresh one.
| Name | Required | Description | Default |
|---|---|---|---|
| data | No | Values for the template, shaped as get_template_schema describes; leave out when it needs none | |
| html | No | A complete HTML document to render instead of a stored template, up to 5 MB | |
| wait | No | Most renders are finished when the call returns; true also waits for one that continues in the background, false returns it as it is (queued or rendering) to check later with get_render | |
| engine | No | Template language of html, when it contains expressions to fill from data | |
| locale | No | BCP 47 tag for helpers and translations, e.g. de-DE | |
| output | No | Left out, the template decides: PDF for PDF templates and html, its image format for image templates, its default output for Word and PowerPoint templates. Word templates render docx or pdf, PowerPoint templates pptx or pdf | |
| einvoice | No | Makes the PDF a ZUGFeRD / Factur-X e-invoice whose XML is built from data._invoice: true for the defaults, an object to choose, false to switch off what the template declares. PDF only; live renders from the Starter plan, test keys on Free make 20 marked ones a day | |
| filename | No | Name of the file behind download_url, e.g. invoice-2026-0042.pdf | |
| template | No | Slug or tpl_ id of a stored template; pass this or html |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | Yes | rnd_ id of the render |
| pdfa | No | The PDF/A verdict (PDF/A-2b or PDF/A-3b), when the template declares it |
| error | Yes | Why a failed render failed |
| units | Yes | Units the render counted |
| status | Yes | |
| einvoice | No | The e-invoice report, when the render made one |
| template | Yes | The template and version rendered; null for html and conversions |
| expires_at | Yes | When download_url stops working; get_render issues a fresh one |
| page_count | Yes | |
| environment | Yes | |
| download_url | Yes | Signed link to the file; null until the render succeeded |
| accessibility | No | The PDF/UA-1 verdict, when the template declares it |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnly=false, destructive=false), and the description adds real behavioral context: every call is a new render consuming units, test keys are free but watermarked, templates render their published version and fail with template_not_found when null. Failures return status failed with a code/message, and links expire at expires_at. Good additions, but the einvoice and return-shape details are partly duplicated by the output schema, so it sits at a solid 3.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core action, then progressively less common concerns (units, einvoice, expiry). It is long, and the einvoice sentence packs several ideas together, but nearly every sentence carries operational information and could not be cut without loss.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 9-parameter, nested-object, mutation tool with an output schema, the description covers source choice, output defaults, failure mode, cost, expiry and the einvoice branch. Nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the schema already documents each parameter with enums and nested einvoice fields. The description adds a few non-obvious constraints — data shaped per get_template_schema, data._invoice for einvoice, published-version template selection — but most of its parameter talk restates the schema. Baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a concrete verb+resource+output matrix: filling a stored template or ad-hoc html with data, yielding PDF/image/DOCX/PPTX. It explicitly names the sibling it is not (convert_to_pdf) in the same breath, so an agent can route without opening either schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
States the positive case ("Use it to make a document from data") and the exclusion with an alternative ("to turn an existing office file into a PDF, use convert_to_pdf"). It also points to validate_template and check_invoice as preflight steps, giving concrete ordering guidance rather than vague hints.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
save_templateSave a template as a draftA
Saves a template as a draft, never published: renders keep using the published version until a person publishes the draft in Formfeed. With template and base_checksum it saves a new version of that template: read it with get_template_source first, change what the user asked for, and pass only the parts you changed (the others are kept from the version you read). With new_template and html it creates a template whose version 1 is the draft. change_note says what changed; the person reads it before publishing. Returns the saved version, the check of the draft with its sample data (fix every error and save again) and review_url, the page where the person reviews and publishes it: hand that to the user. Needs an API key with template:write, or, for a signed-in agent, the person's permission under Account → Connected agents in Formfeed. Word and PowerPoint templates cannot be saved here. Free; a new template counts towards the plan's template limit.
| Name | Required | Description | Default |
|---|---|---|---|
| css | No | The stylesheet; up to 1 MB | |
| head | No | Extra markup for <head>, such as font links | |
| html | No | The body markup in the template language, without <html>, <head> or <body>; up to 2 MB | |
| i18n | No | Translations: locale → key → text, read by the t helper | |
| partials | No | Partials the template includes (name → source) | |
| settings | No | Paper, margins, header, footer, fonts and the other settings, as get_template_source returns them; replaces the stored settings as a whole | |
| template | No | Slug or tpl_ id of the template to save a new draft of; pass this or new_template | |
| data_sets | No | Named sample data sets besides sample_data (name → data) | |
| change_note | Yes | What the draft changes, in one sentence, shown in the version history | |
| data_schema | No | JSON Schema of data; null removes the stored one | |
| sample_data | No | The data the editor previews with and the draft is checked with; never used by a render | |
| new_template | No | Creates a template whose version 1 is this draft; pass this and html, or template | |
| base_checksum | No | With template: the checksum get_template_source returned. When someone saved a newer version since, nothing is saved: read the source again and redo the change on it |
Output Schema
| Name | Required | Description |
|---|---|---|
| ok | Yes | false when the check of the draft found an error; null when the check could not run |
| next | Yes | |
| version | Yes | |
| template | Yes | |
| review_url | No | The page in Formfeed where the person reviews and publishes the draft |
| diagnostics | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations, it discloses the key behavioral trait that the save is draft-only and does not affect renders until a person publishes. It also covers authorization requirements, the return contents including review_url, the validation loop with sample data, base_checksum conflict behavior, and plan-limit implications.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but appropriately sized for a 13-parameter mutation tool with nested objects. The most important fact, that this saves a draft and never publishes, is front-loaded, and each subsequent sentence adds operational guidance rather than repeating schema details.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
It covers the essential context an agent needs: draft semantics, the two invocation modes, prerequisite read via get_template_source, authorization, unsupported template types, validation expectations, and the review workflow. Although an output schema exists, the description's mention of review_url and validation errors is still useful for agent behavior.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is already 100%, so the schema carries most parameter documentation. The description still adds meaningful semantics beyond the schema for the central parameters: partial updates keep unchanged parts from the read version, base_checksum detects newer versions, and change_note is read by the person before publishing.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource ('Saves a template as a draft') and immediately distinguishes it from publishing or rendering by noting that renders keep using the published version. It also clearly separates the two save modes: updating an existing template versus creating a new one.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit workflow guidance: read with get_template_source first, change only what the user asked for, and pass template plus base_checksum for an existing template, or new_template plus html for a new one. It also names a limitation (Word and PowerPoint templates cannot be saved here), which helps the agent avoid misuse.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
validate_templateValidate a template with dataARead-only
Checks a template and its data without rendering: free, no units, nothing stored, no document produced. Call it before render when you assembled the data yourself, or after writing ad-hoc html. With template, the API checks the published version (else the latest, or the version you name) as a render would: syntax, header and footer, errors only this data triggers, and the data against the stored JSON Schema (a wrong type or a missing required field is an error). With html and engine it compiles offline and reports syntax errors, unknown filters and variables missing from data. Returns ok and diagnostics (severity, code, message, plus path, line and column, or part and paragraph for Word and PowerPoint). Warnings leave ok true: settle each before rendering.
| Name | Required | Description | Default |
|---|---|---|---|
| data | No | The data the document will be rendered with, shaped as get_template_schema describes. Left out, a stored template is checked with its sample data | |
| html | No | Template source to check offline, when there is no stored template; pass this or template | |
| engine | No | Template language of html; required with html | |
| version | No | With template: the version to check, such as latest for the newest draft or a number; left out, the published one, else the latest | |
| template | No | Slug or tpl_ id of a stored template; pass this or html |
Output Schema
| Name | Required | Description |
|---|---|---|
| ok | Yes | false when a diagnostic is an error |
| template | No | The stored version checked; absent for html |
| diagnostics | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Adds substantial context beyond the readOnly/non-destructive annotations: the call is free, consumes no units, stores nothing, and produces no document. It also discloses result semantics — `ok` plus `diagnostics` with severity, code, message, path/line/column, or part/paragraph for Word and PowerPoint — and clarifies that warnings leave `ok` true but should be settled before rendering.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Dense but front-loaded: cost and side-effect guarantees come first, then when to call, then per-mode behavior, then return semantics. A few clauses restate schema-documented defaults (version fallback, data omission), but the length is justified by the tool's two-mode complexity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Covers everything an agent needs for a 5-parameter, mode-switching validation tool: cost, side effects, mode selection, what each mode checks, and how to interpret warnings versus errors. An output schema exists, yet the description still summarizes the return contract helpfully rather than leaving a gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds cross-parameter behavior the schema does not convey: the `template` path validates against the published version falling back to latest or a named version, whereas `html` + `engine` compiles offline and reports unknown filters and variables missing from `data`. It also explains what the data is checked against (the stored JSON Schema, with wrong types or missing required fields as errors).
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ("Checks a template and its data without rendering") and immediately distinguishes itself from `render` by declaring no units are consumed and no document is produced. It further splits the tool's two operating modes (stored template vs. ad-hoc html) so an agent can tell exactly which capability it is invoking.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit triggering conditions: call it "before render when you assembled the data yourself, or after writing ad-hoc html," which effectively routes the agent away from `render` when validation is the goal. It does not state any when-not conditions (e.g., cases where validation is redundant), so it falls just short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
3 tool updates
v0.3.22- Changed
convert_to_pdf1 field changed- added
Output schema / properties / pdfaAdded value: +{ + "additionalProperties": {}, + "description": "The PDF/A verdict (PDF/A-2b or PDF/A-3b), when the template declares it", + "propertyNames": { + "type": "string" + }, + "type": "object" +}
- Changed
get_render1 field changed- added
Output schema / properties / pdfaAdded value: +{ + "additionalProperties": {}, + "description": "The PDF/A verdict (PDF/A-2b or PDF/A-3b), when the template declares it", + "propertyNames": { + "type": "string" + }, + "type": "object" +}
- Changed
render1 field changed- added
Output schema / properties / pdfaAdded value: +{ + "additionalProperties": {}, + "description": "The PDF/A verdict (PDF/A-2b or PDF/A-3b), when the template declares it", + "propertyNames": { + "type": "string" + }, + "type": "object" +}
3 tool updates
v0.3.21- Added
get_template_source - Added
save_template - Changed
validate_template1 field changed- added
Input schema / properties / versionAdded value: +{ + "anyOf": [ + { + "maxLength": 32, + "minLength": 1, + "type": "string" + }, + { + "exclusiveMinimum": 0, + "maximum": 9007199254740991, + "type": "integer" + } + ], + "description": "With template: the version to check, such as latest for the newest draft or a number; left out, the published one, else the latest" +}
8 tool updates
v0.3.19- Changed
check_invoice1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "ok": { + "type": "boolean" + }, + "problems": { + "items": { + "additionalProperties": false, + "properties": { + "message": { + "type": "string" + }, + "path": { + "description": "The field, e.g. _invoice.seller.vat_id", + "type": "string" + }, + "rule": { + "description": "The EN 16931 rule, e.g. BR-03", + "type": "string" + } + }, + "required": [ + "path", + "message" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "ok", + "problems" + ], + "type": "object" +}
- Changed
convert_to_pdf1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "accessibility": { + "additionalProperties": {}, + "description": "The PDF/UA-1 verdict, when the template declares it", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "download_url": { + "description": "Signed link to the file; null until the render succeeded", + "type": [ + "string", + "null" + ] + }, + "einvoice": { + "additionalProperties": {}, + "description": "The e-invoice report, when the render made one", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "environment": { + "enum": [ + "live", + "test" + ], + "type": "string" + }, + "error": { + "anyOf": [ + { + "additionalProperties": false, + "properties": { + "code": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "type": "object" + }, + { + "type": "null" + } + ], + "description": "Why a failed render failed" + }, + "expires_at": { + "description": "When download_url stops working; get_render issues a fresh one", + "type": [ + "string", + "null" + ] + }, + "id": { + "description": "rnd_ id of the render", + "type": "string" + }, + "page_count": { + "type": [ + "number", + "null" + ] + }, + "status": { + "enum": [ + "queued", + "rendering", + "succeeded", + "failed" + ], + "type": "string" + }, + "template": { + "anyOf": [ + { + "additionalProperties": false, + "properties": { + "canary": { + "type": "boolean" + }, + "channel": { + "type": [ + "string", + "null" + ] + }, + "id": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": [ + "id", + "slug", + "version" + ], + "type": "object" + }, + { + "type": "null" + } + ], + "description": "The template and version rendered; null for html and conversions" + }, + "units": { + "description": "Units the render counted", + "type": "number" + } + }, + "required": [ + "id", + "status", + "download_url", + "expires_at", + "page_count", + "units", + "environment", + "template", + "error" + ], + "type": "object" +}
- Changed
get_render1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "accessibility": { + "additionalProperties": {}, + "description": "The PDF/UA-1 verdict, when the template declares it", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "download_url": { + "description": "Signed link to the file; null until the render succeeded", + "type": [ + "string", + "null" + ] + }, + "einvoice": { + "additionalProperties": {}, + "description": "The e-invoice report, when the render made one", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "environment": { + "enum": [ + "live", + "test" + ], + "type": "string" + }, + "error": { + "anyOf": [ + { + "additionalProperties": false, + "properties": { + "code": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "type": "object" + }, + { + "type": "null" + } + ], + "description": "Why a failed render failed" + }, + "expires_at": { + "description": "When download_url stops working; get_render issues a fresh one", + "type": [ + "string", + "null" + ] + }, + "id": { + "description": "rnd_ id of the render", + "type": "string" + }, + "page_count": { + "type": [ + "number", + "null" + ] + }, + "status": { + "enum": [ + "queued", + "rendering", + "succeeded", + "failed" + ], + "type": "string" + }, + "template": { + "anyOf": [ + { + "additionalProperties": false, + "properties": { + "canary": { + "type": "boolean" + }, + "channel": { + "type": [ + "string", + "null" + ] + }, + "id": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": [ + "id", + "slug", + "version" + ], + "type": "object" + }, + { + "type": "null" + } + ], + "description": "The template and version rendered; null for html and conversions" + }, + "units": { + "description": "Units the render counted", + "type": "number" + } + }, + "required": [ + "id", + "status", + "download_url", + "expires_at", + "page_count", + "units", + "environment", + "template", + "error" + ], + "type": "object" +}
- Changed
get_template_schema1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "sample_data": { + "additionalProperties": {}, + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "schema": { + "additionalProperties": {}, + "description": "JSON Schema of data; inferred from sample_data when none is stored", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "status": { + "description": "published, or draft when nothing is published yet", + "type": "string" + }, + "template": { + "type": "string" + }, + "version": { + "description": "The version the schema comes from", + "type": "number" + } + }, + "required": [ + "template", + "version", + "status", + "schema", + "sample_data" + ], + "type": "object" +}
- Changed
get_workspace1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "environment": { + "description": "live renders count units; test renders are free", + "enum": [ + "live", + "test" + ], + "type": "string" + }, + "organization": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "slug": { + "type": "string" + } + }, + "required": [ + "id", + "slug", + "name" + ], + "type": "object" + }, + "period": { + "additionalProperties": false, + "description": "The current billing period", + "properties": { + "end": { + "type": "string" + }, + "start": { + "type": "string" + } + }, + "required": [ + "start", + "end" + ], + "type": "object" + }, + "plan": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "monthly_units": { + "description": "Units included a month; null where the plan sets none", + "type": [ + "number", + "null" + ] + }, + "name": { + "type": "string" + } + }, + "required": [ + "id", + "name" + ], + "type": "object" + }, + "units": { + "additionalProperties": false, + "properties": { + "included": { + "type": [ + "number", + "null" + ] + }, + "overage_balance": { + "description": "Prepaid units left beyond the included ones", + "type": "number" + }, + "used": { + "type": "number" + } + }, + "required": [ + "used" + ], + "type": "object" + }, + "workspace": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "region": { + "description": "eu, us or apac", + "type": "string" + }, + "slug": { + "type": "string" + } + }, + "required": [ + "id", + "slug", + "name", + "region" + ], + "type": "object" + } + }, + "required": [ + "workspace", + "organization", + "plan", + "period", + "units" + ], + "type": "object" +}
- Changed
list_templates1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "templates": { + "items": { + "additionalProperties": false, + "properties": { + "description": { + "type": [ + "string", + "null" + ] + }, + "engine": { + "description": "jinja2, liquid or handlebars", + "type": "string" + }, + "id": { + "description": "tpl_ id", + "type": "string" + }, + "kind": { + "description": "pdf, image, docx (Word) or pptx (PowerPoint)", + "type": "string" + }, + "name": { + "type": "string" + }, + "published_version": { + "description": "null: never published, so render cannot use it yet", + "type": [ + "number", + "null" + ] + }, + "slug": { + "type": "string" + }, + "tags": { + "items": { + "type": "string" + }, + "type": "array" + }, + "updated_at": { + "type": "string" + } + }, + "required": [ + "id", + "slug", + "name", + "kind", + "engine", + "description", + "tags", + "published_version", + "updated_at" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "templates" + ], + "type": "object" +}
- Changed
render1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "accessibility": { + "additionalProperties": {}, + "description": "The PDF/UA-1 verdict, when the template declares it", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "download_url": { + "description": "Signed link to the file; null until the render succeeded", + "type": [ + "string", + "null" + ] + }, + "einvoice": { + "additionalProperties": {}, + "description": "The e-invoice report, when the render made one", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "environment": { + "enum": [ + "live", + "test" + ], + "type": "string" + }, + "error": { + "anyOf": [ + { + "additionalProperties": false, + "properties": { + "code": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "type": "object" + }, + { + "type": "null" + } + ], + "description": "Why a failed render failed" + }, + "expires_at": { + "description": "When download_url stops working; get_render issues a fresh one", + "type": [ + "string", + "null" + ] + }, + "id": { + "description": "rnd_ id of the render", + "type": "string" + }, + "page_count": { + "type": [ + "number", + "null" + ] + }, + "status": { + "enum": [ + "queued", + "rendering", + "succeeded", + "failed" + ], + "type": "string" + }, + "template": { + "anyOf": [ + { + "additionalProperties": false, + "properties": { + "canary": { + "type": "boolean" + }, + "channel": { + "type": [ + "string", + "null" + ] + }, + "id": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": [ + "id", + "slug", + "version" + ], + "type": "object" + }, + { + "type": "null" + } + ], + "description": "The template and version rendered; null for html and conversions" + }, + "units": { + "description": "Units the render counted", + "type": "number" + } + }, + "required": [ + "id", + "status", + "download_url", + "expires_at", + "page_count", + "units", + "environment", + "template", + "error" + ], + "type": "object" +}
- Changed
validate_template1 field changed- changed
Output schema / (root)Previous value: -nullNew value: +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "additionalProperties": false, + "properties": { + "diagnostics": { + "items": { + "additionalProperties": false, + "properties": { + "code": { + "type": "string" + }, + "column": { + "type": "number" + }, + "line": { + "type": "number" + }, + "message": { + "type": "string" + }, + "paragraph": { + "type": "number" + }, + "part": { + "description": "Word and PowerPoint templates: the file part of the tag", + "type": "string" + }, + "path": { + "description": "Where in the data, for data errors", + "type": "string" + }, + "severity": { + "enum": [ + "error", + "warning", + "info" + ], + "type": "string" + } + }, + "required": [ + "severity", + "code", + "message" + ], + "type": "object" + }, + "type": "array" + }, + "ok": { + "description": "false when a diagnostic is an error", + "type": "boolean" + }, + "template": { + "additionalProperties": false, + "description": "The stored version checked; absent for html", + "properties": { + "id": { + "type": "string" + }, + "slug": { + "type": "string" + }, + "version": { + "type": "number" + } + }, + "required": [ + "id", + "slug", + "version" + ], + "type": "object" + } + }, + "required": [ + "ok", + "diagnostics" + ], + "type": "object" +}
2 tool updates
v0.3.17- Added
check_invoice - Changed
render1 field changed- added
Input schema / properties / einvoiceAdded value: +{ + "anyOf": [ + { + "type": "boolean" + }, + { + "properties": { + "display_check": { + "description": "What a value of the XML that the PDF does not show does: a warning in the report (default) or a failed render", + "enum": [ + "warn", + "strict" + ], + "type": "string" + }, + "flavour": { + "description": "One specification under two names; the default factur-x and zugferd make the same file", + "enum": [ + "factur-x", + "zugferd" + ], + "type": "string" + }, + "profile": { + "description": "en16931 (default) carries the whole European standard; basic is the smallest full invoice and is refused between two French businesses", + "enum": [ + "en16931", + "basic" + ], + "type": "string" + }, + "xml": { + "description": "both also stores the XML as a file of its own, linked as einvoice.xml_url", + "enum": [ + "embedded", + "both" + ], + "type": "string" + } + }, + "type": "object" + } + ], + "description": "Makes the PDF a ZUGFeRD / Factur-X e-invoice whose XML is built from data._invoice: true for the defaults, an object to choose, false to switch off what the template declares. PDF only; live renders from the Starter plan, test keys on Free make 20 marked ones a day" +}
1 tool update
v0.3.14- Added
get_workspace
6 tool updates
v0.3.10- Changed
convert_to_pdf5 fields changed- changed
Input schema / properties / file_base64 / descriptionPrevious value: -"The document, base64 encoded"New value: +"The document to convert, base64 encoded; needs file_name" - changed
Input schema / properties / filename / descriptionPrevious value: -"Name of the PDF"New value: +"Name of the PDF behind download_url, e.g. report.pdf" - changed
Input schema / properties / page_ranges / descriptionPrevious value: -"Pages to convert, e.g. 1-3,5"New value: +"Pages to convert, e.g. 1-3,5; all pages when left out" - changed
Input schema / properties / path / descriptionPrevious value: -"Absolute path of a document on this machine"New value: +"Absolute path of the document on this machine" - changed
Input schema / properties / render_id / descriptionPrevious value: -"rnd_ id of a render whose output is docx or pptx"New value: +"rnd_ id of a render whose output is docx or pptx, to get that document as a PDF"
- Changed
get_render1 field changed- added
Input schema / properties / id / descriptionAdded value: +"rnd_ id of the render, as render or convert_to_pdf returned it"
- Changed
get_template_schema1 field changed- changed
Input schema / properties / template / descriptionPrevious value: -"Template slug or tpl_ id"New value: +"Slug (e.g. invoice) or tpl_ id, as list_templates returns them"
- Changed
list_templates4 fields changed- added
Input schema / properties / engine / descriptionAdded value: +"Only templates written in this template language" - added
Input schema / properties / kind / descriptionAdded value: +"Only templates that produce this kind of document" - changed
Input schema / properties / q / descriptionPrevious value: -"Search in name and slug"New value: +"Text to find in the name or slug, case-insensitive (e.g. invoice)" - added
Input schema / properties / tag / descriptionAdded value: +"Only templates with this tag; exact match, tags are lowercase with dashes (e.g. billing)"
- Changed
render6 fields changed- added
Input schema / properties / data / descriptionAdded value: +"Values for the template, shaped as get_template_schema describes; leave out when it needs none" - changed
Input schema / properties / engine / descriptionPrevious value: -"Engine for html that contains template syntax"New value: +"Template language of html, when it contains expressions to fill from data" - added
Input schema / properties / filename / descriptionAdded value: +"Name of the file behind download_url, e.g. invoice-2026-0042.pdf" - changed
Input schema / properties / html / descriptionPrevious value: -"Raw HTML instead of a template"New value: +"A complete HTML document to render instead of a stored template, up to 5 MB" - changed
Input schema / properties / template / descriptionPrevious value: -"Template slug or tpl_ id"New value: +"Slug or tpl_ id of a stored template; pass this or html" - changed
Input schema / properties / wait / descriptionPrevious value: -"Wait for the render to finish (sync renders finish immediately)"New value: +"Most renders are finished when the call returns; true also waits for one that continues in the background, false returns it as it is (queued or rendering) to check later with get_render"
- Changed
validate_template4 fields changed- added
Input schema / properties / data / descriptionAdded value: +"The data the document will be rendered with, shaped as get_template_schema describes. Left out, a stored template is checked with its sample data" - changed
Input schema / properties / engine / descriptionPrevious value: -"Required with html"New value: +"Template language of html; required with html" - changed
Input schema / properties / html / descriptionPrevious value: -"Template source when no slug is given"New value: +"Template source to check offline, when there is no stored template; pass this or template" - changed
Input schema / properties / template / descriptionPrevious value: -"Template slug or tpl_ id"New value: +"Slug or tpl_ id of a stored template; pass this or html"
6 tool updates
v0.3.7- First observed
convert_to_pdf - First observed
get_render - First observed
get_template_schema - First observed
list_templates - First observed
render - First observed
validate_template
TDQS
Scored across 10 tools
Most tools target a clearly distinct operation (listing, reading source, reading schema, saving, validating, rendering, converting, checking status, validating invoices, workspace info), and the descriptions explicitly cross-reference each other (e.g. render vs convert_to_pdf, validate_template vs render). The remaining soft overlaps are get_template_schema vs get_template_source (both 'read about a template') and validate_template vs check_invoice (both 'validate'), but the descriptions disambiguate them well enough to avoid misselection.
Names follow a consistent snake_case verb_noun pattern: get_template_source, get_template_schema, get_render, get_workspace, list_templates, save_template, validate_template, convert_to_pdf, check_invoice. The lone deviation is `render`, a bare verb with no object, which slightly breaks the pattern but is unambiguous in context.
Ten tools is well within the sweet spot and each earns its place: discovery, schema, source, edit, validation, render, conversion, polling, e-invoice check, and workspace context. Nothing feels padded or redundant.
The core lifecycle is covered: list/read/schema/save-draft/validate/render/convert/poll, with a deliberate human publish step and invoice validation for e-invoicing. Gaps exist around template housekeeping (no archive/delete, no list of versions or past renders beyond a single id), but agents can work around these via the review_url and Formfeed UI.
Maintenance
Related MCP Connectors
Render PDFs and images from your Formfeed templates, check data against a template's schema, make ZUGFeRD / Factur-X e-invoices checked against EN 16931, and convert Office files to PDF. Sign in with your Formfeed account or use an API key.
Automate production-ready PDF, Word, Excel and PowerPoint documents from templates and JSON data
Render PDFs and images from your Typefold templates, and build visual templates.
Render PDFs from templates you design once: list templates, check the data shape, preview, render.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables Word document generation from templates using Jinja2 syntax and parsing of DOCX, PDF, and Excel files to extract structured content, metadata, and text.10 npm1MIT

docjet-mcpofficial
AlicenseAqualityCmaintenanceRender branded PDF documents (invoices, reports, certificates) and PNG social images directly from any MCP client using DocJet templates or raw HTML with JSON data.321 npmMIT- AlicenseNot gradedqualityDmaintenanceEnables PDF and image generation from templates, JSON, HTML, or URLs through the PDF Gen Studio API. Supports rendering, template management, and multiple output formats.6 npm1MIT

DocuQueue MCP Serverofficial
AlicenseNot gradedqualityCmaintenanceEnables creating professional documents (invoices, contracts, certificates, proposals, reports) via the DocuQueue API, with tools for template management, filling, previewing, and PDF generation.70 npm1MIT