# FoxForm API & Product — Full Reference > Complete reference for FoxForm (foxform.app): a serverless forms & funnel/quiz builder. This single file documents base URLs, authentication, plans, the form/funnel data model, end-to-end recipes, and every PUBLIC management API endpoint with method, path, parameters and response shape. Machine spec: https://foxform.app/openapi.json · MCP server: https://mcp.foxform.app/mcp --- ## Base URLs | Surface | URL | Notes | |---|---|---| | Management API (production) | https://api.foxform.app | REST, JSON, API-key auth | | Management API (staging) | https://api-staging.foxform.app | mirror of production | | MCP server | https://mcp.foxform.app/mcp | Streamable HTTP MCP (same API key) | | Dashboard app | https://app.foxform.app | Next.js builder + analytics | | Marketing site | https://foxform.app | product + pricing | | Published forms (static HTML) | https://forms.foxform.app/{slug} | served from CloudFront | | Analytics beacon | https://beacon.foxform.app/b.gif | response collection (GIF beacon) | | OpenAPI spec | https://foxform.app/openapi.json | OpenAPI 3.0.3 | All API responses are JSON with the shape `{ "success": boolean, "data"?: ..., "message"?: string, "code"?: string }`. Errors carry a human `message` and a machine `code` (e.g. `PAID_FIELDS_REQUIRED`). --- ## Authentication The public API authenticates with a **FoxForm API key**. Generate one in the dashboard under **MCP & API** (read or read+write scope) — it's shown once (`ff_live_…`). Send it any of these ways: - Header `x-api-key: ff_live_…` - Header `Authorization: Bearer ff_live_…` - Query param `?key=ff_live_…` (handy for one-URL / MCP setups) **Scopes:** a *read* key authorizes `GET` calls; a *read+write* key also authorizes create / update / publish / delete. Two endpoints need no key: `GET /forms/slug/{slug}` (public published form) and `POST /responses` (server-to-server submit). The same key works with the FoxForm MCP server (`https://mcp.foxform.app/mcp`) — e.g. `claude mcp add --transport http foxform "https://mcp.foxform.app/mcp?key=ff_live_…"`. > Account, authentication (signup/login), billing, onboarding, organization and Google-Drive endpoints are **internal** to the FoxForm app and are not part of the public API. --- ## Plans & limits All plans are paid and start with a **14-day free trial**. There is no free tier. Billing currency follows the **language of the page**, not the visitor's country: the Portuguese site bills in BRL, the English and Spanish sites bill in USD. | | Pro | Premium | Unlimited | |---|---|---|---| | Price (USD) | USD 9/mo or USD 4.50/mo billed annually (USD 54/yr) | USD 19/mo or USD 9/mo annually (USD 108/yr) | USD 39/mo or USD 18/mo annually (USD 216/yr) | | Price (BRL) | R$49/mo or R$24.50/mo annually (R$294/yr) | R$99/mo or R$45.75/mo annually (R$549/yr) | R$199/mo or R$91.58/mo annually (R$1,099/yr) | | Responses/month | 10,000 (USD 2 per extra 1k) | 20,000 (USD 2 per extra 1k) | Unlimited (no overage) | | Analytics history | Unlimited | Unlimited | Unlimited | | Users | Up to 10 | Up to 10 | Unlimited | | Webhooks | Unlimited | Unlimited | Unlimited | | Media/Pro fields | Allowed | Allowed | Allowed | | Ads + "Powered by FoxForm" | Removed | Removed | Removed | Pro-only fields (block publish with HTTP 402 `PAID_FIELDS_REQUIRED` when the account has no active paid plan): **file_upload**, **picture_choice**, **video upload** (videoSource=upload), **testimonials with a photo**. --- ## Key architecture concepts 1. **Three rendering systems stay identical** — the builder preview, the live player and the backend static-HTML generator must render every field the same way. 2. **Published forms are static HTML** — publishing serializes the form to HTML, uploads it to S3 and invalidates CloudFront (`forms.foxform.app/{slug}`). The API is not in the hot path of a published form. 3. **Responses are BEACON-ONLY** — the player fires `beacon.foxform.app/b.gif` events; a backend log processor turns them into responses every ~10 minutes. `POST /responses` exists for server-to-server use but the player never calls it. Read responses and analytics via the API. --- ## The form & funnel data model A **Form** has: `title`, `description`, `slug`, `status` (draft|published|closed), `theme` (midnight|ocean|sunset|forest|lavender|minimal), `questions[]`, `thank_you_message`, `webhook_url`, `show_progress_bar`, `show_navigation_arrows`, `email_notifications`, `hide_branding` (Pro), timestamps. A **screen** (`QuestionConfig`) has `id`, `type`, `title`, `description`, `required`, and type-specific fields. Screen types: - **Flow:** `welcome`, `loading`, `result` (multi-card, each card shown by its own rule), `end`. - **Question:** `short_text`, `long_text`, `number` (models: plain/currency/percentage/decimal/height/weight), `email`, `phone`, `url`, `dropdown`, `checkboxes`, `multiple_choice`, `yes_no`, `rating` (17 icon shapes), `opinion_scale`, `nps`, `slider`, `date`, `file_upload` (Pro), `picture_choice` (Pro). - **Content:** `alert` (multi, per-item display rule + semantic/custom color), `testimonials` (photo = Pro; carousel/single/grid), `media` (Video: upload=Pro / URL / embed — Panda/YouTube/Vimeo), `timer` (until-date or fixed-period per visitor), `arguments`, `price` (single/selection/table), `progress_bar`. **Funnel layer** (all optional, backward-compatible): - Options become objects (`choices[]`) with `points` (→ `{{score}}`) and `value` (→ `{{variableName}}`). - `calc(...)` expressions evaluate variables (e.g. BMI `calc({{weight}}/(({{height}}/100)*({{height}}/100)))`). - **Conditional navigation** (`logic.conditionalNavigationV2`): OR of AND-groups, operators equal_to/not_equal_to/greater_than/greater_or_equal_than/less_than/less_or_equal_than/contains; first matching group's `then` wins. - **Conditional display** (`logic.display` and per-item `display`): same engine + optional `showAfterSeconds`. - **Rich text** titles/descriptions with `{{variable}}` interpolation at render time. The complete field-by-field schema (QuestionConfig, QuestionOption, QuestionStyle, QuestionLogic, ConditionGroup, ResultCard, AlertItem, ArgumentItem, TestimonialItem, PriceCard, etc.) is in the OpenAPI spec under `components.schemas`. --- ## Recipe: create and publish a form ```bash export FF=ff_live_your_key # dashboard → MCP & API (read+write scope) # 1) Create a form curl -s https://api.foxform.app/forms \ -H "x-api-key: $FF" -H 'Content-Type: application/json' \ -d '{"title":"My quiz","theme":"sunset","questions":[ {"id":"q1","type":"welcome","title":"Welcome","required":false,"buttonText":"Start"}, {"id":"q2","type":"multiple_choice","title":"Your level?","required":true, "choices":[{"id":"a","label":"Beginner","points":10},{"id":"b","label":"Advanced","points":40}]}, {"id":"q3","type":"result","title":"Your score: {{score}}","required":false, "resultCards":[{"id":"c1","title":"Advanced plan","display":{"enabled":true, "groups":[{"id":"g1","conditions":[{"id":"k","left":"{{score}}","operator":"greater_or_equal_than","right":"40"}]}]}}]} ]}' # → data.id # 2) Publish (generates static HTML + CDN). 402 if the account has no paid plan and the form has Pro-only fields. curl -s -X POST https://api.foxform.app/forms/$FORM_ID/publish -H "x-api-key: $FF" # → data.slug → public form at https://forms.foxform.app/{slug} ``` ## Recipe: read responses & analytics ```bash # Raw responses (paginated) curl -s "https://api.foxform.app/forms/$FORM_ID/responses?page=1&limit=50" -H "x-api-key: $FF" # Aggregated per-question stats curl -s "https://api.foxform.app/responses/stats/aggregated?formId=$FORM_ID" -H "x-api-key: $FF" # CSV export curl -s "https://api.foxform.app/forms/$FORM_ID/export?format=csv" -H "x-api-key: $FF" -o responses.csv ``` --- ## API Endpoints ### Forms - `GET /forms?page=&limit=` — List the user's forms (paginated). - `POST /forms` — Create a form (FormInput). - `GET /forms/{formId}` — Get a form by ID. - `PUT /forms/{formId}` — Update a form (FormInput). - `DELETE /forms/{formId}` — Delete a form. - `GET /forms/slug/{slug}` — **Public (no key).** Get a published form by public slug (for the player). - `POST /forms/{formId}/clone` — Duplicate a form. - `POST /forms/{formId}/publish` — Publish (static HTML + CDN). 200 → { data: Form + publicUrl }. 402 `PAID_FIELDS_REQUIRED` with `paidFields[]` when the account has no active paid plan and the form uses Pro-only fields. - `POST /forms/{formId}/unpublish` — Take a form offline. ### Responses - `GET /forms/{formId}/responses?page=&limit=` — List responses (paginated). - `GET /responses/{responseId}` — Get one response. - `POST /responses` — **Public (no key), server-to-server.** Submit a response. Body: form_id, answers, session_id?. (The player does NOT use this — see beacon-only note.) - `DELETE /responses/{responseId}` — Delete a response. ### Analytics - `GET /responses/stats/aggregated?formId=` — Aggregated per-question stats (overview, timeline, per-question choice/boolean/numeric/date/text/file stats). - `GET /forms/{formId}/export?format=csv|xlsx` — Export responses as a file. ### Account - `GET /user/profile` — The authenticated account behind the key (id, email, organization). Useful for a "who am I" check. --- ## Notes for integrators & LLM agents - Send your API key on every call (`x-api-key`, `Authorization: Bearer ff_live_…`, or `?key=`). A read key only allows GET; use a read+write key to create/update/publish. - Prefer reading responses via the API; do not assume `POST /responses` is how the form player submits (it is beacon-driven). - When building or editing forms programmatically, keep the three rendering systems in mind — use the documented `QuestionConfig` fields; unknown fields are ignored. - On `POST /forms/{formId}/publish` handle `402 PAID_FIELDS_REQUIRED`: inspect `paidFields[]` to know which fields to remove or which upgrade to offer. - The same key powers the MCP server (`https://mcp.foxform.app/mcp`) — tools mirror these endpoints (list/get/create/update/publish/unpublish forms, list/get responses, analytics, export).