Documents carry the data you least want to explain to a regulator: names, addresses, amounts, sometimes health or employment details. So “hosted in the EU” deserves more than a flag on a pricing page. Here is the whole component list.
| Component | Where | What it holds |
|---|---|---|
| Database, authentication, fonts | Frankfurt | accounts, templates, render metadata |
| Rendering | Frankfurt | documents in memory during the render, nothing persistent |
| Output storage and CDN | Cloudflare R2, EU jurisdiction | rendered files until they expire |
| Library and template files | Cloudflare R2, EU jurisdiction | library uploads, brand logos, Word and PowerPoint templates |
| API gateway | Cloudflare edge, near the caller | request handling, no payloads; short-lived caches of key metadata and template versions, idempotent replies for a day |
| Resend, USA, standard contractual clauses | recipient address and mail text | |
| Billing | Stripe | billing contact, invoices, payment data |
| Errors and uptime | Sentry EU region, Better Stack | traces without document content |
Rendered documents leave the EU only when a workspace copies them to a bucket of its own. One part is not tied to the EU: the gateway runs at Cloudflare’s edge, so it can execute close to a caller outside the EU; it forwards to Frankfurt, stores no payload and keeps only short-lived caches in Cloudflare’s global key-value store. Account mail and payment data go to processors in the USA under standard contractual clauses, which is the honest answer rather than a claim that no third country is involved at all.
The endpoint says so
The documented host is api-eu.formfeed.dev. The alias api.formfeed.dev and api-us.formfeed.dev,
which is reserved for a later US region, reach the same gateway, and it does not take a region from
the host name: a render runs in the region of its key’s workspace, and every workspace is in the EU.
Every render answer says where it ran, in its region field and the X-Render-Region header, and
GET /v1/account names the workspace’s region. That is the point: where a document is rendered is a
property of the workspace that you can read back, not of a host name a caller can get wrong. Leave
the request’s region field out; the EU is the only region that renders.
Retention you control
Outputs expire with the plan’s retention, or sooner where the workspace’s setting or the expires_in
of a request says so, and a delete endpoint removes them at once. Render log entries keep metadata,
never the document, for 13 months, which is what makes billing and support possible; request payloads are stored only if a workspace turns that on. Deleting an
organisation removes its data within thirty days and backups within a further thirty.
Paper you can hand to legal
A data processing agreement under Article 28 GDPR is part of every paid plan and available on request for free workspaces. The sub-processor list is a page with change notifications, not a sentence in the terms. The security page describes the measures: encryption in transit and at rest, least-privilege access to the render path, row-level isolation per workspace in the database, and signed, expiring download URLs.
Why EU only at launch
Because a second region is not a checkbox. It is another database, another storage bucket, another render fleet, and a routing rule that has to be right every time. A US region in Ashburn is planned for when it can be done properly; it has no date yet. Until then, one region, documented, with the endpoint that proves it.