A submission is one respondent’s answers to one published version of a form. The Platform API can list submissions, read one submission, or stream every matching submission as a CSV or JSONL attachment.
| Operation | What it does |
|---|---|
GET / | Lists submissions by creation time, newest first by default. |
GET / | Reads one submission, including signed URLs for uploaded files. |
GET / | Streams matching submissions as a CSV or JSONL attachment. |
All three operations need forms:read and cost one credit each. A Private Template never collects submissions: its list is empty, its CSV export holds only the metadata header row, its JSONL export is empty, and reading a submission ID through it returns 404 NOT_FOUND.
The submission resource
Section titled “The submission resource”Every list item is the same object returned by the single-submission operation:
{ "public_id": "Sb01EventReg2026Ab3cDe", "form_public_id": "Ev3ntReg01", "status": "completed", "published_version": 3, "created_at": "2026-10-01T09:00:00.000Z", "updated_at": "2026-10-01T09:00:00.000Z", "answers": { "nNam": "Ada Lovelace", "nTck": { "value": "speaker", "label": "Speaker" } }, "hidden_values": { "utm_source": "conference-newsletter" }}Every field of a submission is listed in the response of Read a submission.
An unanswered question is absent from answers. Answer shapes follow the question type:
- Text, number, and yes-or-no answers are JSON strings, numbers, or booleans.
- A single choice is
{ "value": "speaker", "label": "Speaker" }; a multiple choice is an array of those objects. - A matrix answer is an array of
[row_value, selected_option]pairs. - A File Upload answer is always an array of file objects, even when the respondent uploaded a single file. A file that has not finished uploading is left out of the array.
Each file object carries type: "file", the file’s public_id, name, size_bytes, mime_type, and a download_url signed for five minutes. Fetch the submission again when a URL has expired, and use public_id, not the URL, as the file’s identity.
Listing submissions
Section titled “Listing submissions”GET /v1/forms/{form_public_id}/submissions returns completed submissions, newest first. Use order=asc for the oldest first:
curl -s -H "Authorization: Bearer $FOMR_TOKEN" \ "https://platform.fomr.io/v1/forms/Ev3ntReg01/submissions?limit=100"{ "data": [ { "public_id": "Sb01EventReg2026Ab3cDf", "form_public_id": "Ev3ntReg01", "status": "partial", "published_version": 3, "created_at": "2026-10-01T09:00:00.000Z", "updated_at": "2026-10-01T09:00:00.000Z", "answers": { "nNam": "Grace Hopper" }, "hidden_values": { "utm_source": "partner" } }, { "public_id": "Sb01EventReg2026Ab3cDe", "form_public_id": "Ev3ntReg01", "status": "completed", "published_version": 3, "created_at": "2026-10-01T09:00:00.000Z", "updated_at": "2026-10-01T09:00:00.000Z", "answers": { "nNam": "Ada Lovelace", }, "hidden_values": { "utm_source": "conference-newsletter" } } ], "next_cursor": "eyJpZCI6IlNiMDFFdmVudFJlZzIwMjZBYjNjRGUiLCJrIjoiMjAyNi0xMC0wMVQwOTowMDowMC4wMDAwMDBaIiwibyI6ImRlc2MiLCJzIjoiY3JlYXRlZF9hdCJ9"}The response is { data, next_cursor }. Pass next_cursor back as cursor until it is null, and keep the same order while paging. Submissions created at the same instant are neither skipped nor repeated. Conventions covers cursors in full.
Filters
Section titled “Filters”Every filter is listed with the query parameters of List submissions.
since and until are inclusive. You can combine all four filters; for example, this reads completed submissions for version 3 that started during September:
curl -s -H "Authorization: Bearer $FOMR_TOKEN" \ "https://platform.fomr.io/v1/forms/Ev3ntReg01/submissions?published_version=3&since=2026-09-01T00%3A00%3A00.000Z&until=2026-09-30T23%3A59%3A59.999Z"Partial responses and plans
Section titled “Partial responses and plans”Completed submissions are available on every plan. status=partial and status=all need the Pro plan’s Partial Response Access feature, and so does reading a Partial Response by its ID. A Free organization gets 402 PRO_FEATURE_REQUIRED with feature: "partial_response_access" and path: "status".
A Partial Response is the same resource as a completed submission, with status: "partial" and only the answers saved so far.
Reading one submission
Section titled “Reading one submission”Use the submission public ID from a list response to read one submission:
curl -s -H "Authorization: Bearer $FOMR_TOKEN" \ "https://platform.fomr.io/v1/forms/Ev3ntReg01/submissions/Sb01EventReg2026Ab3cDe"The submission must belong to the form in the URL and to your organization. A missing, deleted, or foreign submission returns the same 404 NOT_FOUND.
Exporting submissions
Section titled “Exporting submissions”GET /v1/forms/{form_public_id}/submissions/export streams every matching submission as an attachment. There is no job to poll: rows start arriving immediately. CSV is the default:
curl -sS -H "Authorization: Bearer $FOMR_TOKEN" \ -o submissions-Ev3ntReg01.csv \ "https://platform.fomr.io/v1/forms/Ev3ntReg01/submissions/export"In CSV:
- The first six columns are
submission_public_id,form_public_id,status,published_version,created_at, andupdated_at. Answer columns follow, then Hidden Values ashidden_values.{key}. - Answer columns are the columns of the app’s Responses table: the questions of the newest published version in form order, then questions only older versions carry, kept when at least one exported submission answered them.
- Answers read as they do in the Responses table. A choice answer is its label, with several joined by commas. A Checkbox or Switch answer is
YesorNo. Dates and times use the question’s date and time format, and a phone number is written in international format. A File Upload answer is its download URLs, one per line, signed for five minutes. - A Matrix question gets one column per row, headed
{question label}: {row label}, or{node_public_id}.{row_value}withheader=public_id. Each cell holds the label of the option chosen for that row. A row that only an older version carries gets a column when an exported submission answered it. - Every value is quoted. A value beginning with
=,+,-,@, a tab, or a carriage return gains a leading apostrophe, so spreadsheet software treats respondent input as text.
Ask for JSON Lines with format=jsonl. Each line is one JSON object with the same shape as a submission:
curl -sS -H "Authorization: Bearer $FOMR_TOKEN" \ -o submissions-Ev3ntReg01.jsonl \ "https://platform.fomr.io/v1/forms/Ev3ntReg01/submissions/export?format=jsonl"The export accepts the same status, since, until, and published_version filters as the list, with the same Pro gate on Partial Responses. Rows come newest published version first, then newest submission first within a version. The response streams until every row is sent or the client disconnects.
An organization can run two exports at a time, counted across the whole organization: every token, member, and parallel job shares the two slots. A third returns 503 SERVICE_UNAVAILABLE with retry_after_seconds and the matching Retry-After header. A slot frees as soon as an export finishes or its client disconnects.
Every query parameter is listed under Export submissions.
Answer headers
Section titled “Answer headers”header=label, the default, keys CSV answer columns and JSONL answers entries by each question’s label in the newest published version, as the Responses table shows it: a piped answer appears as [Question], and a question without text uses its placeholder. With published_version, it uses that version’s labels instead:
{ "public_id": "Sb01EventReg2026Ab3cDe", "form_public_id": "Ev3ntReg01", "status": "completed", "published_version": 3, "created_at": "2026-10-01T09:00:00.000Z", "updated_at": "2026-10-01T09:00:00.000Z", "answers": { "Full name": "Ada Lovelace", "Ticket type": { "value": "speaker", "label": "Speaker" } }, "hidden_values": { "utm_source": "conference-newsletter" }}Every field of a row is listed in the response of Export submissions.
Use header=public_id for stable keys such as nNam. It is the safer choice for an automated import, because labels can change. In label mode, a question with neither text nor a placeholder uses its node public ID, and a label that is already taken gets the node public ID appended in brackets, so every heading stays unique.
Hidden values
Section titled “Hidden values”hidden_values holds the values captured with this response, not the defaults currently set on the form. Keys come from the Form Blueprint’s hidden_values declarations and stay separate from question answers. The submission resource and every export row include them. See Hidden values for declaring keys and defaults.
Errors
Section titled “Errors”| Status | Code | When |
|---|---|---|
400 | INVALID_INPUT | A filter, date range, version, limit, cursor, format, or header is invalid. |
402 | PRO_FEATURE_REQUIRED | A Free organization asks for Partial Responses. |
403 | FORBIDDEN | The member’s role lacks forms:read. |
404 | NOT_FOUND | The form or submission is missing, deleted, foreign, or mismatched. |
429 | API_COOLDOWN or TOO_MANY_REQUESTS | The organization lacks the credits for the call, hit its minute cap, or is in API Cooldown. See Rate limits and plans. |
503 | SERVICE_UNAVAILABLE | Only on export: the organization already has two exports running, from any token or member. Retry after retry_after_seconds. |
The full error shapes and retry rules are on Errors.