Developers

REST API

Authenticate with an API key, find the full OpenAPI reference, and learn the API's main resources, errors and conventions.

The FormWork REST API lets another system work with your forms, entries, data tables and automations: for reporting, migrations, syncing records or starting an automation from outside FormWork.

For a conversational integration, connect through the MCP server at https://app.useformwork.com/mcp. It is separate from the REST API.

Check the no-code options first. Automations, API connectors, imports and exports and embedding cover many integrations without writing a client.

Full reference

Every endpoint, with its parameters, request bodies and responses, is in the OpenAPI reference. These pages need no sign-in.

FormatURL
Interactive referenceapp.useformwork.com/api/v1/openapi
OpenAPI JSONapp.useformwork.com/api/v1/openapi.json
OpenAPI YAMLapp.useformwork.com/api/v1/openapi.yaml

The specification is the contract: requests that don’t match it are rejected before they reach FormWork.

Base URL

https://app.useformwork.com/api/v1

Requests and responses use JSON.

Authentication

Send an API key as a bearer token, and the ID of its account in X-Account-ID, on every request:

curl "https://app.useformwork.com/api/v1/projects" \
  -H "Authorization: Bearer fwk_..." \
  -H "X-Account-ID: YOUR_ACCOUNT_ID"
  • Keys start with fwk_. The full key is shown once, when it is created. See API keys for creating and revoking them.
  • X-Account-ID must be the account the key belongs to. Otherwise the request fails with 403.
  • A key acts as the team member who created it, with their role in the account. It can’t do anything they can’t. See Team members and roles.
  • A key is either full access or limited to chosen permissions and projects. Keep it on your server; never put it in browser code. Missing permissions are refused with 403 and insufficient_scope. Account administration endpoints require full access as well as the appropriate role.

Main resources

AreaPathsWhat it covers
Projects/projectsProjects, and project secrets used by automations and connected apps
Forms/formsForms, their public schema and notification settings
Form versions/form_versionsDrafts and published versions: settings, pages and fields, validation, the process, and publishing
Entries/entriesListing, creating and updating entries, answers, team fields (metafields in the API), files, history and automation runs
Revisions/entry_revisionsEarlier versions of entries
Stages/entries/{entry_id}/stage_moves, /entries/{entry_id}/owner, /entries/{entry_id}/approvals, /my_workMoving entries between stages, owners, approvals and each person’s work
Automations/workflowsAutomations in a form version, their triggers and steps, and their runs
Data tables/data_tablesTables, columns and rows
Views/viewsSaved entry views and filters
Imports and exports/imports, /exports, /jobsCSV imports, exports and the background jobs that run them
Files/filesUploading and downloading files
Templates/templatesReusable content templates
Extensions/extensionsInstalled extensions, API connectors, environments, actions and action tests
References/referencesLooking up the values that can be inserted into steps and templates
Accounts and users/accounts, /users, /keysAccount details, team members, groups, invitations and scoped API keys
AI connections and audit/ai_connections, /accounts/{account_id}/ai_connections, /accounts/{account_id}/audit_eventsPersonal assistant connections, account-level connection management and the admin audit log
Billing/accounts/{account_id}/billingPlan status, and links to checkout and the billing portal

The API keeps some older names: automations are workflows and team fields are metafields.

Lists and pagination

Most list endpoints take limit and offset and return the items in data with paging details in meta:

{
  "data": [],
  "meta": { "limit": 20, "offset": 0, "total": 134 }
}

The default and largest limit differ between endpoints; the reference gives them. Public field-option searches page with a cursor and return a next_cursor. Entry views also support cursor pagination, including sorted views; use the reference for their response shape and pass the returned cursor to continue.

Changing drafts safely

Form versions, their fields, process and automations are changed on the draft, and go live when the draft is published. See Drafts and publishing.

Each change moves the draft on by one revision. Responses about a draft include its current revision, in the ETag header or a revision field. Send it back in If-Match on your next change: if someone else changed the draft in the meantime, nothing is saved and you get 412 with stale_revision and the current revision. Without If-Match the change is saved whatever the revision.

The draft’s changes can be read from GET /form_versions/{version_id}/changes. An isolated preview link can be requested from POST /form_versions/{version_id}/preview_link; it opens the draft without signing in for an hour, suppresses external effects like the builder preview, and its test entries are removed a day after their last change. Use the OpenAPI reference for the exact request and response shapes.

Account audit log

Account admins can read GET /accounts/{account_id}/audit_events using their session or a full-access API key. Results are newest first and support limit (default 50, maximum 200) and offset.

The log records API key creation and revocation, AI connections made, revoked or removed from an account, assistant writes and refusals, and form publications from the builder, API or assistant. It records the actor and assistant where relevant, targets and outcome rather than answer or secret values. It doesn’t cover every read or every ordinary app edit.

Errors

Errors return an HTTP status and a JSON body with a short code and a message:

{
  "error": "not_found",
  "message": "Entry not found"
}
StatusCommon codesMeaning
400bad_request, invalid_requestThe request is missing something or has a wrong value. When the request doesn’t match the specification, the body has a validation object saying which part failed.
401unauthorized, invalid_api_key, api_key_revokedThe key is missing, wrong or revoked.
402billing_inactiveThe account’s plan isn’t active, so this change isn’t allowed.
403forbidden, insufficient_scopeThe key can’t access this account, project or action, or lacks a required permission.
404not_foundThe item doesn’t exist or isn’t in this account.
409conflictThe item isn’t in a state that allows this, for example an automation that is switched off.
412stale_revisionThe draft changed since the revision you sent in If-Match.
429rate_limitedToo many requests. Wait and try again.
5xxinternal_errorSomething went wrong in FormWork. Retry later with backoff.

Use the code, not the message, in your logic. Messages are written for people and may change.

Start an automation from another system

An automation with the API request trigger can be started for an existing entry:

curl -X POST "https://app.useformwork.com/api/v1/workflows/WORKFLOW_ID/runs?form_version_id=VERSION_ID" \
  -H "Authorization: Bearer fwk_..." \
  -H "X-Account-ID: YOUR_ACCOUNT_ID" \
  -H "Content-Type: application/json" \
  -d '{"entry_id": "ENTRY_ID"}'

The automation must be switched on. Quick steps run while the request waits. From the first slow step, the rest continues in the background; the response returns the run in its current state. If it reaches a Wait step before returning, the run comes back as waiting and continues later. Run once for each entry can refuse a second trigger-started run with 409. See Triggers.

To check why an entry’s automations did or didn’t run, read GET /entries/{entry_id}/automation_check. It evaluates the current setup without starting a run. See Runs and testing.

Public form endpoints

The form people fill in uses its own endpoints under /entry_sessions. They don’t use an API key:

  • GET /forms/{form_id}/public_schema loads a published form before any entry exists.
  • POST /entry_sessions creates an entry and returns its entry_key. This is the only time the key is returned.
  • Every later call for that entry sends the key in the X-Formwork-Entry-Key header: saving answers, adding and removing repeating items, uploading files, button clicks, resume links and submitting.

If the form has a security check (CAPTCHA) turned on, creating an entry needs a valid CAPTCHA proof.

For a normal website, use the web component, which handles all of this for you. Use these endpoints only if you are building and maintaining your own complete form interface. Never use an entry key as account authentication, or an API key in a respondent’s browser.

Webhooks into FormWork

Built-in extensions can receive webhooks from their provider, for example Stripe at /extensions/stripe/webhooks. These endpoints check the provider’s signature instead of an API key. See Stripe.

Something missing or unclear? Tell us.