---
title: "Schema reference"
description: "The field list that drives validation, the embed widget, notification emails and the dashboard."
---

> Documentation Index
> Fetch the complete documentation index at: https://docs.getquickforms.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Schema reference

A form schema is an object with a `fields` list (1 to 100 fields). The same schema drives validation, the embed widget, notification emails and the dashboard.

```json title="schema.json"
{
  "fields": [
    { "name": "name", "type": "text", "label": "Your name", "required": true, "max": 120 },
    { "name": "email", "type": "email", "required": true, "message": "We need an email to reply to." },
    { "name": "website", "type": "url", "placeholder": "https://" },
    { "name": "phone", "type": "tel", "pattern": "^[0-9 +()-]{6,20}$" },
    { "name": "seats", "type": "number", "integer": true, "min": 1, "max": 50 },
    { "name": "start", "type": "date", "min": "today" },
    { "name": "plan", "type": "select", "options": ["starter", "pro"] },
    { "name": "topics", "type": "checkboxes", "options": [
      { "value": "api", "label": "REST API" },
      { "value": "mcp", "label": "MCP" }
    ], "min": 1 },
    { "name": "contact_by", "type": "radio", "enum": ["email", "phone"] },
    { "name": "message", "type": "textarea", "min": 10, "max": 2000, "help": "Tell us a bit about your project." },
    { "name": "cv", "type": "file", "accept": ["pdf", "docx"], "max_size_kb": 4096 },
    { "name": "terms", "type": "checkbox", "accepted": true, "label": "I accept the terms" },
    { "name": "campaign", "type": "hidden", "default": "autumn" }
  ]
}
```

## Field types

| Type | Accepts | min / max | Notes |
| --- | --- | --- | --- |
| `text` | String | Length | Up to 1000 characters. |
| `textarea` | String | Length | Up to 10000 characters. |
| `email` | RFC email address | Length | Up to 254 characters. Used as reply-to and for the autoresponder. |
| `url` | http or https URL | Length | Up to 2048 characters. |
| `tel` | String | Length | Up to 1000 characters. Combine with pattern. |
| `number` | Number | Value | Set `integer` to accept whole numbers only. |
| `date` | Date | Earliest / latest date | Bounds are dates such as `2026-01-01` or `"today"`. |
| `select` | One option value | Count (with multiple) | Needs options. Set `multiple` to accept a list, with no repeats and no longer than the options. |
| `radio` | One option value | | Needs options. |
| `checkbox` | Boolean | | Values like `on`, `1` and `true` become booleans. Set `accepted` to require it ticked. |
| `checkboxes` | List of option values | Count | Needs options. Post as `name[]` from HTML. A plain list with no repeats, no longer than the options. |
| `hidden` | String | Length | Up to 1000 characters. The embed fills it from `default`. |
| `file` | Upload | | Post the file as multipart, or send an `upl_...` ID from a [direct upload](https://docs.getquickforms.com/file-uploads/#direct-uploads) in JSON. `accept` lists extensions, `max_size_kb` caps the size. Stored as name, size, mime, disk and path either way. See [file uploads](https://docs.getquickforms.com/file-uploads/). |
| `ranking` | Every option value, best first | - | Needs options. Post as `name[]` from HTML. A partial ranking is rejected. |
| `matrix` | `{row: column}` | - | Needs `rows` and `options` (the columns). Post as `name[row]` from HTML. A required matrix needs every row. |
| `signature` | Stored file | - | Drawn on the hosted page or embed and saved as a PNG up to 512 KB. Delivered like a `file`. This records a drawn signature and is not an e-signing service. |
| `calculated` | Number | - | Needs a `formula`. Worked out from earlier answers. Anything sent for it is ignored. |

## Field options

| Key | Type | Description |
| --- | --- | --- |
| `name` | string | Required and unique. Starts with a letter, then letters, numbers, dashes or underscores, up to 64 characters. |
| `type` | string | Required. One of the types above. |
| `label` | string | Shown to people and used in error messages. Defaults to the name with dashes and underscores as spaces. |
| `placeholder` | string | Input placeholder. For a single checkbox, the text next to it. |
| `help` | string | Help text under the field. |
| `message` | string | A custom error message used for every rule on this field. Not included in the public schema. |
| `required` | boolean | Defaults to false. |
| `min` | number or date | Lower bound. Means length, value, count or date depending on the type. |
| `max` | number or date | Upper bound, as above. Length limits never exceed the type ceiling. |
| `integer` | boolean | number only. |
| `multiple` | boolean | select only. |
| `accepted` | boolean | checkbox only. The box must be ticked. |
| `pattern` | string | A regular expression the value must match. Applies to text-like fields and numbers. |
| `options` | array | For select, radio and checkboxes. Strings, or `{"value", "label"}` objects. Up to 500. |
| `enum` | array | Alias for `options`. |
| `accept` | array | file only. Allowed extensions such as `["pdf", "png"]`. For direct uploads, checked against the file name when the upload URL is issued. |
| `max_size_kb` | integer | file only. Cannot exceed the installation upload cap. For direct uploads, checked when the upload URL is issued and again on the stored file. |
| `default` | any | Default value. The embed uses it for hidden fields. |
| `show_when` | array | Conditions on earlier questions. See [conditional questions](#conditional-questions). |

> **Note**
>
> Saving a schema validates it too: unknown types, duplicate names, a choice field without options, a non-numeric bound, an invalid pattern, a pattern that repeats a group which already repeats (like `(a+)+`) or a field asking for passwords or card details are rejected with a 422 that points at the field, for example `schema.fields.3.options`.

## Showing earlier answers

Use `{{field_name}}` in a `label` or `help` to show an answer given earlier in the form, as people type. Notification emails fill them in too.

## Calculated fields

A `calculated` field works out a number from earlier answers:

```json
{ "name": "total", "type": "calculated", "label": "Total", "formula": "{{quantity}} * {{price}} + 5", "decimals": 2, "prefix": "£" }
```

| Property | Type | What it does |
|---|---|---|
| `formula` | string | Numbers, `{{field}}`, `+ - * /` and brackets. Up to 500 characters. It can only use `number`, `select`, `radio`, `checkbox`, `checkboxes` and `calculated` questions that come before it. |
| `decimals` | integer | 0 to 4. Defaults to 2. |
| `prefix`, `suffix` | string | Shown around the result, up to 12 characters each. |
| `display` | boolean | `false` keeps the result off the form, for scores. It's still stored. |

An option adds its `score`, for example `{"value": "large", "label": "Large", "score": 9.5}`, or its value if that's a number. Several ticked options add up, a ticked `checkbox` is 1, and an unanswered or hidden question is 0. Dividing by zero stores no result.

## Conditional questions

Add `show_when` to show a question only for certain answers. Every condition must match, up to 10 per question.

```json
{
  "name": "order_number",
  "type": "text",
  "required": true,
  "show_when": [{ "field": "topic", "operator": "equals", "value": "support" }]
}
```

`operator` is `equals`, `not_equals`, `contains`, `is_set` or `is_empty`. A single checkbox counts as `yes` when ticked.

A condition can only use a question that comes before it, and not a file field. The hosted page and the embed show and hide questions as people answer. The server applies the same conditions whichever way a submission arrives: a question that isn't shown isn't required, and any answer sent for it is dropped.

Source: https://docs.getquickforms.com/schema/index.mdx
