AI assistants like Claude or ChatGPT connect to Formwork by signing in through your browser. A CI pipeline, a script or a command-line tool cannot do that. For those, Formwork has API tokens: a secret string you create once in Formwork and give to the machine, which then talks to the same MCP server on your behalf.
Typical uses:
- A release pipeline that files its test results and screenshots into a Formwork test run after every build.
- A script that bulk-creates or updates items from a spreadsheet.
- Claude Code or Codex on a machine where a browser sign-in is impractical.
Creating a token
- In Formwork, switch to the company the token should work in. A token belongs to exactly one company and cannot be moved.
- Go to Settings → API access and click New token. You'll be asked to re-enter your password first.
- Give the token a name that says what uses it, e.g. GitHub Actions release pipeline.
- Pick the access level:
- Read: query, read, search and trace.
- Read and write: additionally create, update, upload files and send items for review. If your role in the company is viewer, only read is offered.
- Pick how long the token should live: 30 days, 90 days or 1 year.
- Click Create token. Formwork shows the token once. Copy it now and store it where your pipeline or script keeps its secrets. If you lose it, revoke it and create a new one.
Tokens look like this:
fw_G80tbc3vgC4uCsPFNK_wC72MZoWeYEjb9tVlk0ivYhE
What a token can do
A token acts as you in the company it belongs to. It can never do more than your own account can: a read-and-write token used by a viewer still cannot change anything, and a token never approves, releases, rejects, archives or deletes anything. If your role in the company changes later, the token follows it. If you leave the company, the token stops working.
Using a token
Send it as a bearer token to https://app.openregulatory.com/mcp. Every request is a JSON-RPC message; no session or handshake is needed.
From the command line
curl -X POST https://app.openregulatory.com/mcp \
-H "Authorization: Bearer fw_..." \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
That lists the tools the token can use: five for a read token, nine for a read-and-write token. Calling a tool looks like this:
curl -X POST https://app.openregulatory.com/mcp \
-H "Authorization: Bearer fw_..." \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "query_formwork_resources",
"arguments": { "resource_type": "capa", "filters": { "status": "open" } }
}
}'
The result comes back as JSON under result.structuredContent. For the full list of tools, resource types, filters and fields, see MCP tools reference. The Accept header must list both application/json and text/event-stream, even though the answer is plain JSON.
From Claude Code
claude mcp add --transport http formwork https://app.openregulatory.com/mcp \
--header "Authorization: Bearer fw_..."
No sign-in step: Claude Code sends the token with every request.
From Codex CLI
Codex reads the token from an environment variable rather than storing it in its config:
export FORMWORK_TOKEN=fw_...
codex mcp add formwork --url https://app.openregulatory.com/mcp --bearer-token-env-var FORMWORK_TOKEN
From a GitHub Actions workflow
Store the token as a repository secret (e.g. FORMWORK_TOKEN) and use it from a step:
- name: File test results in Formwork
env:
FORMWORK_TOKEN: ${{ secrets.FORMWORK_TOKEN }}
run: |
curl -sS -X POST https://app.openregulatory.com/mcp \
-H "Authorization: Bearer $FORMWORK_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d @update-test-run.json
where update-test-run.json holds the tools/call message for the update you want to make.
Uploading files
Screenshots, PDFs and other evidence are uploaded in two steps: ask the upload_formwork_file tool for an upload URL by passing the file's size, then POST the file to that URL. The URL is valid for 10 minutes, works once, and needs no token:
# 1. ask for an upload URL
curl -sS -X POST https://app.openregulatory.com/mcp \
-H "Authorization: Bearer $FORMWORK_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"upload_formwork_file","arguments":{"filename":"report.pdf","byte_size":184223}}}'
# 2. upload the file to the upload_url from the answer
curl -sS -X POST "<upload_url>" -F "[email protected]"
The second call answers with an attachment_signed_id. Pass it in attachment_signed_ids when updating a system test run, user test run, document or record to attach the file. The id is valid for 24 hours; upload and attach in the same run.
Managing tokens
Settings → API access lists your tokens with their company, access level, creation and expiry dates, when each was last used, and its status. From there you can:
- Extend an active token by 30 days, 90 days or a year. A token never runs more than a year from today. Extend before it expires: an expired token stays expired, create a new one instead.
- Revoke a token. Anything using it stops working immediately.
Company admins see every token bound to their company, with its owner, under Settings → API access in the company section, and can revoke any of them.
Keeping tokens safe
- Treat a token like a password: store it in your CI's secret store or a password manager, never in a repository or a chat message.
- All tokens start with
fw_, which makes them easy to catch with a secret-scanning rule. - Give each machine its own token with a name that says what it is for. When one pipeline goes away, revoke just that token.
- Pick the shortest lifetime that works. Read access is enough for anything that only reports.
- If a token has leaked, revoke it in Formwork and create a new one. Revocation takes effect instantly.
Troubleshooting
- 401 Unauthorized: the token was revoked, has expired, you are no longer a member of its company, or the
Authorizationheader is missing or malformed. Create or extend a token in Formwork and check the header readsBearer fw_.... - "Tool not found" for
create_formwork_resourceor another writing tool: the token is read-only. Create a read-and-write token. - "you do not have edit access for this company" or a
forbiddenanswer: the token works, but your own account cannot make that change. Ask a company admin about your role. - The response is not JSON: make sure the
Acceptheader lists bothapplication/jsonandtext/event-stream. - The upload URL is rejected: it expired (10 minutes), was already used, or the file is larger than the
byte_sizeyou declared. Ask for a fresh URL.
As always, reach out in the customer support chat in the bottom right corner if you run into any trouble.