Security
Last updated: 2026-10-17
What protects the documents and data you send to Formfeed, where they are kept and for how long. The documentation has the details: data residency, authentication and webhooks.
Where your data lives
Formfeed renders in the EU only. The database and sign-in run at Supabase in Frankfurt (AWS eu-central-1). Rendering runs on Fly.io in Frankfurt: Chromium for HTML templates, LibreOffice for turning office files into PDFs, and the service that makes and checks e-invoices and PDF/UA-1 files. Rendered files, files in the file library, brand kit logos and Word and PowerPoint template files are stored in Cloudflare R2 under EU jurisdiction. No other region renders: api-us.formfeed.dev is reserved for a US region that is not open, and a request sent there renders in the EU as well.
Two parts run wherever is closest to the caller, which can be outside the EU. The API gateway on Cloudflare’s edge forwards each request to Frankfurt without storing what it carries; in Cloudflare’s global key-value store it keeps API key details, template versions and the brand kit for a minute, font lists for ten minutes and the replies to requests with an idempotency key for 24 hours. The app’s functions at Supabase (the editor’s true render, billing, the imports from other providers) also run closest to their caller and store nothing outside Frankfurt.
A workspace can also have every output copied into a bucket of its own (bring your own storage, Pro and above): that bucket is wherever the workspace chose, and Formfeed keeps its own copy in the EU for the retention. Webhook events go to the addresses you register.
Isolation
Every table that holds an organisation’s data carries the organisation’s id and is protected by row-level security that the database enforces, not only the application. The API reads and writes only through database functions, which it calls with the workspace of the key.
Every render gets a browser context of its own in Chromium, closed when the render ends, so no cookies or storage pass from one render to the next. A template’s JavaScript runs as script of that page, never in the server’s own process. Every connection Chromium opens, WebSockets included, passes a proxy that checks the address after DNS resolution: private networks, loopback and link-local addresses, cloud metadata services and internal hosts are refused, and so is plain http; service workers are blocked, and WebRTC gets no direct UDP.
The office converter loads nothing from the web. It and the e-invoice service have no public address: only the render servers reach them, through Fly.io’s private network.
The editor’s previews run in sandboxed frames. The CDN serves the HTML kept for a failed render and every file of the file library with Content-Security-Policy: sandbox, so a file opened directly runs no script.
Sign-in and roles
Members sign in with a password, a magic link, or a Google or GitHub account. Two-factor authentication is optional for each member and included in every plan: a 6-digit code from an authenticator app (TOTP), asked for at sign-in, before a password reset, when approving formfeed login and when allowing an AI agent. There are no recovery codes: a second authenticator is the backup, and without one, support switches the second factor off only after checking that the account is yours. API keys are not affected by it.
Each member has a role: owner, admin, editor or viewer. Only owners and admins create API keys in the app; the key that formfeed login gives a member’s command line has only the scopes of that member’s role. An AI agent signed in through the hosted MCP server can read templates, renders and the account and render documents, nothing more, whatever the member’s role. On Pro and above, owners and admins see in the activity log who changed members, keys, templates, webhooks and settings (30 days on Pro, 365 on Business).
API keys and webhooks
An API key is shown once, when it is created. Formfeed stores only a SHA-256 hash of it, combined with a secret that never leaves the database’s vault. A key belongs to one workspace, carries scopes and an environment (live or test), shows when it was last used and can be revoked at any time. A revoked key stops working within a minute, because the API keeps a key’s details for up to 60 seconds. The API refuses a key sent from a web page: keys belong on servers.
Deliveries to a registered webhook endpoint are signed with HMAC-SHA256 over a timestamp and the body (header Webhook-Signature), with a secret that is shown once. A per-render webhook_url is signed only when the request also sends a webhook_secret.
The credentials of a workspace’s own bucket are kept in the database’s vault, readable only by the render servers, and Formfeed only ever writes to that bucket. Secrets of the platform live in the providers’ secret stores, never in the code.
Download links
Download links are signed unless you choose otherwise: a signed link is valid for at most seven days and never longer than the file is kept, and GET /renders/{id} issues a fresh one. A workspace or a request can choose public links instead, which have no signature and work until the file is deleted; use them only for documents that are not confidential. Files in the file library are always served without a signature, under an address made of the workspace id and the file name, so keep customer documents out of the library. See download links, access and retention.
Encryption
Connections over the internet are encrypted with TLS. Between the render servers and the converters they call, traffic stays inside Fly.io’s private network. The database, its backups and the file storage are encrypted at rest.
Retention
Rendered files are deleted when the plan’s retention ends: 1 day on Free, 30 days on Starter, 365 days on Pro, without a limit on Business. A workspace’s setting and a request’s expires_in can only shorten it, outputs of test renders are kept 24 hours at most, and any output can be deleted earlier through the API. A batch zip is deleted when the first file in it expires (a file deleted sooner through the API stays in the zip until then); its link expires after seven days at most.
The data you send is used for the render and then dropped, unless the workspace turns on Keep render requests (24 hours on Starter, up to 7 days on Pro and above) to replay failed renders; every read of a kept request is written to the audit log, and passwords for PDF protection are never kept. A failed render keeps its assembled HTML, which contains its data, for 24 hours, so the render log can show what failed. Fonts and images that templates load from the web are cached on the render servers for up to 7 days.
The render log keeps metadata (status, timings, units, template version, your meta, checksums), never the document; its entries are deleted after 13 months, or later only while the file they belong to is still stored. Deleting a workspace revokes its keys at once and removes its content after 30 days; deleting the organisation removes its data after 30 days, and from backups within a further 30 days.
Operations
The database is backed up by Supabase. Errors of the API and the render servers are reported to Sentry in its EU data region, without request bodies. Uptime monitors check the public endpoints from Europe and the US, and the status page shows outages and planned maintenance. Dependencies are checked against published security advisories every night. Every render records the Chromium build it used, a checksum of the template version and a SHA-256 of the file, so a document can be traced to what made it.
Incidents
If a breach of the security of personal data affects your data, we tell you without undue delay, with what is known at that time, and follow up as the investigation goes on, as the data processing agreement says.
Responsible disclosure
Report vulnerabilities to security@formfeed.dev (PGP on request). We acknowledge within two working days, keep you informed, and take no legal action against good-faith research that avoids privacy violations, data destruction and service disruption. Our security.txt names the contact for automated tools.
Compliance
GDPR: a data processing agreement under Art. 28 is part of the terms of every paid plan, and Free workspaces can request it by email. The sub-processor list names every provider, and organisation owners hear of a change 30 days ahead. Templates and renders can be exported through the API and the CLI; outputs are deleted through the API, workspaces in the app or through the API, the organisation in the app. Formfeed holds no SOC 2 or ISO 27001 certification.