---
title: "Using the REST API | formformform"
description: "Trade your sign-in for a bearer token, then list forms, pull submissions and post answers from your own code. The endpoints that actually exist."
url: "https://www.formformform.com/help/rest-api"
type: static
generatedAt: "2026-08-23T18:56:58.727Z"
---
# Using the REST API

A webhook pushes a submission to you the second it lands. The REST API works the other way round: your code asks when it suits — a nightly job pulling yesterday's callout reports into a rota system, a script that spins up a fresh intake form per client.

Everything sits under https://app.formformform.com. Authentication is a bearer token you fetch by signing in over the API; there is no API-key screen in Settings, and the token reaches as far as your account does. Resources come back wrapped in a data key.

## Sign in for a token

You exchange your account password for a token once, then send that token on every later call. Nothing expires on a timer, so a token left in a cron job keeps working until POST /api/v1/auth/logout revokes it — and that call revokes only the token that made it.

1.  1
    
    POST to https://app.formformform.com/api/v1/auth/login with a JSON body holding email and password.
    
2.  2
    
    Add device\_name to label the token — "rota-sync", say. Left out, it is labelled "mobile".
    
3.  3
    
    With two-factor authentication on, the reply holds two\_factor: true and a challenge\_token instead. POST that token and your six-digit code to /api/v1/auth/two-factor-challenge; a recovery\_code works in place of code, and is spent once used.
    
4.  4
    
    Store token where your code can read it and others cannot.
    
5.  5
    
    Send it as Authorization: Bearer <token>, alongside Accept: application/json.
    
6.  6
    
    Confirm it works with GET /api/v1/auth/me.
    

Five wrong passwords for the same email and IP inside a minute returns 429. The account's email must also be verified: an unverified account gets 403 on forms and submissions even though login itself succeeds.

## 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.

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 the live form: title, thank-you message, and every field with its integer id, type, options and is\_required flag. POST back to the same address with a fields object keyed by those ids. The reply carries submission\_uuid plus the thank-you message and any redirect. Throttled at 30 posts a minute per IP.

A file goes up on its own, before the submission exists. POST it as multipart to /f/<public\_id>/uploads with form\_field\_id set to the File Upload field, read the uuid from the reply, then send that uuid twice: as the field's value inside fields, and in the upload\_uuids array. Up to 50 files per submission. An encrypted form takes an armoured encrypted\_payload in place of fields.

2 MB per file, enforced on the server: a field's settings.max\_size accepts a larger number through the API, but anything above 2 MB is still refused. Prefer /api/f/<public\_id> over /api/v1/public/forms/<public\_id>/submissions when the form has a File Upload field — the v1 route stores the answers but does not attach the uploads.

## Frequently asked questions

Is there an API key I can generate in Settings?

No. Settings holds Profile, Password, Two-Factor Auth, Appearance, Billing and Zapier, and none of them mints a key. The token comes from POST /api/v1/auth/login, with device\_name as its only label. Call /api/v1/auth/logout with a token to revoke it.

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](https://www.formformform.com/help/webhooks)
    
    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](https://www.formformform.com/help/connect-zapier)
    
    Authorise your account once, then fire a Zap the moment one of your forms is submitted.
    
-   [Read and search your responses](https://www.formformform.com/help/read-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](https://www.formformform.com/help/export-to-csv)
    
    Download every response to a form as one CSV file, and know what each column holds before you open it.
    

-   [Webhooks & API →](https://www.formformform.com/features/webhooks-api)
-   [Automations →](https://www.formformform.com/automations)

Nothing here answering your question? The editor is quick enough that trying it is often faster than reading about it.

[Open the editor](https://app.formformform.com/register?utm_source=landing&utm_medium=bottom&utm_campaign=help&utm_content=rest-api)
