Using the REST API
Use the REST API to create a form per client, update questions and conditional logic, or pull responses into your own system. The full API reference at /api includes copyable examples, request properties and an OpenAPI schema.
The base URL is https://app.formformform.com/api/v1. Create a named key in Settings → API Keys and send it as Authorization: Bearer <key>, with Accept: application/json.
Create an API key
Open Settings → API Keys. Read-only keys can read and export; read and write keys can also create, edit and delete forms, fields and responses. Keys apply to your account’s forms and responses; they cannot change account settings, passwords or billing.
- 1
Open https://app.formformform.com/settings/api-keys.
- 2
Give the key a name and choose Read and write to build forms.
- 3
Copy the secret when it appears. It is shown only once.
- 4
Store it in an environment variable or your agent’s secret configuration.
- 5
Send Authorization: Bearer <key> and Accept: application/json on requests. Use Content-Type: application/json for JSON bodies.
- 6
Check it with GET /api/v1/auth/me.
Keys do not expire automatically. Revoke an unused key in Settings to stop access immediately. POST /api/v1/auth/logout also revokes the credential making that call.
What the API covers
Under /api/v1, with your token: forms (list, create, read, update, delete, plus /duplicate and /toggle-active), a form's fields (create, update, delete, reorder), submissions (list, read, delete, export), and /api/v1/templates. Under /api/v1/public, with no token: read an active form and post an answer to it.
Which identifier goes where is what trips people up. A form is addressed by its uuid, already in the editor's address bar at /form-builder/<uuid>/edit, and a submission by its own uuid. Fields are the exception, taking the integer id the API hands back. Public routes use a third value, the short public_id from the share link.
Create with is_active: false, then edit the seeded fields and add new ones before the submit button. v1 ignores an inline fields array on form creation. Read back the form and PATCH is_active: true when it is ready. Field logic uses field UUIDs even though owner edit routes use numeric field IDs.
Creating a form needs only title; description, thank_you_message, redirect_url, notify_on_submission, is_active and template are optional, template taking a slug (default blank) to seed the fields. slug is derived from the title, public_id never changes, and both are ignored if sent.
Read responses
GET /api/v1/forms/<uuid>/submissions returns newest first, 25 to a page, per_page adjustable up to 100, with today_count alongside. Each row holds uuid, created_at, ip_address, user_agent and a values array — one entry per answered field, giving field_uuid, field_type, field_label and value. Fields left blank or hidden by conditional logic are absent rather than null, so read by field_uuid, never by position.
Fetching one submission adds prev and next uuids for walking the list, plus the armoured encrypted_payload on an encrypted form. /submissions/export streams the same CSV as the Submissions tab: a column per input field, then Submitted At, IP Address, Filled At and Was Offline.
A File Upload answer's value is the upload's uuid, not a link. The bytes come from the owner-only download route behind the Submissions tab, which reads a browser session rather than a bearer token.
Post a submission, uploads included
GET /api/f/<public_id> returns an active form with public_url, encryption metadata and its fields. Public field id values are UUID strings; use those UUIDs as keys in the fields object when posting answers. The owner API still uses numeric field IDs for field edits. POST /api/f/<public_id> accepts answers and is limited to 30 posts per minute per IP.
For uploads, POST multipart to /f/<public_id>/uploads with file and form_field_id set to the upload field’s UUID. Read the returned upload UUID, put it in the field’s answer and include it in upload_uuids. Both /api/f/<public_id> and /api/v1/public/forms/<public_id>/submissions attach those files. Encrypted forms require the client-side encryption flow and encrypted_payload instead of plaintext fields.
The server enforces 2 MB per file, even if settings.max_size is higher. Up to 50 upload references can accompany a submission. Payment forms require online checkout; the v1 public submission endpoint returns 409 for them.
Frequently asked questions
Is there an API key I can generate in Settings?
Yes. Open Settings → API Keys, name a key, choose its access and copy it once. Revoke it from the same page. Existing login-issued tokens still work but have broader account access.
Can I download the files people uploaded through the API?
No. The submission gives you each upload's uuid, but the download and preview routes are owner-only and read a browser session, not a bearer token. Open the submission in the Submissions tab instead. On an encrypted form the stored bytes are ciphertext anyway.
Can I set up a webhook through the API?
No. A webhook URL and its signing secret belong to one form and are set in the editor, under the Settings tab then the Integrations sub-tab. No endpoint reads or writes them, so a script that creates forms still leaves someone a trip to the editor.
Related articles
- Send submissions to a webhook
Point a form at a URL you own and every submission is posted to it as JSON, signed if you want it signed.
- Connect your forms to Zapier
Authorise your account once, then fire a Zap the moment one of your forms is submitted.
- Read and search your responses
Every response lands in the form's Submissions tab, where you can search it and open any one of them in full.
- Export responses to CSV
Download every response to a form as one CSV file, and know what each column holds before you open it.
Nothing here answering your question? The editor is quick enough that trying it is often faster than reading about it.
Open the editor