This walkthrough takes you from an empty terminal to a form and its first response. You will create a Personal Access Token, confirm what it can do, post a Form Blueprint, publish it, submit a response, and read that submission. Every step uses curl; only steps 1 and 7 need a browser.
What you need
Section titled “What you need”- A Fomr account, and a role in your organization that can create and publish forms. The walkthrough tells you how to check.
curl, andjqto pull values out of responses.
The form you will create
Section titled “The form you will create”The examples throughout this section of the help center use one form: the Docs Example Form, an event registration form with an intro page, a question page, and a final page. Save this document as event-registration.json — step 4 posts it as-is.
{ "public_id": "Ev3ntReg01", "name": "Event registration", "is_template": false, "hidden_values": [ { "key": "utm_source", "default_value": null } ], "logic_rules": [], "calculated_values": [], "pages": [ { "public_id": "pIn1", "name": "Welcome", "type": "intro", "is_hidden": false, "is_entrypoint": true, "is_auto_jump_next_page": false, "nodes": [ { "public_id": "nWlc", "parent_node_id": null, "column_index": null, "sort_order": 1, "is_hidden": false, "type": "text", "meta": { "content": "<p>Join us for the Acme Events community meetup.</p>" } }, { "public_id": "nSt1", "parent_node_id": null, "column_index": null, "sort_order": 2, "is_hidden": false, "type": "actions", "meta": { "label": "Start", "is_page_actions_override": false, "back_button_label": "Back" } } ] }, { "public_id": "pQ01", "name": "Your details", "type": "normal", "is_hidden": false, "is_entrypoint": false, "is_auto_jump_next_page": false, "nodes": [ { "public_id": "nNam", "parent_node_id": null, "column_index": null, "sort_order": 1, "is_hidden": false, "type": "text_input_short", "meta": { "question": "<span>Full name</span>", "placeholder": "Jane Doe", "validation": { "required": true } } }, { "public_id": "nEml", "parent_node_id": null, "column_index": null, "sort_order": 2, "is_hidden": false, "type": "text_input_email", "meta": { "question": "<span>Email address</span>", "validation": { "required": true } } }, { "public_id": "nTck", "parent_node_id": null, "column_index": null, "sort_order": 3, "is_hidden": false, "type": "multiple_choice", "meta": { "question": "<span>Ticket type</span>", "select_options_data": [ { "value": "general", "label": "General", "label_formatted": "<span>General</span>" }, { "value": "student", "label": "Student", "label_formatted": "<span>Student</span>" }, { "value": "speaker", "label": "Speaker", "label_formatted": "<span>Speaker</span>" } ], "select_options_randomize": false, "select_options_selection": "single", "multiple_choice_variant": "alphabet", "validation": { "required": true } } }, { "public_id": "nDie", "parent_node_id": null, "column_index": null, "sort_order": 4, "is_hidden": false, "type": "checkbox_choice", "meta": { "question": "<span>Dietary needs</span>", "select_options_data": [ { "value": "vegetarian", "label": "Vegetarian", "label_formatted": "<span>Vegetarian</span>" }, { "value": "vegan", "label": "Vegan", "label_formatted": "<span>Vegan</span>" }, { "value": "gluten_free", "label": "Gluten-free", "label_formatted": "<span>Gluten-free</span>" } ], "select_options_randomize": false, "select_options_selection": "single", "select_options_style": "checkbox", "validation": { "required": false } } }, { "public_id": "nCmp", "parent_node_id": null, "column_index": null, "sort_order": 5, "is_hidden": true, "type": "text_input_short", "meta": { "question": "<span>Company</span>", "validation": { "required": false } } }, { "public_id": "nSub", "parent_node_id": null, "column_index": null, "sort_order": 6, "is_hidden": false, "type": "actions", "meta": { "label": "Register", "is_page_actions_override": false, "back_button_label": "Back" } } ] }, { "public_id": "pFn1", "name": "Thank you", "type": "final", "is_hidden": false, "is_entrypoint": false, "is_auto_jump_next_page": false, "nodes": [ { "public_id": "nThx", "parent_node_id": null, "column_index": null, "sort_order": 1, "is_hidden": false, "type": "thank_you", "meta": { "thank_you_title": "<h1 style=\"text-align: center\">See you there!</h1>", "thank_you_description": "<p style=\"text-align: center\">We sent a confirmation to your email address.</p>", "thank_you_icon": "check_animated" } } ] } ], "settings": { "email_notifications_enabled_owner": 1, "email_notifications_enabled_respondent": 0, "email_notifications_recipients_owner": [ ], "email_notifications_recipient_respondent": null }, "properties": { "show_branding": 1, "auto_jump_next_page": 1, "allow_back_navigation": 1, "meta_title": null, "meta_description": null, "meta_index": 1, "meta_favicon_url": null, "meta_ogimage_url": null, "partial_response_capture_enabled": 0 }, "access": { "is_closed": 0, "close_at": null, "open_at": null, "duplicate_response_rule": null }, "styles": { "theme_id": null, "override_properties": null }}The document is a Form Blueprint, the single JSON shape that describes a whole form. Form Blueprint explains every part of it; for now it is enough to know that you send one and get a form back.
The walkthrough
Section titled “The walkthrough”-
Create a Personal Access Token.
Open Settings > API in the app, create a token, and give it a name that says where it will run. The full value is shown once and never again — after that only a Token Hint like
fomrt_…Ab9zremains, so copy it now. -
Configure authentication.
Typing a token into a command leaves it in your shell history, so read it into your environment instead. Run this, paste the token, and press Enter:
Terminal window read -rs FOMR_TOKENexport FOMR_TOKEN -
Confirm the token works.
GET /v1/meis the call to make first. It answers with your organization, the member the token acts as, and that member’s current permissions.Terminal window curl -s https://platform.fomr.io/v1/me \-H "Authorization: Bearer $FOMR_TOKEN" | jq '.member.permissions'Two values in there decide whether the rest of this page will work:
formsmust bewrite, andform_publishmust betrue. If either is missing, ask an organization Admin to create the token: only Admins can create and use Personal Access Tokens. A401instead means the token is wrong — check you copied the whole value. -
Create the form.
POST /v1/formstakes the blueprint and answers201with the new form. Keep thepublic_idit assigns; every later call needs it.Terminal window FORM_ID=$(curl -s -X POST https://platform.fomr.io/v1/forms \-H "Authorization: Bearer $FOMR_TOKEN" \-H "Content-Type: application/json" \--data-binary @event-registration.json | jq -r '.public_id')echo "$FORM_ID"Ev3ntReg01Your form gets its own ID;
Ev3ntReg01is the one the documentation examples use. The full response also carries the stored document underdraft, with every ID the server minted, so a real client can persist those without a second call. -
Check that the form is ready to publish.
POST …/validateruns Publish Validation against the draft and answers200either way. It writes nothing and publishes nothing.Terminal window curl -s -X POST -H "Authorization: Bearer $FOMR_TOKEN" \"https://platform.fomr.io/v1/forms/$FORM_ID/validate"POST /v1/forms/Ev3ntReg01/validate {"valid": true,"issues": [],"would_publish_version": 1,"share_url": "https://fomr.io/s/Ev3ntReg01"}valid: trueand an emptyissuesarray mean the next step will succeed. If you edited the document and something is missing, each entry inissuesnames the page and block to fix. -
Publish it.
POST …/publishpublishes the stored draft as the next version and answers the form. The share link is inurls.share.Terminal window curl -s -X POST -H "Authorization: Bearer $FOMR_TOKEN" \"https://platform.fomr.io/v1/forms/$FORM_ID/publish" | jq -r '.urls.share'https://fomr.io/s/Ev3ntReg01 -
Open and submit the form.
Paste that URL into a browser, fill in the form, and submit it. The API lists completed submissions by default, so finish the form before moving on.
-
Read the submission.
List the newest completed submission, keep its public ID, then read the full resource:
Terminal window SUBMISSION_ID=$(curl -s -H "Authorization: Bearer $FOMR_TOKEN" \"https://platform.fomr.io/v1/forms/$FORM_ID/submissions?limit=1" \| jq -r '.data[0].public_id')curl -s -H "Authorization: Bearer $FOMR_TOKEN" \"https://platform.fomr.io/v1/forms/$FORM_ID/submissions/$SUBMISSION_ID"GET /v1/forms/Ev3ntReg01/submissions/Sb01EventReg2026Ab3cDe {"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"}}answersis keyed by each question’s stable node public ID:nNamis the name question in the Docs Example Form.hidden_valuescarries any Hidden Values captured when the response began. Submissions covers filters, Partial Responses, file downloads, and exports.
What just happened
Section titled “What just happened”Only two of those calls changed anything: the create and the publish.
| Call | What it changed |
|---|---|
GET / | Nothing. It reads the identity behind the token. |
POST / | Created the form and stored the document as its draft. Nothing was live yet. |
POST …/ | Nothing. It reports whether a publish would succeed. |
POST …/ | Published version 1. From here the share link serves. |
GET …/ | Nothing. It lists submissions for the form. |
GET …/ | Nothing. It reads one submission. |
Creating a form and writing its draft never change what respondents see. Only publish does that. Forms covers the rest of the lifecycle: taking a form offline again, closing it, scheduling when it opens and closes, and moving it to Trash.
Where to go next
Section titled “Where to go next”- Change the form. Pull the document with
GET …/draft, edit it, and send the whole thing back withPUT …/draft. Pages and blocks are matched by ID, so IDs and answer history survive the write. See Replacing the draft. - Learn the document. Form Blueprint covers pages, nodes, settings, and every node type.
- Learn the shared rules. Conventions covers content types, dates, identifiers, and how lists page.
- Work with answers. Submissions covers completed and partial responses, signed file downloads, and streamed CSV or JSONL exports.
- Handle failure properly. Errors has the code catalog, the extra fields each code carries, and the retry rules.