Form Definition Reference
A form definition is the JSON that describes a form: its fields, steps, conditions, limits, and spam settings. The builder writes it for you, and the server enforces it on every submission. Use this page when you need the exact name or value of a setting.
Each example below is a definition patch. Every top-level key you send replaces that whole key, and keys you leave out stay as they are. To add one field, send every existing field plus the new one.
These top-level keys can be changed: fields, steps, contentBlocks, conditions, visibilityRules, validationRules, limits, spam, verification, settings, allowedOrigins, and primaryPublicTarget.
Recipients, senders, and notification emails aren’t part of this patch. Set them on the form’s Delivery and Templates tabs. See Getting Started.
Field Names And Paths
fields maps a field name to its definition. Names use letters, digits, _, and -.
Other settings refer to a field by its path:
| Path | Refers to |
|---|---|
email | A top-level field |
contact.email | A field inside the contact group |
guests[] | Every item of the guests list, in steps, content blocks, and visibility rules |
guests[0].name | One item of the list, in conditions and validation rules |
A path that doesn’t exist fails with FORM_CONFIG_FIELD_REF_UNKNOWN.
Field Types
type takes one of these twelve values. Anything else, such as file or signature,
fails with FORM_CONFIG_FIELD_TYPE_INVALID.
type | Collects | Submitted as |
|---|---|---|
string | Short text | String |
textarea | Long text | String |
email | An email address, always checked for format | String |
url | A web address, always checked for format | String |
phone | A phone number | String |
number | A number | Number, or a string that converts to one |
boolean | Yes or no | true or false |
date | A date as YYYY-MM-DD, always checked | String |
select | One choice from a list | String |
checkbox | One tick box, or several choices | true or false, or a string or array of strings with allowedValues |
object | A group of fields | Object |
array | A repeating item | Array |
Rating and slider questions in the builder are number fields. There are no file
upload or signature fields.
Options For Every Field
| Option | Value | Effect |
|---|---|---|
label | String | The field’s name on the form |
description | String | Help text under the field |
required | true or false | An empty answer fails with FIELD_REQUIRED |
placeholder | String | Hint inside the field. Not on object or array |
defaultValue | Depends on type | Starting answer. Not on object or array |
messages | Object of strings | Your own error text, keyed by rule or by error code |
{
"fields": {
"name": {
"type": "string",
"label": "Your name",
"description": "As it appears on your invoice.",
"placeholder": "Jane Example",
"required": true,
"messages": { "required": "Enter your name." }
}
}
}A defaultValue must match the field: a finite number on number, true or false on boolean, a real YYYY-MM-DD date on date, and a string on other text types. With allowedValues, it must be one of them.
Text Fields
These options work on string, textarea, email, url, phone, and on date and select where the table says so.
| Option | Value | Works on | Error code |
|---|---|---|---|
min, max | Length, 0 or more | All text types | FIELD_STRING_TOO_SHORT, FIELD_STRING_TOO_LONG |
exactLength | Length, 0 or more. Can’t combine with min or max | All text types, date, select | FIELD_STRING_LENGTH_INVALID |
pattern, flags | Regular expression up to 500 characters. flags takes i, m, and u | All text types | FIELD_PATTERN_INVALID |
allowedValues | Array of strings | All text types | FIELD_VALUE_NOT_ALLOWED |
startsWith, endsWith | Non-empty string | string, textarea, email, url, phone | FIELD_STRING_PREFIX_INVALID, FIELD_STRING_SUFFIX_INVALID |
contains, notContains | Non-empty string | string, textarea, email, url, phone | FIELD_STRING_CONTAINS_REQUIRED, FIELD_STRING_CONTAINS_FORBIDDEN |
{
"fields": {
"reference": {
"type": "string",
"label": "Reference",
"min": 4,
"max": 20,
"startsWith": "REF-",
"pattern": "^REF-[0-9]+$"
},
"message": {
"type": "textarea",
"label": "Message",
"required": true,
"max": 5000,
"trim": true
}
}
}Masked And Hidden Text
On string only, inputType is password or hidden. It changes how the field shows.
Validation still treats it as a string.
{
"fields": {
"access_code": { "type": "string", "label": "Access code", "inputType": "password" },
"campaign": { "type": "string", "inputType": "hidden", "defaultValue": "spring" }
}
}Format Checks On Text
These options work on string fields. countryCode and regionCodeCountry also work
on select.
| Option | Value | Error code |
|---|---|---|
uuidVersion | any, v4, or v7 | FIELD_UUID_INVALID |
countryCode | iso2 | FIELD_COUNTRY_CODE_INVALID |
postalCodeCountry | CA or US | FIELD_POSTAL_CODE_INVALID |
regionCodeCountry | CA or US | FIELD_REGION_CODE_INVALID |
timeFormat | HH:mm or HH:mm:ss | FIELD_TIME_INVALID |
dateTimeFormat | iso8601, which needs a time zone | FIELD_DATETIME_INVALID |
{
"fields": {
"postal_code": { "type": "string", "label": "Postal code", "postalCodeCountry": "CA" },
"province": { "type": "select", "label": "Province", "regionCodeCountry": "CA", "allowedValues": ["QC", "ON"] },
"pickup_time": { "type": "string", "label": "Pickup time", "timeFormat": "HH:mm" }
}
}Email Fields
email answers are always checked for format, and fail with FIELD_EMAIL_INVALID. normalizeEmail lowercases the address before it’s checked and stored.
{
"fields": {
"email": { "type": "email", "label": "Email", "required": true, "max": 254, "trim": true, "normalizeEmail": true }
}
}URL Fields
url answers are always checked, and accept http and https by default. Use allowedProtocols to change that. javascript, data, and vbscript aren’t allowed.
A bad address fails with FIELD_URL_INVALID, and another protocol with FIELD_URL_PROTOCOL_NOT_ALLOWED.
{
"fields": {
"website": { "type": "url", "label": "Website", "allowedProtocols": ["https"] }
}
}Phone Fields
A phone field accepts any text by default. phoneFormat takes e164, which requires
a number such as +15145550123, and fails with FIELD_PHONE_INVALID.
{
"fields": {
"phone": { "type": "phone", "label": "Phone", "phoneFormat": "e164" }
}
}Number Fields
| Option | Value | Error code |
|---|---|---|
min, max | The lowest and highest value, 0 or more | FIELD_NUMBER_TOO_SMALL, FIELD_NUMBER_TOO_LARGE |
integer | true for whole numbers only | FIELD_INTEGER_REQUIRED |
maxDecimalPlaces | Whole number, 0 or more | FIELD_DECIMAL_PLACES_INVALID |
A value that can’t be read as a number fails with FIELD_TYPE_INVALID. min and max can’t be negative, on any field type.
{
"fields": {
"rating": { "type": "number", "label": "Your rating", "integer": true, "min": 1, "max": 5 },
"budget": { "type": "number", "label": "Budget", "maxDecimalPlaces": 2 }
}
}Date Fields
date answers must be real calendar dates as YYYY-MM-DD. dateMin and dateMax take
the same format, and include the date you give.
| Rule | Error code |
|---|---|
| Not a real date | FIELD_DATE_INVALID |
Before dateMin | FIELD_DATE_TOO_EARLY |
After dateMax | FIELD_DATE_TOO_LATE |
{
"fields": {
"event_date": { "type": "date", "label": "Event date", "dateMin": "2027-01-01", "dateMax": "2027-12-31" }
}
}Choice Fields
Use select for one choice. List the choices in allowedValues.
{
"fields": {
"department": { "type": "select", "label": "Department", "required": true, "allowedValues": ["Sales", "Support"] }
}
}Several Choices
Use checkbox with allowedValues. minSelections and maxSelections set the range,
and fail with FIELD_SELECTION_TOO_FEW and FIELD_SELECTION_TOO_MANY. defaultValue is
a string or an array of strings from the list.
{
"fields": {
"interests": {
"type": "checkbox",
"label": "Interests",
"allowedValues": ["Workshops", "Newsletter", "Events"],
"minSelections": 1,
"maxSelections": 2,
"defaultValue": ["Newsletter"]
}
}
}One Tick Box
Without allowedValues, a checkbox is one tick box, and boolean works the same way. defaultValue is true or false. Add accepted when the box must be ticked, such as
for consent. It fails with FIELD_ACCEPTED_REQUIRED.
{
"fields": {
"consent": { "type": "checkbox", "label": "I agree to the terms", "required": true, "accepted": true },
"subscribe": { "type": "boolean", "label": "Send me the newsletter", "defaultValue": false }
}
}Groups
type: "object" groups fields under a shared name. It takes label, description, required, messages, and fields. Groups can nest up to 8 levels. Answers arrive as an
object, and their paths look like contact.email.
{
"fields": {
"contact": {
"type": "object",
"label": "Contact",
"fields": {
"name": { "type": "string", "label": "Name", "required": true },
"email": { "type": "email", "label": "Email", "required": true }
}
}
}
}Lists
type: "array" repeats one item. item holds any field type, including a group.
| Option | Value | Error code |
|---|---|---|
minItems | Whole number, 0 or more | FIELD_ARRAY_TOO_SHORT |
maxItems | Whole number, 0 or more | FIELD_ARRAY_TOO_LONG |
uniqueItems | true to refuse repeats | FIELD_ARRAY_DUPLICATES |
{
"fields": {
"guests": {
"type": "array",
"label": "Guests",
"minItems": 1,
"maxItems": 5,
"item": {
"type": "object",
"fields": {
"name": { "type": "string", "label": "Guest name", "required": true }
}
}
}
}
}Cleaning Answers
Processors clean an answer before it’s validated. The result is the value that’s stored
and delivered. They work on string, textarea, email, url, phone, date, and select, and a field can have up to 25 of each.
sanitizers run first, then trim, collapseWhitespace, and normalizeEmail, then transformers, then caseTransform.
Sanitizer type | What it does |
|---|---|
trim | Removes spaces at the start and end |
collapseWhitespace | Turns any run of spaces and line breaks into one space |
normalizeNewlines | Makes every line break the same |
stripTags | Removes anything written like an HTML tag |
removeUrls | Removes web addresses that start with http://, https://, or www. |
removeEmoji | Removes emoji |
digitsOnly | Keeps only digits. On string and phone only |
Transformer type | Also needs | What it does |
|---|---|---|
case | mode: upper or lower | Changes the case of the whole answer |
map | values: up to 100 strings | Swaps one whole answer for another. The answer must match exactly |
{
"fields": {
"province": {
"type": "string",
"label": "Province",
"sanitizers": [{ "type": "trim" }, { "type": "collapseWhitespace" }],
"transformers": [{ "type": "case", "mode": "upper" }, { "type": "map", "values": { "QC": "Québec" } }]
}
}
}Shortcut Options
| Option | Value | Works on |
|---|---|---|
trim | true | Text fields |
collapseWhitespace | true | string, textarea |
normalizeEmail | true | email |
caseTransform | upper or lower | string, textarea, select |
Pipelines
pipeline runs sanitizers, transformers, and validators in one list that you order
yourself. It can’t be combined with sanitizers, transformers, validators, trim, collapseWhitespace, normalizeEmail, or caseTransform, fails with FORM_CONFIG_FIELD_OPTION_CONFLICT if you try, and needs at least one step. The app’s Processing tab shows a pipeline without letting you edit it, so prefer the two lists
above.
{
"fields": {
"code": {
"type": "string",
"label": "Code",
"pipeline": [
{ "step": "sanitizer", "sanitizer": { "type": "trim" } },
{ "step": "validator", "validator": { "type": "startsWith", "value": "A", "code": "CODE_PREFIX", "message": "Start the code with A." } },
{ "step": "transformer", "transformer": { "type": "case", "mode": "lower" } }
]
}
}
}Validators
Add checks to a text field with validators. Each one needs a type, a code in capitals
such as CUSTOM_POSTAL_CODE, and the message to show. Validation responses return your code and message.
type | Also takes | Passes when the answer is |
|---|---|---|
email | An email address | |
url | allowedProtocols | A web address |
domain | A domain name | |
phoneE164 | format: e164 | A number such as +15145550123 |
postalCode | country: CA or US, required | A postal or ZIP code |
regionCode | country: CA or US, required | A province or state code |
countryCode | format: iso2 | A two-letter country code |
uuid | version: any, v4, or v7 | A UUID |
ip | An IPv4 or IPv6 address | |
ipv4 | An IPv4 address | |
ipv6 | An IPv6 address | |
contains | value, required | Contains the text |
notContains | value, required | Doesn’t contain the text |
startsWith | value, required | Starts with the text |
endsWith | value, required | Ends with the text |
regex | pattern, required, up to 500 characters. flags: i, m, u | Matches the expression |
{
"fields": {
"postal_code": {
"type": "string",
"label": "Postal code",
"validators": [
{ "type": "postalCode", "country": "CA", "code": "CUSTOM_POSTAL_CODE", "message": "Enter a valid postal code." }
]
},
"sku": {
"type": "string",
"label": "Product code",
"validators": [
{ "type": "regex", "pattern": "^[A-Z]{3}-[0-9]{4}$", "code": "CUSTOM_SKU", "message": "Use a code such as ABC-1234." }
]
}
}
}Conditions
A condition is true or false for a set of answers. It’s used by visibility rules, validation rules, steps, and content blocks.
| Operator | value | True when the answer |
|---|---|---|
isFilled | None | Isn’t empty |
isEmpty | None | Is empty |
equals | Any | Equals the value |
notEquals | Any | Doesn’t equal the value |
in | Array | Is in the array |
notIn | Array | Isn’t in the array |
greaterThan | Number or text | Is greater |
greaterThanOrEqual | Number or text | Is greater or equal |
lessThan | Number or text | Is less |
lessThanOrEqual | Number or text | Is less or equal |
matches | Regular expression | Matches the expression |
Combine conditions with all, any, and not. A condition can nest up to 5 levels, and a
group can hold up to 25 conditions. An empty group fails with FORM_CONFIG_CONDITION_REQUIRED.
The next example is complete. It shows details when type is Commercial and size is
over 10, or when type isn’t filled in.
{
"fields": {
"type": { "type": "select", "label": "Type", "allowedValues": ["Personal", "Commercial"] },
"size": { "type": "number", "label": "Team size" },
"details": { "type": "textarea", "label": "Details" }
},
"visibilityRules": [
{
"id": "show-details",
"path": "details",
"default": "hidden",
"state": "visible",
"when": {
"any": [
{ "all": [
{ "path": "type", "operator": "equals", "value": "Commercial" },
{ "path": "size", "operator": "greaterThan", "value": 10 }
] },
{ "not": { "path": "type", "operator": "isFilled" } }
]
}
}
]
}Named Conditions
conditions stores up to 100 conditions by name, for reuse in the app. Each has an id, and
optionally a label up to 200 characters, a description up to 2,000, and a when. A rule
keeps its own copy of the logic, so a named condition doesn’t change how a form behaves.
{
"fields": {
"type": { "type": "select", "label": "Type", "allowedValues": ["Personal", "Commercial"] }
},
"conditions": [
{
"id": "is-commercial",
"label": "Commercial project",
"when": { "path": "type", "operator": "equals", "value": "Commercial" }
}
]
}Visibility Rules
A visibility rule shows or hides one field or group. When when is true, the field takes state. Otherwise it takes default, which is visible if you leave it out. A form can have up
to 100 rules, with one rule per field.
The server applies these rules too. Answers to a hidden field are dropped, and its checks don’t run.
{
"fields": {
"meal": { "type": "select", "label": "Meal", "allowedValues": ["Standard", "Other"] },
"meal_details": { "type": "string", "label": "Tell us more" }
},
"visibilityRules": [
{
"id": "show-meal-details",
"path": "meal_details",
"default": "hidden",
"state": "visible",
"when": { "path": "meal", "operator": "equals", "value": "Other" }
}
]
}Validation Rules
A validation rule checks a field only when a condition is true. then holds up to 25 actions,
each with a path and a validator. A form can have up to 100 rules.
The validator is any type from Validators, or one of these:
type | Also takes | Passes when |
|---|---|---|
required | The field isn’t empty | |
allOf | validators | Every validator in the list passes |
anyOf | validators | At least one validator in the list passes |
Each needs a code and a message. allOf and anyOf lists hold up to 25 validators, and can
nest up to 3 levels.
{
"fields": {
"type": { "type": "select", "label": "Type", "allowedValues": ["Personal", "Commercial"] },
"company": { "type": "string", "label": "Company" }
},
"validationRules": [
{
"id": "company-if-commercial",
"when": { "path": "type", "operator": "equals", "value": "Commercial" },
"then": [
{
"path": "company",
"validator": { "type": "required", "code": "COMPANY_REQUIRED", "message": "Enter the company name." }
}
]
}
]
}Steps
steps groups fields into pages. With two or more, hosted pages and script embeds show one at a
time, with Next, Back, and a counter. A form can have up to 50 steps.
| Option | Value | Effect |
|---|---|---|
id | Unique string | Names the step, including for step validation |
fields | 1 to 100 paths | The fields in this step. List conditional ones too |
label | String | The step’s title |
summary | String up to 2,000 characters | Shown under the title |
validateOn | stepSubmit, change, or blur | change lets Next skip the server check. The others check first |
visibleWhen | Condition | Hides the step and skips its fields unless this is true |
Submitting always checks the whole form. See Sections And Multi-Step Forms.
{
"fields": {
"name": { "type": "string", "label": "Name", "required": true },
"email": { "type": "email", "label": "Email", "required": true },
"attending": { "type": "boolean", "label": "Will you attend?" },
"meal": { "type": "select", "label": "Meal", "allowedValues": ["Standard", "Vegetarian"] }
},
"steps": [
{ "id": "about", "label": "About you", "summary": "How to reach you.", "fields": ["name", "email", "attending"], "validateOn": "stepSubmit" },
{
"id": "seat",
"label": "Your seat",
"fields": ["meal"],
"visibleWhen": { "path": "attending", "operator": "equals", "value": true }
}
]
}Content Blocks
A content block puts text or a divider among the fields. It holds no answer, so it never reaches validation, submissions, or email. A form can have up to 100.
| Option | Value | Effect |
|---|---|---|
id | Unique string | Names the block |
type | text or separator | A separator shows a line, with title as its label if set |
title | String up to 200 characters | A heading |
description, body | Strings up to 5,000 characters | Paragraphs, on text |
before, after | Field path | Where it goes. Use one of them. With neither, it ends the form |
visibleWhen | Condition | Shows the block only when this is true |
{
"fields": {
"name": { "type": "string", "label": "Name" },
"seats": { "type": "number", "label": "Seats", "integer": true, "min": 1 }
},
"contentBlocks": [
{ "id": "intro", "type": "text", "title": "Seats are limited", "body": "Register early to save your spot.", "before": "name" },
{ "id": "divider", "type": "separator", "after": "name" },
{
"id": "group-note",
"type": "text",
"body": "We'll contact you about group rates.",
"after": "seats",
"visibleWhen": { "path": "seats", "operator": "greaterThan", "value": 5 }
}
]
}Limits
limits takes duplicateWindowSeconds, rateLimits, and quotas. Any other key fails the
whole request with BAD_REQUEST. See Limits And Spam Protection for what
each one does.
| Option | Value |
|---|---|
duplicateWindowSeconds | Whole number. Anything under 60 acts as 60 |
rateLimits[].id | Unique string |
rateLimits[].scope | ipHash per visitor, form, workspace, origin, deviceToken, or idempotencyKey |
rateLimits[].windowSeconds | Whole number, 1 or more |
rateLimits[].max | Whole number, 1 or more |
rateLimits[].message | Your own message |
quotas[].id | Unique string |
quotas[].scope | form or workspace |
quotas[].period | day, week, month, or lifetime |
quotas[].maxAccepted | Whole number, 1 or more |
quotas[].message | Your own message |
{
"limits": {
"duplicateWindowSeconds": 300,
"rateLimits": [
{ "id": "per-visitor", "scope": "ipHash", "windowSeconds": 600, "max": 10 },
{ "id": "whole-form", "scope": "form", "windowSeconds": 3600, "max": 300 }
],
"quotas": [
{ "id": "cap", "scope": "form", "period": "lifetime", "maxAccepted": 200, "message": "Registration is full." }
]
}
}The response code stays RATE_LIMITED or FORM_QUOTA_REACHED whatever the message says.
Open and close dates aren’t part of limits. Set them under Response window on the Publish tab.
Spam Checks
| Option | Value | Effect |
|---|---|---|
honeypotField | Field path | Rejects a submission with this field filled. New forms use _formerie_hp. Leave it out to turn the check off |
minSubmitTimeMs | Whole number, 0 or more | Rejects a form sent faster than this, when the client sends metadata.startedAt. 0 turns it off |
maxLinks | Whole number, 0 or more | Rejects a submission with more links than this, across all its answers. 0 rejects any link. Leave it out to turn the check off |
{
"spam": {
"honeypotField": "_formerie_hp",
"minSubmitTimeMs": 1000,
"maxLinks": 3
}
}Human Verification
| Option | Value | Effect |
|---|---|---|
provider | turnstile, recaptcha-v2, or recaptcha-v3 | The challenge to show |
mode | managed or custom | managed uses Formerie’s keys, on turnstile only. Always set it |
required | true or false | Whether a submission must pass |
siteKey | Public site key | Needed with custom when required is true |
minScore | 0 to 1 | Lowest score to accept, on recaptcha-v3 only |
expectedAction | String | The action the token must carry, on recaptcha-v3 only |
{
"verification": {
"provider": "turnstile",
"mode": "managed",
"required": true
}
}With your own keys, enter the secret key in the app, under Human verification in the form’s Settings. It’s encrypted, and it stays in place when you change the rest of verification with the
same provider. Until one is saved, required has to stay false, as in this example, or the change fails
with FORM_CONFIG_VERIFICATION_SECRET_REQUIRED.
{
"verification": {
"provider": "recaptcha-v3",
"mode": "custom",
"required": false,
"siteKey": "your-public-site-key",
"minScore": 0.5,
"expectedAction": "contact"
}
}Allowed Origins
allowedOrigins lists the sites whose browser code can call the form directly. Each entry is an
exact origin with no path, and no wildcards. See Allowed Origins for when you need them.
{
"allowedOrigins": ["https://www.example.com", "https://example.com", "http://localhost:5173"]
}Public Address
primaryPublicTarget sets the address the form uses as its main one.
kind | Also needs | Address |
|---|---|---|
workspace | The workspace path on forms.formerie.com | |
formerie | The same Formerie address | |
runtimeHostname | hostname, and bindingKind: workspace or form | Your own hostname. Connect it first under Domains |
{
"primaryPublicTarget": {
"kind": "runtimeHostname",
"hostname": "forms.example.com",
"bindingKind": "workspace"
}
}Form Behaviour
settings.behavior holds what happens around the fields. settings replaces as a whole, so
send behavior, branding, and layout together.
| Option | Value |
|---|---|
language | A language tag such as en-CA or fr-CA |
submitButtonLabel | Up to 80 characters |
successMessage | Up to 280 characters |
successRedirectUrl | An http or https address, up to 500 characters |
Branding
settings.branding styles the hosted page and script embed.
| Option | Value |
|---|---|
brandName | Up to 80 characters |
logoUrl | An http or https address, up to 500 characters |
density | compact, comfortable, or spacious |
fontPreset | arial, georgia, helvetica, inter, mono, system, times, or verdana |
previewTheme | light or dark. Visitors get the one you choose |
radius | none, soft, or round |
themes.light, themes.dark | Colours as #RRGGBB |
Each theme takes accentColor, accentTextColor, backgroundColor, borderColor, errorColor, errorTextColor, fieldBackgroundColor, mutedTextColor, surfaceColor, and textColor.
Custom CSS isn’t applied to hosted pages.
Layout
settings.layout sets the width of the hosted page.
| Option | Value |
|---|---|
containerWidthMode | contained, custom, or full |
containerMinWidth, containerMaxWidth | A length such as 44rem, 720px, 90%, or 72ch, with custom |
{
"settings": {
"behavior": {
"language": "fr-CA",
"submitButtonLabel": "Envoyer",
"successMessage": "Merci, nous vous répondrons sous peu."
},
"branding": {
"brandName": "Example",
"density": "comfortable",
"fontPreset": "inter",
"previewTheme": "light",
"radius": "soft",
"themes": { "light": { "accentColor": "#007c72" } }
},
"layout": { "containerWidthMode": "custom", "containerMaxWidth": "44rem" }
}
}Size Limits
| What | Limit |
|---|---|
| Fields on a form, counting those inside groups and lists | 250 |
| Nesting of groups and lists | 8 levels |
| Steps | 50 |
| Paths in a step | 100 |
| Step summary | 2,000 characters |
| Content blocks | 100 |
| Content block title | 200 characters |
| Content block description or body | 5,000 characters each |
| Named conditions | 100 |
| Named condition label, description | 200, 2,000 characters |
| Visibility rules | 100 |
| Validation rules | 100 |
| Actions in a validation rule | 25 |
| Conditions in a rule | 25 |
| Condition nesting | 5 levels |
Validators in an allOf or anyOf | 25, nested up to 3 levels |
| Sanitizers, transformers, or pipeline steps on a field | 25 of each |
Entries in a map transformer | 100 |
| Regular expression | 500 characters |
| A submission’s JSON body | 100,000 bytes |
Reading Definition Errors
A rejected definition returns VALIDATION_FAILED with issues, and each issue has a path, a
stable code, and a message. Fix those paths and send the patch again.
| Code | What’s wrong |
|---|---|
FORM_CONFIG_FIELD_TYPE_INVALID | type isn’t one of the twelve field types |
FORM_CONFIG_FIELD_OPTION_INVALID | That option doesn’t work on this field type |
FORM_CONFIG_FIELD_OPTION_CONFLICT | Two options can’t be used together |
FORM_CONFIG_FIELD_REF_UNKNOWN | A path names a field that doesn’t exist |
FORM_CONFIG_FIELDS_REQUIRED | The form has no fields |
FORM_CONFIG_NUMBER_TOO_SMALL | A number is below its minimum |
FORM_CONFIG_RANGE_INVALID | A minimum is above its maximum |
FORM_CONFIG_DEFAULT_VALUE_INVALID | defaultValue doesn’t suit the field |
FORM_CONFIG_DEFAULT_VALUE_NOT_ALLOWED | defaultValue isn’t in allowedValues |
FORM_CONFIG_CODE_INVALID | A code isn’t in capitals, digits, and _ |
FORM_CONFIG_VALIDATOR_INVALID | The validator type doesn’t exist |
FORM_CONFIG_CONDITION_OPERATOR_INVALID | The condition operator doesn’t exist |
FORM_CONFIG_ID_DUPLICATE | Two entries in a list share an id |
FORM_CONFIG_ORIGIN_WILDCARD | An allowed origin uses * |
FORM_CONFIG_ORIGIN_INVALID | An allowed origin has a path, or isn’t http or https |