@formfeed/sdk
The TypeScript client for the Formfeed API: generate PDFs and images from
templates, and fill Word and PowerPoint templates. It has no dependencies and uses only Web APIs (fetch, Web Crypto), so it runs in Node 22+,
Deno, Bun, Cloudflare Workers and browsers.
- Retries
429 and 503 with the server's Retry-After.
- Sends an
Idempotency-Key with every render, so a retried request never renders twice.
- Turns API problems into
FormfeedError with code, status and request_id.
- Verifies webhook signatures.
npm install @formfeed/sdk
Render a template
import { Formfeed } from '@formfeed/sdk';
const client = new Formfeed({ apiKey: process.env.FORMFEED_API_KEY!, region: 'eu' });
const render = await client.renders.create({
template: 'invoice-de',
data: { invoice: { number: '2026-0042', lines: [{ description: 'Consulting', qty: 8, price: 120 }] } },
});
console.log(render.download_url, render.page_count);
const pdf = await client.renders.download(render);
Async renders and batches
const queued = await client.renders.create({ template: 'invoice-de', data, mode: 'async' });
const done = await client.renders.waitFor(queued.id);
const job = await client.renders.batch({
template: 'invoice-de',
items: orders.map((order) => ({ data: { invoice: order } })),
zip: true,
});
const finished = await client.jobs.waitFor(job.id);
Word and PowerPoint
import { readFile } from 'node:fs/promises';
await client.templates.create({
name: 'Offer',
slug: 'offer',
kind: 'docx',
engine: 'jinja2',
file: { data: await readFile('offer.docx'), name: 'offer.docx' },
publish: true,
});
const filled = await client.renders.create({ template: 'offer', output: 'docx', data });
await client.templates.versions.create('offer', { file: { data: await readFile('offer-v2.docx'), name: 'offer.docx' } });
const docx = await client.templates.versions.file('offer', 'latest');
const pdf = await client.pdf.convert({ file: { data: await readFile('report.xlsx'), name: 'report.xlsx' } }, { single_page_sheets: true });
Webhooks
import { parseWebhookEvent } from '@formfeed/sdk';
const event = await parseWebhookEvent(secret, request.headers.get('webhook-signature'), rawBody);
if (event.type === 'render.completed') console.log(event.data);
Documentation
Licence
MIT