Build a form by pasting JSON
Create or replace a form's questions in one step by pasting a JSON document, including one written by an LLM.
Building a long form one question at a time works, but it's slow. Importing skips that: paste a JSON document describing every page, question, choice, and follow-up rule, and the whole form is built at once. Copy this article with the button above or below, paste it into an LLM along with a description of the form you want, and it can write that document for you.
Find importing from Create form, under Paste JSON, or from Import JSON in the form editor's page bar.
- On Create form, importing builds a brand-new draft form, the same as choosing a starting point does.
- In the editor, Import JSON replaces the current draft's pages and questions entirely with what's in the pasted document — anything in the draft that isn't in the JSON is gone. A form you've already published is unaffected until you publish again; respondents keep seeing the last published version until then.
Either way, the document is checked in full before anything is saved. If any part of it doesn't fit the format below, nothing is created or changed, and the specific problem is reported.
The document
{
"title": "Volunteer sign-up",
"schema": { "description": "...", "pages": [] }
}
title is optional, up to 150 characters. Leave it out and the title typed
into the page's own title field is used instead; replacing a form in the
editor keeps its current title.
schema holds the form itself. description is optional intro text under the
title (1000 characters). completion is optional — { "heading": "...", "message": "..." } — shown after a response is submitted. payment is
optional too; leave it out unless the form should charge for a response, which
still needs Stripe connected before the form can be published (see
Require payment for a Form response). pages is required, and needs at least one page:
{ "title": "...", "blocks": [] }.
A page's blocks run in order, and are one of four kinds: { "type": "heading", "text": "..." }, { "type": "paragraph", "text": "..." }, { "type": "divider" }, or a question: { "type": "field", "field": { ... } }.
Questions
{
"key": "attending",
"type": "yes_no",
"label": "Will you attend?",
"required": true
}
label is the question text, required, up to 500 characters. description is
optional help text shown underneath it. required is true or false,
defaulting to false. key is a short made-up name for this question — it
isn't part of the finished form, and only matters if a later question follows
up on this one, covered below.
options lists the choices offered, and is required — at least one — for
radio, checkboxes, and select; leave it out for every other type.
| Type | Answer | |
|---|---|---|
text |
one line | |
textarea |
multiple lines | |
email |
an email address | checked as valid |
phone |
one line | not checked |
url |
a web address | checked as valid |
number |
one line | not range-checked |
radio |
one of several choices | needs options |
checkboxes |
any of several choices | needs options |
select |
one choice, as a dropdown | needs options |
yes_no |
Yes or No | built in — don't set options |
date |
a date | |
time |
a time | |
rating |
1 to 5 | built in — don't set options |
file |
one uploaded file |
Follow-up questions
Give an earlier question a key, then point a later question's visibility
at it with fieldKey:
{
"key": "has_guests",
"type": "yes_no",
"label": "Bringing any guests?",
"required": true
},
{
"key": "guest_names",
"type": "textarea",
"label": "Guest names",
"visibility": [
{ "fieldKey": "has_guests", "operator": "equals", "value": "Yes" }
]
}
This is the same conditional visibility the editor's own Conditional
visibility panel offers — see Build, publish, and share a form. Every
fieldKey must match a key used elsewhere in the document, or the import is
rejected. operator is equals, not_equals, contains, answered, or
not_answered. A question can list more than one rule; add
"visibilityMode": "any" alongside visibility when matching just one of
them should be enough, instead of requiring all of them.
A complete example
{
"title": "Team retreat RSVP",
"schema": {
"description": "Let us know if you're joining this year's retreat.",
"completion": { "heading": "Thanks!", "message": "We'll follow up with details closer to the date." },
"pages": [{
"title": "",
"blocks": [
{ "type": "field", "field": { "key": "name", "type": "text", "label": "Full name", "required": true } },
{ "type": "field", "field": { "key": "email", "type": "email", "label": "Email", "required": true } },
{ "type": "field", "field": { "key": "attending", "type": "yes_no", "label": "Will you attend?", "required": true } },
{ "type": "field", "field": {
"key": "diet",
"type": "select",
"label": "Dietary preference",
"required": true,
"options": ["No restrictions", "Vegetarian", "Vegan", "Other"],
"visibility": [{ "fieldKey": "attending", "operator": "equals", "value": "Yes" }]
} }
]
}]
}
}
If an import is rejected for a reason you can't work out, Contact support.