# Question types

> Every trulyform question type with its settings, answer format, logic operators and a ready-to-edit JSON template you can copy into a form.

## What you need to know

- Pick the type that matches the answer you need. The overview below says what each one is for.
- Copy a type's template, give it a title, and replace its IDs with fresh ones that are unique in the form.
- Types with choices need at least two labelled options. A matrix also needs at least one labelled row.
- Every type takes the common settings `nextButton` and `imageAlt`, plus its own. A setting the type does not use is ignored, so leave it out.
- Send whole-number settings such as `maxLength` as JSON numbers, not strings.
- `list_field_types` returns the same information, plus fresh IDs when you pass `id_count`.

## Overview

| Kind | Use it for |
| --- | --- |
| [`short_text`](https://trulyform.com/docs/question-types.md#short_text) | One line of free text, such as a name or a company. |
| [`long_text`](https://trulyform.com/docs/question-types.md#long_text) | A multi-line answer, such as feedback or a description. |
| [`email`](https://trulyform.com/docs/question-types.md#email) | An email address, checked for a valid format. |
| [`phone`](https://trulyform.com/docs/question-types.md#phone) | A phone number in any common format. |
| [`number`](https://trulyform.com/docs/question-types.md#number) | A number, such as a quantity, an age or a budget. |
| [`date`](https://trulyform.com/docs/question-types.md#date) | A calendar date, such as an event or a birthday. |
| [`single_choice`](https://trulyform.com/docs/question-types.md#single_choice) | Pick one of a few visible choices. |
| [`dropdown`](https://trulyform.com/docs/question-types.md#dropdown) | Pick one choice from a long list, such as a country. |
| [`multiple_choice`](https://trulyform.com/docs/question-types.md#multiple_choice) | Pick any number of choices. |
| [`consent`](https://trulyform.com/docs/question-types.md#consent) | A single checkbox, such as agreeing to terms. |
| [`nps`](https://trulyform.com/docs/question-types.md#nps) | Net Promoter Score: how likely someone is to recommend you, from 0 to 10. |
| [`opinion_scale`](https://trulyform.com/docs/question-types.md#opinion_scale) | A rating on a short numbered scale, such as 1 to 5. |
| [`ranking`](https://trulyform.com/docs/question-types.md#ranking) | Put every choice in order of preference. |
| [`matrix`](https://trulyform.com/docs/question-types.md#matrix) | Rate several rows on the same set of columns. Options are the columns. |
| [`file_upload`](https://trulyform.com/docs/question-types.md#file_upload) | Attach one or more files, such as a CV or a photo. |
| [`statement`](https://trulyform.com/docs/question-types.md#statement) | Show text between questions. Collects no answer. |
| [`website`](https://trulyform.com/docs/question-types.md#website) | A website address. |

## Settings

Settings live in each question's `settings` object.

- **`nextButton`**: string of up to 100 characters, default `"Next"`. Used by every type. Label of the button that moves on. Must not be blank. Statements default to "Continue". There is no separate submit button: the last question's button sends the form, so label it "Submit" or similar.
- **`imageAlt`**: string of up to 1,000 characters. Used by every type. Alt text for an image set on this question. Only used when media.inherit is false.
- **`placeholder`**: string of up to 1,000 characters. Used by `short_text`, `long_text`, `email`, `phone`, `number`, `dropdown`, `website`. Hint shown in the empty answer field. Leave it out for the default hint; an empty string shows none.
- **`maxLength`**: integer from 1 to 10,000, default `10000`. Used by `short_text`, `long_text`. Most characters an answer may have.
- **`min`**: number. Used by `number`, `opinion_scale`. Lowest accepted answer. Number questions: a decimal string such as "0" or "9.5" (a JSON number works too). Opinion scale: a whole number, default 1.
- **`max`**: number. Used by `number`, `opinion_scale`. Highest accepted answer. Number questions: a decimal string such as "100" (a JSON number works too). Opinion scale: a whole number, default 5, at most 10 above min.
- **`lowLabel`**: string of up to 255 characters, default `"Not likely"`. Used by `nps`, `opinion_scale`. Caption under the lowest score.
- **`highLabel`**: string of up to 255 characters, default `"Very likely"`. Used by `nps`, `opinion_scale`. Caption under the highest score.
- **`comment`**: true or false, default `false`. Used by `nps`, `opinion_scale`. Adds an optional comment box under the scale. The answer becomes {score, comment}.
- **`label`**: string of up to 2,000 characters, default `"I agree"`. Used by `consent`. Text beside the checkbox.
- **`maxFiles`**: integer from 1 to 10, default `1`. Used by `file_upload`. Most files per answer. The owner's plan can lower it.
- **`maxSizeMB`**: integer from 1 to 10,000. Used by `file_upload`. Largest file in MB. Leave it out to use the account limit, which also caps any value set here.
- **`accept`**: string of up to 255 characters, default `".pdf,.jpg,.jpeg,.png,.webp,.txt,.csv,.docx"`. Used by `file_upload`. Comma-separated file extensions respondents may upload.

Allowed `accept` extensions: pdf, jpg, jpeg, png, webp, txt, csv, doc, docx, xls, xlsx.

## Types

### `short_text`

**Short text.** One line of free text, such as a name or a company.

- **Answer:** Text up to maxLength characters.
- **Choices:** Not used. Keep `options` and `rows` empty.
- **Settings:** `placeholder`, `maxLength`, plus the common `nextButton` and `imageAlt`.
- **Logic operators:** `eq`, `neq`, `has_value`, `is_empty`, `contains`, `not_contains`. Compare with a string, such as "yes".
- **Calculations:** Not usable.

```json title="Short text question"
{
    "id": "01990000-0000-7000-8000-000000000001",
    "kind": "short_text",
    "title": {"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"What is your name?"}]}]},
    "description": {"type":"doc","content":[{"type":"paragraph","content":[]}]},
    "required": false,
    "options": [],
    "rows": [],
    "settings": {"nextButton":"Next"},
    "media": {"inherit":true,"assetId":null,"layout":"above"}
}
```

### `long_text`

**Long text.** A multi-line answer, such as feedback or a description.

- **Answer:** Text up to maxLength characters, line breaks allowed.
- **Choices:** Not used. Keep `options` and `rows` empty.
- **Settings:** `placeholder`, `maxLength`, plus the common `nextButton` and `imageAlt`.
- **Logic operators:** `eq`, `neq`, `has_value`, `is_empty`, `contains`, `not_contains`. Compare with a string, such as "yes".
- **Calculations:** Not usable.

```json title="Long text question"
{
    "id": "01990000-0000-7000-8000-000000000002",
    "kind": "long_text",
    "title": {"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"What should we know about your project?"}]}]},
    "description": {"type":"doc","content":[{"type":"paragraph","content":[]}]},
    "required": false,
    "options": [],
    "rows": [],
    "settings": {"nextButton":"Next"},
    "media": {"inherit":true,"assetId":null,"layout":"above"}
}
```

### `email`

**Email.** An email address, checked for a valid format.

- **Answer:** An email address of up to 320 characters.
- **Choices:** Not used. Keep `options` and `rows` empty.
- **Settings:** `placeholder`, plus the common `nextButton` and `imageAlt`.
- **Logic operators:** `eq`, `neq`, `has_value`, `is_empty`, `contains`, `not_contains`. Compare with a string, such as "yes".
- **Calculations:** Not usable.

```json title="Email question"
{
    "id": "01990000-0000-7000-8000-000000000003",
    "kind": "email",
    "title": {"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"What is your email address?"}]}]},
    "description": {"type":"doc","content":[{"type":"paragraph","content":[]}]},
    "required": false,
    "options": [],
    "rows": [],
    "settings": {"nextButton":"Next"},
    "media": {"inherit":true,"assetId":null,"layout":"above"}
}
```

### `phone`

**Phone.** A phone number in any common format.

- **Answer:** 7 to 15 digits, optionally starting with +. Spaces, brackets, dots and dashes are allowed.
- **Choices:** Not used. Keep `options` and `rows` empty.
- **Settings:** `placeholder`, plus the common `nextButton` and `imageAlt`.
- **Logic operators:** `eq`, `neq`, `has_value`, `is_empty`, `contains`, `not_contains`. Compare with a string, such as "yes".
- **Calculations:** Not usable.

```json title="Phone question"
{
    "id": "01990000-0000-7000-8000-000000000004",
    "kind": "phone",
    "title": {"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"What is the best number to reach you?"}]}]},
    "description": {"type":"doc","content":[{"type":"paragraph","content":[]}]},
    "required": false,
    "options": [],
    "rows": [],
    "settings": {"nextButton":"Next"},
    "media": {"inherit":true,"assetId":null,"layout":"above"}
}
```

### `number`

**Number.** A number, such as a quantity, an age or a budget.

- **Answer:** A decimal number with up to 18 digits and 6 decimal places, within min and max. Decimals are always allowed; for a whole count such as guests, use a choice question with scored options instead.
- **Choices:** Not used. Keep `options` and `rows` empty.
- **Settings:** `placeholder`, `min`, `max`, plus the common `nextButton` and `imageAlt`.
- **Logic operators:** `eq`, `neq`, `has_value`, `is_empty`, `gt`, `gte`, `lt`, `lte`. Compare with a number or decimal string, such as "7" or "2.5".
- **Calculations:** Read the answer as a number with an `answer` node.

```json title="Number question"
{
    "id": "01990000-0000-7000-8000-000000000005",
    "kind": "number",
    "title": {"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"How many guests are coming?"}]}]},
    "description": {"type":"doc","content":[{"type":"paragraph","content":[]}]},
    "required": false,
    "options": [],
    "rows": [],
    "settings": {"nextButton":"Next"},
    "media": {"inherit":true,"assetId":null,"layout":"above"}
}
```

### `date`

**Date.** A calendar date, such as an event or a birthday.

- **Answer:** A date as YYYY-MM-DD.
- **Choices:** Not used. Keep `options` and `rows` empty.
- **Settings:** Only the common ones: `nextButton` and `imageAlt`.
- **Logic operators:** `eq`, `neq`, `has_value`, `is_empty`, `gt`, `gte`, `lt`, `lte`. Compare with a date as "YYYY-MM-DD".
- **Calculations:** Not usable.

```json title="Date question"
{
    "id": "01990000-0000-7000-8000-000000000006",
    "kind": "date",
    "title": {"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"When is your event?"}]}]},
    "description": {"type":"doc","content":[{"type":"paragraph","content":[]}]},
    "required": false,
    "options": [],
    "rows": [],
    "settings": {"nextButton":"Next"},
    "media": {"inherit":true,"assetId":null,"layout":"above"}
}
```

### `single_choice`

**Single choice.** Pick one of a few visible choices.

- **Answer:** The ID of one option.
- **Choices:** Needs at least 2 options, each with a label. Options may carry a decimal-string `score` for calculations.
- **Settings:** Only the common ones: `nextButton` and `imageAlt`.
- **Logic operators:** `eq`, `neq`, `has_value`, `is_empty`. Compare with an option ID from this question.
- **Calculations:** Add up the scores of the selected options with an `option_score` node.

```json title="Single choice question"
{
    "id": "01990000-0000-7000-8000-000000000007",
    "kind": "single_choice",
    "title": {"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"Which plan are you interested in?"}]}]},
    "description": {"type":"doc","content":[{"type":"paragraph","content":[]}]},
    "required": false,
    "options": [{"id":"01990000-0000-7000-8000-000000000008","label":"Option 1"},{"id":"01990000-0000-7000-8000-000000000009","label":"Option 2"}],
    "rows": [],
    "settings": {"nextButton":"Next"},
    "media": {"inherit":true,"assetId":null,"layout":"above"}
}
```

### `dropdown`

**Dropdown.** Pick one choice from a long list, such as a country.

- **Answer:** The ID of one option.
- **Choices:** Needs at least 2 options, each with a label. Options may carry a decimal-string `score` for calculations.
- **Settings:** `placeholder`, plus the common `nextButton` and `imageAlt`.
- **Logic operators:** `eq`, `neq`, `has_value`, `is_empty`. Compare with an option ID from this question.
- **Calculations:** Add up the scores of the selected options with an `option_score` node.

```json title="Dropdown question"
{
    "id": "01990000-0000-7000-8000-000000000010",
    "kind": "dropdown",
    "title": {"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"Which country are you in?"}]}]},
    "description": {"type":"doc","content":[{"type":"paragraph","content":[]}]},
    "required": false,
    "options": [{"id":"01990000-0000-7000-8000-000000000011","label":"Option 1"},{"id":"01990000-0000-7000-8000-000000000012","label":"Option 2"}],
    "rows": [],
    "settings": {"nextButton":"Next"},
    "media": {"inherit":true,"assetId":null,"layout":"above"}
}
```

### `multiple_choice`

**Multiple choice.** Pick any number of choices.

- **Answer:** A list of distinct option IDs. When required, at least one option must be chosen.
- **Choices:** Needs at least 2 options, each with a label. Options may carry a decimal-string `score` for calculations.
- **Settings:** Only the common ones: `nextButton` and `imageAlt`.
- **Logic operators:** `contains`, `not_contains`, `any_of`, `has_value`, `is_empty`. Compare with an option ID from this question. any_of takes a list of option IDs.
- **Calculations:** Add up the scores of the selected options with an `option_score` node.

```json title="Multiple choice question"
{
    "id": "01990000-0000-7000-8000-000000000013",
    "kind": "multiple_choice",
    "title": {"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"Which services do you need?"}]}]},
    "description": {"type":"doc","content":[{"type":"paragraph","content":[]}]},
    "required": false,
    "options": [{"id":"01990000-0000-7000-8000-000000000014","label":"Option 1"},{"id":"01990000-0000-7000-8000-000000000015","label":"Option 2"}],
    "rows": [],
    "settings": {"nextButton":"Next"},
    "media": {"inherit":true,"assetId":null,"layout":"above"}
}
```

### `consent`

**Consent.** A single checkbox, such as agreeing to terms.

- **Answer:** true or false. When required, the respondent must check it.
- **Choices:** Not used. Keep `options` and `rows` empty.
- **Settings:** `label`, plus the common `nextButton` and `imageAlt`.
- **Logic operators:** `eq`, `neq`, `has_value`, `is_empty`. Compare with true or false.
- **Calculations:** Not usable.

```json title="Consent question"
{
    "id": "01990000-0000-7000-8000-000000000016",
    "kind": "consent",
    "title": {"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"Do you agree to the terms of the event?"}]}]},
    "description": {"type":"doc","content":[{"type":"paragraph","content":[]}]},
    "required": false,
    "options": [],
    "rows": [],
    "settings": {"nextButton":"Next","label":"I agree"},
    "media": {"inherit":true,"assetId":null,"layout":"above"}
}
```

### `nps`

**NPS.** Net Promoter Score: how likely someone is to recommend you, from 0 to 10.

- **Answer:** A whole number from 0 to 10, or {score, comment} when comment is on.
- **Choices:** Not used. Keep `options` and `rows` empty.
- **Settings:** `lowLabel`, `highLabel`, `comment`, plus the common `nextButton` and `imageAlt`.
- **Logic operators:** `eq`, `neq`, `has_value`, `is_empty`, `gt`, `gte`, `lt`, `lte`. Compare with a number or decimal string, such as "7" or "2.5".
- **Calculations:** Read the answer as a number with an `answer` node.

```json title="NPS question"
{
    "id": "01990000-0000-7000-8000-000000000017",
    "kind": "nps",
    "title": {"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"How likely are you to recommend us to a friend?"}]}]},
    "description": {"type":"doc","content":[{"type":"paragraph","content":[]}]},
    "required": false,
    "options": [],
    "rows": [],
    "settings": {"nextButton":"Next"},
    "media": {"inherit":true,"assetId":null,"layout":"above"}
}
```

### `opinion_scale`

**Opinion scale.** A rating on a short numbered scale, such as 1 to 5.

- **Answer:** A whole number from min to max, or {score, comment} when comment is on.
- **Choices:** Not used. Keep `options` and `rows` empty.
- **Settings:** `min`, `max`, `lowLabel`, `highLabel`, `comment`, plus the common `nextButton` and `imageAlt`.
- **Logic operators:** `eq`, `neq`, `has_value`, `is_empty`, `gt`, `gte`, `lt`, `lte`. Compare with a number or decimal string, such as "7" or "2.5".
- **Calculations:** Read the answer as a number with an `answer` node.

```json title="Opinion scale question"
{
    "id": "01990000-0000-7000-8000-000000000018",
    "kind": "opinion_scale",
    "title": {"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"How satisfied are you with our support?"}]}]},
    "description": {"type":"doc","content":[{"type":"paragraph","content":[]}]},
    "required": false,
    "options": [],
    "rows": [],
    "settings": {"nextButton":"Next"},
    "media": {"inherit":true,"assetId":null,"layout":"above"}
}
```

### `ranking`

**Ranking.** Put every choice in order of preference.

- **Answer:** A list of every option ID, in the respondent's order.
- **Choices:** Needs at least 2 options, each with a label. Options may carry a decimal-string `score` for calculations.
- **Settings:** Only the common ones: `nextButton` and `imageAlt`.
- **Logic operators:** `contains`, `not_contains`, `any_of`, `has_value`, `is_empty`. Compare with an option ID from this question. any_of takes a list of option IDs.
- **Calculations:** Add up the scores of the selected options with an `option_score` node.

```json title="Ranking question"
{
    "id": "01990000-0000-7000-8000-000000000019",
    "kind": "ranking",
    "title": {"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"Rank these features by importance."}]}]},
    "description": {"type":"doc","content":[{"type":"paragraph","content":[]}]},
    "required": false,
    "options": [{"id":"01990000-0000-7000-8000-000000000020","label":"Option 1"},{"id":"01990000-0000-7000-8000-000000000021","label":"Option 2"}],
    "rows": [],
    "settings": {"nextButton":"Next"},
    "media": {"inherit":true,"assetId":null,"layout":"above"}
}
```

### `matrix`

**Matrix.** Rate several rows on the same set of columns. Options are the columns.

- **Answer:** An object mapping row IDs to option IDs. When required, every row needs an answer.
- **Choices:** `options` are the columns and `rows` are the rows. Needs at least 2 labelled options and 1 labelled row.
- **Settings:** Only the common ones: `nextButton` and `imageAlt`.
- **Logic operators:** `has_value`, `is_empty`. These take no comparison value.
- **Calculations:** Add up the scores of the selected options with an `option_score` node.

```json title="Matrix question"
{
    "id": "01990000-0000-7000-8000-000000000022",
    "kind": "matrix",
    "title": {"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"How would you rate each part of the event?"}]}]},
    "description": {"type":"doc","content":[{"type":"paragraph","content":[]}]},
    "required": false,
    "options": [{"id":"01990000-0000-7000-8000-000000000023","label":"Option 1"},{"id":"01990000-0000-7000-8000-000000000024","label":"Option 2"}],
    "rows": [{"id":"01990000-0000-7000-8000-000000000025","label":"Row 1"}],
    "settings": {"nextButton":"Next"},
    "media": {"inherit":true,"assetId":null,"layout":"above"}
}
```

### `file_upload`

**File upload.** Attach one or more files, such as a CV or a photo.

- **Answer:** A list of uploaded file IDs, up to maxFiles.
- **Choices:** Not used. Keep `options` and `rows` empty.
- **Settings:** `maxFiles`, `maxSizeMB`, `accept`, plus the common `nextButton` and `imageAlt`.
- **Logic operators:** `has_value`, `is_empty`. These take no comparison value.
- **Calculations:** Not usable.

```json title="File upload question"
{
    "id": "01990000-0000-7000-8000-000000000026",
    "kind": "file_upload",
    "title": {"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"Please upload your CV."}]}]},
    "description": {"type":"doc","content":[{"type":"paragraph","content":[]}]},
    "required": false,
    "options": [],
    "rows": [],
    "settings": {"nextButton":"Next"},
    "media": {"inherit":true,"assetId":null,"layout":"above"}
}
```

### `statement`

**Statement.** Show text between questions. Collects no answer.

- **Answer:** None. Keep required false and leave options and rows empty.
- **Choices:** Not used. Keep `options` and `rows` empty.
- **Settings:** Only the common ones: `nextButton` and `imageAlt`.
- **Logic operators:** Cannot be used in conditions.
- **Calculations:** Not usable.

```json title="Statement question"
{
    "id": "01990000-0000-7000-8000-000000000027",
    "kind": "statement",
    "title": {"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"Next, a few questions about your budget."}]}]},
    "description": {"type":"doc","content":[{"type":"paragraph","content":[]}]},
    "required": false,
    "options": [],
    "rows": [],
    "settings": {"nextButton":"Continue"},
    "media": {"inherit":true,"assetId":null,"layout":"above"}
}
```

### `website`

**Website.** A website address.

- **Answer:** An http or https URL. A bare domain such as example.com is saved as https://example.com.
- **Choices:** Not used. Keep `options` and `rows` empty.
- **Settings:** `placeholder`, plus the common `nextButton` and `imageAlt`.
- **Logic operators:** `eq`, `neq`, `has_value`, `is_empty`, `contains`, `not_contains`. Compare with a string, such as "yes".
- **Calculations:** Not usable.

```json title="Website question"
{
    "id": "01990000-0000-7000-8000-000000000028",
    "kind": "website",
    "title": {"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"What is your website?"}]}]},
    "description": {"type":"doc","content":[{"type":"paragraph","content":[]}]},
    "required": false,
    "options": [],
    "rows": [],
    "settings": {"nextButton":"Next"},
    "media": {"inherit":true,"assetId":null,"layout":"above"}
}
```

## Logic operators

Each type above lists the operators it supports. [Logic rules](https://trulyform.com/docs/logic.md#conditions) explains every operator and the comparison values each type expects.


---

Previous: [Form definition](https://trulyform.com/docs/form-definition.md) · Next: [Logic](https://trulyform.com/docs/logic.md)

All pages: https://trulyform.com/llms.txt. Connected over MCP? Call read_docs with a page name instead.
