Form JSON
Introduction
A form as data instead of markup: its pages and fields, their choices, the rules the server will apply, the conditions, how the form is meant to look, and what a script needs to submit it.
Two ways to get it
From a template, with output="json". Put it in a template whose type is JSON and you have an endpoint with ExpressionEngine's own routing and caching. Field defaults can use EE variables here, because the output is parsed after the tag runs.
From the JSON API: GET forms/{form}. It is the same document, wrapped in {"data": …}. Defaults that use EE variables are left out there, because nothing parses them.
A form as JSON
Example{exp:formidable:form form_handle="contact_us" output="json"}
{
"schema": 1,
"form": {
"id": 1,
"handle": "contact_us",
"name": "Contact Us",
"open": true,
"closed_message": "",
"members_only": false,
"templating": {
"theme": "tailwind",
"columns": 12,
"instructions": "top",
"attributes": {}
},
"success_message": "Thank you — your submission has been received.",
"pages": [
…
"fields": [
{
"handle": "first_name",
"label": "First Name",
"fieldtype": "text",
"stores_value": true,
"multiple": false,
"hidden": false,
"required": false,
"instructions": "We prefer your legal name",
"default": "",
"page": 1,
"section": "s_1_1",
"columns": 4,
"templating": {
"columns": 12,
"attributes": {
"instructions": {
"class": "text-sm text-grey-300"
}
}
},
"attributes": {
"type": "text"
},
"options": null,
"rules": [
{
"name": "whenPresent",
"value": ""
}
],
"conditions": {
"mode": "show",
"show": [],
"require": []
}
},
{
"handle": "last_name",
…
"submit": {
"url": "https://example.com/index.php?ACT=104&endpoint=forms/contact_us/submissions",
"action_url": "https://example.com/index.php?ACT=101",
"method": "POST",
"meta": "…",
"csrf_token": "…",
"honeypot": {
"name": "formidable_hp",
"js_name": ""
},
"multipart": false
}
}
What is in it
| Key | |
|---|---|
schema | The document version. It changes only when a published key changes meaning or is removed. New keys may appear at any time. |
form | Id, handle, name, open, closed_message, members_only, success_message, templating, and pages with their sections, navigation labels and conditions. |
fields | One entry per field in builder order: handle, label, fieldtype, whether it stores a value or several, hidden, required, instructions, default, page, section, width, templating, attributes, options, rules and conditions. |
submit | Where and how to submit: the API's submissions URL, the submit action URL, the signed meta, the session's csrf_token, the honeypot input names, and whether the form needs multipart/form-data. |
optionsare resolved to{value, label}pairs, whatever their source, and arenullfor a field without choices. A Recipients field publishes its values and labels only, never its addresses.rulesare the rules the server will apply, as{name, value}pairs.attributesare the field's authored attributes, withoutid,nameandvalue, which the client decides.templating, on the form and on each field, is how the form is meant to look, resolved exactly as the form tag resolves it: theme, grid width, instruction position, and the attributes for each part. A field'stemplating.columnsis its width in the template's grid, with{field_columns}already substituted.- A field whose choices depend on another field carries
depends_on, the other field's handle, and each of its options aparentlist of that field's values it belongs to. Offer only the options whoseparentincludes the other field's answer, plus those withoutparent. The server refuses any other pairing. - A Computed field carries
computed, its definition with fields named by handle, so a client can show the result as the visitor types. Whatever is posted for it is ignored: the server works it out again. - For a guest on a members-only form,
openis false andclosed_messageis filled in.
Submitting it
Post to submit.url as described in JSON API › Submitting. Or post to submit.action_url, the same action a rendered form posts to, with an X-Requested-With: XMLHttpRequest header so the answer comes back as JSON rather than a redirect:
- every field under its handle;
_formidable_metaset tosubmit.meta, andcsrf_tokenset tosubmit.csrf_token;submit.honeypot.namepresent and empty. Whensubmit.honeypot.js_nameis not empty, send that input non-empty;multipart/form-datawhensubmit.multipartis true.
The answer is {"success": true, "submission_id": …} or {"success": false, "errors": {"handle": "message", …}}.
The CSRF token belongs to the browser session that fetched the document, so the submission has to come from the same session, in practice the same site. A front end on another domain can read the document, but should submit through the JSON API with a key.
Never published
Workflows, recipients and their addresses, connections and their credentials, email templates, encryption settings, and anything from the Registry beyond a fieldtype's handle.