Formwork
Menu
Get started free Log in

API keys, requests and errors

Create an API key, send authenticated requests to the REST API, and handle pagination, errors and rate limits.

The REST API lets you read forms and responses, close or reopen a form, and manage webhooks from your own scripts. Everything is JSON over HTTPS.

The base URL is {APP_URL}/api/v1, for example https://formwork.movortech.com/api/v1. Replace the host with your Formwork address. Local development uses http://localhost/forms/api/v1.

Create a key

Open API keys from the account menu at the bottom of the sidebar, or go to /account/api. Enter a Name such as "Zapier" or "nightly export", choose the Access level (Read only or Read and write), and select Create key. Owners and editors can create write keys.

A key:

  • belongs to one workspace, the one you were in when you created it, and never reaches another;
  • is read (GET requests only) or write (also PATCH, POST and DELETE);
  • never gives more than you can do yourself: a viewer's write key still cannot change anything, and viewers can only create read-only keys;
  • is shown once, stored only as a SHA-256 hash, and starts with fwk_;
  • can be revoked on the same page if it leaks. Requests using it then fail.

You can have up to 10 active keys. Changing your password revokes all of your keys, and leaving a workspace revokes the keys you made in it.

The API is a plan feature. By default read keys need a plan with API read access and write keys need write access. See Default plan limits.

Send a request

Send the key as a bearer token:

export FORMWORK_KEY=fwk_0123456789abcdef0123456789abcdef01234567
curl https://formwork.movortech.com/api/v1/me -H "Authorization: Bearer $FORMWORK_KEY"

There are no cookies, sessions or CSRF tokens on the API. Never put a key in a URL.

GET /me

Who the key acts as.

curl -s https://formwork.movortech.com/api/v1/me -H "Authorization: Bearer $FORMWORK_KEY"
{
  "data": {
    "user": { "id": 1, "name": "Demo Maker", "email": "demo@formwork.test" },
    "workspace": { "id": 1, "name": "Demo Maker's workspace", "role": "owner" },
    "key": { "id": 3, "name": "Zapier", "prefix": "fwk_22cd", "scope": "read" }
  }
}

Responses and errors

Success wraps the payload in data. Lists add meta:

{ "data": [ ... ], "meta": { "page": 1, "per_page": 25, "total": 135 } }

Errors always look like this, with a matching HTTP status:

{ "error": { "code": "not_found", "message": "Form not found." } }
StatuscodeWhen
400bad_requestMalformed request
401unauthorizedMissing, wrong or revoked key (also sent with WWW-Authenticate: Bearer)
403forbiddenRead-only key on a write route, or your role in the workspace does not allow it
404not_foundNo such endpoint, or a form or response outside the key's workspace. Both look the same on purpose
422validation_failedBad parameter or body. error.details.field names the field when there is one
429rate_limitedSee below. Retry-After is in seconds
500server_errorOur side. The message is generic, details go to the server log

A form or response in another workspace gives the same 404 as one that does not exist, so a key cannot be used to find out what exists elsewhere.

Rate limits

120 requests per minute per key. Failed authentication is limited separately, at 30 per minute per IP address, to make key guessing pointless. Both answer 429 with Retry-After: 60.

When you get a 429, wait the number of seconds in Retry-After and try again, rather than retrying at once.

Pagination

List endpoints take page (from 1) and per_page (1 to 100, default 25) and return meta.total.

curl -s "https://formwork.movortech.com/api/v1/forms?page=2&per_page=50" -H "Authorization: Bearer $FORMWORK_KEY"

Timestamps are ISO 8601 in UTC.

Versioning

This is v1. Additive changes such as new fields and new endpoints can land at any time, so ignore fields you do not know. Anything that breaks existing clients will ship as /api/v2.

Next

Updated Sep 30, 2026