Skip to main content

Authentication

Every request carries an API key. Keys are created in the app under API keys → Create key, by owners and admins of the organisation. A key belongs to exactly one workspace, and its secret is shown once, when it is created; afterwards the app shows only its first characters (the prefix).

Authorization: Bearer ff_live_3f9c...

X-API-Key: ff_live_3f9c... is accepted as well; when a request sends both, Authorization counts. A key is ff_live_ or ff_test_ followed by 43 letters and digits.

When a key is refused​

AnswerWhy
401 unauthorizedNo key was sent (the answer carries WWW-Authenticate: Bearer), the key is not in the ff_live_…/ff_test_… format, or it is unknown or revoked. detail says which.
403 forbiddenThe key lacks the scope the endpoint needs (This API key lacks the "template:write" scope).
403 forbiddenThe request comes from a web page (it has an Origin header). Keys are for servers; see From a browser.
403 forbiddenThe plan does not include the feature; the problem names it in feature (post_processing, office, einvoice, channels …). See Plan limits.

Test and live keys​

The prefix tells you the environment. You pick it when you create the key (Test or Live).

ff_live_…ff_test_…
Use it forProductionDevelopment, CI, staging, the dev kit
UnitsCounted against the plan (units)Not counted. Test e-invoices are limited to a number per day instead
When the Free plan's units are used upRenders stop with 402 quota_exceededRenders go on
Outputs keptThe plan's retention, or the workspace's shorter one (retention)At most 24 hours
Test mark on the fileNeverOn the Free plan on every test file; on every plan on a test e-invoice
DeduplicationYesNo, every request renders
version: "latest" (the draft)Refused with 400 invalid_requestAllowed
Word and PowerPoint templatesFrom the Starter plan onOn the Free plan the PDF only (403 test_output_unavailable for docx and pptx)
Rate limitThe plan's requests per secondThe same bucket as live keys, plus 30 requests per minute on the Free plan
Webhook eventsDelivered to every endpoint that has live in its environmentsDelivered to every endpoint that has test in its environments (both are the default)

Everything else is identical: the same endpoints, the same templates, the same output. Switching to production means swapping the key.

Scopes​

A key can do only what its scopes allow. Pick them when you create the key; a ✓ marks the ones the dialog ticks by default.

ScopeAllowsDefault
render:createPOST /renders, POST /renders/batch, the PDF tools POST /pdf/merge, /pdf/protect, /pdf/watermark and /pdf/convert, the compatibility endpoints✓
render:readGET /renders, GET /renders/{id}, GET /renders/{id}/result, GET /jobs/{id}, POST /pdf/info✓
render:inputGET /renders/{id}/input, the stored request of a render (formfeed renders pull); every read is audited. See Reproduce a failed render
template:readReading templates, versions, files, schemas and channels, POST /templates/{id}/validate, reading the brand kit and shared partials (formfeed templates pull)✓
template:writeCreating, updating, publishing and archiving templates, moving release channels, writing shared partials (formfeed templates push)
webhook:manageManaging webhook endpoints (needed by the triggers of the no-code integrations), resending events to them, and live events in formfeed listen --live
webhook:listenformfeed listen sessions and resending events to them. See Develop webhooks locally
account:readGET /account, GET /usage (formfeed whoami)✓
file:readGET /files, GET /files/{id} (the workspace file library)
file:writePOST /files (upload to the library)
file:deleteDELETE /renders/{id}/outputs, DELETE /files/{id}
workspace:deleteDELETE /workspaces/{id} for any workspace of the key's organisation, its own included (formfeed workspaces delete). Never part of a default or of a CLI login key

A write scope comes with its read scope: ticking render:create in the dialog ticks render:read too, template:write ticks template:read and file:write ticks file:read, because writing answers with the object and a key should be able to read back what it wrote.

Give each integration its own key with the smallest set of scopes it needs: a backend that only renders needs render:create and render:read; the key in your CI publish step needs template:write.

Keeping keys safe​

  • Keep keys on the server. The API refuses a key sent from a web page (From a browser), and a key in a mobile app or a browser bundle is public anyway: anyone who finds it can render on your account.
  • Store keys in your secret manager or environment (FORMFEED_API_KEY), never in the repository.
  • Name keys after where they live (billing-service-prod, github-actions). The list under API keys shows when each key was Last used, so an unused key is easy to spot and revoke.
  • Rotate without downtime: create the new key, deploy it, check that the old key's last used time stops moving, then revoke the old key (Revoke key). A revoked key stops working within a minute, because the API keeps a key's details for up to 60 seconds.

The app and AI agents​

The app and AI agents connected through the MCP server with a Formfeed sign-in do not use API keys. They send the member's session token as Authorization: Bearer …, and the app names the workspace in X-Formfeed-Workspace. Such a token can do what the member's role allows. An agent's token is narrowed further, to render:create, render:read, template:read and account:read: an agent cannot publish, delete, manage webhooks or render a draft (latest), whatever the member's role. If the member allows it for that agent, and their role may edit templates, the agent may also save templates as drafts (POST /templates and POST /templates/{id}/versions without publish; see Template drafts). If the member uses two-factor authentication, an agent works only after the member has confirmed it in Formfeed. You do not need any of this for your own integrations: use an API key.

CLI login​

formfeed login uses a device flow instead of a pasted key: the CLI shows a code, you approve it in the browser at app.formfeed.dev/device and pick a workspace, and the CLI receives a live key (ff_live_…) named Formfeed CLI on <machine>, with the scopes of your role in the organisation. Renders made with it are live renders and count against the plan's units; use formfeed login --api-key ff_test_… with a test key when you only try things out.

RoleScopes of the CLI key
Viewerrender:read, template:read, account:read, file:read
Editorthe viewer's, plus render:create, template:write, file:write, render:input, webhook:listen
Admin and ownerthe editor's, plus webhook:manage and file:delete

The key never gets workspace:delete. It appears under API keys like any other and can be revoked there. formfeed logout only removes the key from your machine; it stays valid until you revoke it in the app. In CI, set FORMFEED_API_KEY instead. See CLI installation.