Skip to content

Authentication

API keys for server-to-server calls, OAuth 2.0 for apps acting on behalf of users.

API keys

Create a key under Settings, Developers. The full key is shown once. Send it as a bearer token. A key is bound to one workspace and limited to the scopes you pick; scopes are the same strings as permissions.

http
GET /api/v1/forms
Authorization: Bearer ff_live_xxxxxxxx
FormFlow-Version: 2026-10-01
ScopeAllows
form:readRead forms, versions, themes
form:writeCreate and edit drafts
form:publishPublish forms
submission:readList and read submissions
submission:deleteEdit and delete submissions
submission:exportCreate exports
webhook:manageManage webhook endpoints
member:manageList and manage members
apikey:manageManage API keys
audit:readRead audit events
analytics:readMetrics, question summaries, funnel, NPS/CSAT
ai:useAI form generation and response analysis (uses credits)
workspace:manageInclude sensitive fields in responses
form:deleteDelete forms

Who am I

GET /api/v1/me returns the credential type, workspace, role and the effective permissions and scopes, which is the quickest way to debug a 403.

OAuth 2.0 (authorization code + PKCE)

Register an app, then send users to the authorization endpoint. PKCE with S256 is required.

text
GET /api/v1/oauth/authorize?response_type=code&client_id=CLIENT_ID
  &redirect_uri=https://app.example.com/cb&scope=form:read%20submission:read
  &state=XYZ&code_challenge=BASE64URL(SHA256(verifier))&code_challenge_method=S256

POST /api/v1/oauth/token
  grant_type=authorization_code&code=...&redirect_uri=...&client_id=...&code_verifier=...

POST /api/v1/oauth/revoke
  token=...

Access tokens start with ffo_ and expire after one hour; use the refresh token to renew.

Browser sessions and CSRF

The dashboard uses a session cookie; mutating requests then need the CSRF token. Integrations should use API keys or OAuth, which are not subject to CSRF checks.