REST API
The MCP tools over plain HTTP: one POST per tool, an OpenAPI 3.1 description, JSON errors, and the same credentials as the connector.
Everything the MCP connector can do is also a REST API, for scripts, automations, and agents that speak function calling rather than MCP. It is the same tool set on the same encrypted, workspace-scoped paths; only the transport differs.
Authenticate
Send a bearer token on every request: a workspace API token from Settings → API (owners and admins; the secret is shown once and starts with fbk_), or an OAuth 2.1 access token. Without one, GET /api/v1 answers 401 with a WWW-Authenticate header that points at the discovery metadata. The full walkthrough for agents, including scopes and revocation, is at https://formbear.app/auth.md.
Call a tool
Each tool is POST /api/v1/tools/{name}with its arguments as a JSON body (an empty body for tools that take none). The response is the tool’s result as JSON.
curl https://formbear.app/api/v1/tools/list_forms -X POST \
-H "Authorization: Bearer fbk_your_token_here" \
-H "Content-Type: application/json" -d "{}"
curl https://formbear.app/api/v1/tools/get_form -X POST \
-H "Authorization: Bearer fbk_your_token_here" \
-H "Content-Type: application/json" \
-d '{"publicId": "a1b2c3d4"}'GET /api/v1 returns the workspace the credential acts on and the tools it may call; GET /api/v1/tools describes every tool with its JSON Schema input. The OpenAPI 3.1 document at https://formbear.app/openapi.json carries the same, with one operation per tool (the operation id is the tool name), so it can be loaded straight into an agent framework or an API client.
Plans and scopes
Read tools work on every plan. Write tools (create, edit, publish, delete) need a Pro workspace and answer 403 pro_required on Free. OAuth credentials can be limited to forms:read, forms:write, responses:read, or responses:write; a missing scope answers 403 insufficient_scope. Tokens and OAuth connections share one budget: 120 requests per minute per credential, 240 per IP.
Paging through responses
list_responses takes limit (up to 200) and offset, and answers with total, offset, count, and the page. Keep adding count to offset until it reaches total. For aggregates, summarize_responses is cheaper than paging.
Safe retries
Send an Idempotency-Key header (any unique string, a UUID works) on a POST and you can retry it after a timeout without running the tool twice: the same key within 24 hours returns the first response again, marked Idempotency-Replayed: true. The same key with a different body is refused with 422 idempotency_key_reused.
Rate limits
120 requests per minute per credential and 240 per IP, shared with the MCP connector. Every response carries RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset (plus the X-RateLimit-* forms), so a client can pace itself; a 429 adds Retry-After.
Versioning and deprecation
The major version is in the path: /api/v1. Additive changes (new tools, new optional fields) ship in place and never change a version. A breaking change ships as a new version, and the version it replaces keeps working for at least six months. When that happens we announce it in the changelog and every response from the old version carries a Deprecation header (the announcement date) and a Sunset header (the date it stops responding). Nothing is deprecated today.
Errors
Every error, on every path (including one that does not exist), is a JSON object with a stable code, a message, and a hint that says what to do next.
| Status | Code | Meaning |
|---|---|---|
| 401 | unauthorized | Missing, invalid, or expired credential. |
| 403 | insufficient_scope | The OAuth grant lacks the scope the tool needs. |
| 403 | pro_required | A write tool on a Free workspace. |
| 403 | forbidden | Your role in the workspace does not allow it. |
| 400 | invalid_arguments | The body did not match the tool's input schema. |
| 400 | tool_error | The tool refused (for example, a change that would discard answers). |
| 422 | idempotency_key_reused | The Idempotency-Key was already used with a different body. |
| 404 | not_found | No such tool, form, block, or response. |
| 429 | rate_limited | Back off for Retry-After seconds. |
| 500 | internal_error | The tool failed on our side; retry later. |