---
title: Survey definition
description: Reference the complete editable survey JSON, question types, invitations, translations and publication lifecycle.
url: https://docs.userreason.com/prompts/definition/
updated: 2026-11-10
---

# Survey definition

Conversational voice (Pip’s **Text or voice** mode) is **not generally available** and is marked **Coming soon**. Ordinary text-question dictation is a separate feature; see [conversation availability](https://docs.userreason.com/prompts/conversations/index.md).

The CLI accepts a complete survey definition file for `prompt create` and `prompt update`. Updates replace the editable definition rather than patching a few fields. Start with the valid [first survey definition](https://docs.userreason.com/get-started/first-prompt/index.md#save-a-complete-definition).

## Top-level fields

All fields in this table are required. Unknown fields are rejected.

| Field | Type and meaning |
| - | - |
| `schema_version` | `1`. |
| `name` | Internal name, 1–240 characters. |
| `content` | Questions, presentation and language; described below. |
| `rules` | `{ schema_version: 1, audience, triggers, frequency }`, with an optional `saved_audience`; see [Targeting](https://docs.userreason.com/prompts/targeting/index.md). |
| `sampling_percent` | Number from 0 to 100. |
| `priority` | Integer from 0 to 2147483647. Higher wins. |
| `enrollment_epoch` | Stable enrollment identifier. Keep it during ordinary edits to preserve the sample and each person's counts and waits. |

Identifiers, including question and option IDs, start with a letter or digit and contain only letters, digits, `.`, `_`, `:` and `-`, up to 160 characters. They are not display labels. Keep them stable when wording changes; choose new IDs when the meaning changes.

## Content

Required content fields are `schema_version: 1`, `title` (up to 240 characters), `description` (string up to 4000 or `null`), `questions` (1–64 entries), and `thank_you` (string up to 2000 or `null`).

Optional fields include `invitation`, `asker`, `language`, `translations`, `collect_partial_responses` and the explicit `flow` used for [experiments](https://docs.userreason.com/prompts/experiments/index.md). Explicit `null` is allowed for `invitation` and `asker`. Drafts may contain unfinished text; publication validates that questions, labels, images and translations are ready.

[Text conversations with Pip](https://docs.userreason.com/prompts/conversations/index.md) are available in production and development through an optional `conversation` setting on an entry-only terminal text question, subject to workspace AI settings and allowance.

Supported choice, matrix and ranking questions can set `answer_order`; see [Answer randomisation](https://docs.userreason.com/prompts/answer-order/index.md). Read [Writing useful surveys](https://docs.userreason.com/prompts/survey-quality/index.md) for choosing wording, options and timing.

`content.collect_partial_responses` is an optional boolean, enabled when omitted or `true`. Set it to `false` to collect only the final response. SDK 0.4.0 or later saves validated answers when the visitor advances with **Next**; typing alone does not save them. See [Partial responses](https://docs.userreason.com/prompts/partial-responses/index.md) for save boundaries and results.

## Questions

Every question requires `id`, `kind`, `prompt` and `required`. Question IDs are unique within the survey. Question text is at most 2000 characters. The following fields are additional to those common fields.

| Kind | Required fields | Optional fields and constraints |
| - | - | - |
| `rating` | `min`, `max`, `min_label`, `max_label` | Integer range within 0–10 with `max` greater than `min`. Endpoint labels are strings or `null`. Optional `style` and `labels`. |
| `text` | `max_length` | 1–10000 characters. Optional `lines: 'single'` or `'multiple'`; single-line answers are limited to 200. Optional `dictation: { language: 'prompt' \| 'detect', filler_words: 'remove' \| 'keep' }` lets visitors answer by voice; see [voice answers](https://docs.userreason.com/sdk/voice-answers/index.md). |
| `single_choice` | `options` | 2–32 listed options, or up to 250 with `display: 'dropdown'`. Optional `columns: 1` or `2`. Dropdowns cannot use images. |
| `multiple_choice` | `options`, `min_selections`, `max_selections` | 2–32 options; selection limits must be positive and fit the options. Optional `columns: 1` or `2`. |
| `matrix` | `rows`, `min`, `max`, `min_label`, `max_label` | 2–5 unique rows, each `{ id, label }`. Rating range 0–10. Optional `style: 'numbers'`, `'stars'` or `'hearts'`. |
| `ranking` | `options`, `top` | 2–10 `{ id, label }` options. `top` is the number to rank, from 1 to the number of options. |
| `date` | `entry`, `earliest`, `latest` | `entry` is `'picker'` or `'parts'`. Each bound is `null`, `'today'`, or a valid `YYYY-MM-DD` date. |
| `fields` | `fields`, `purpose`, `consent` | 1–8 typed fields, optional purpose text represented as a string or `null`, and consent details or `null`. |

Questions are presented in sequence unless they carry logic (below). Older installed SDKs can be withheld surveys containing question types they do not support; verify your app's installed version when using newer types.

### Exclusive choices and selected-option follow-ups

SDK 0.5.3 supports `exclusive: true` on a multiple-choice option, such as “None of these”. Selecting it clears the other selections; selecting an ordinary option clears the exclusive one. A question using an exclusive option must have `min_selections: 1`.

A `single_choice` question can set `options_source_question_id` to the ID of an earlier `multiple_choice` question. It then offers only the options the visitor selected there, excluding exclusive options. Its required `options` array can be empty or contain additional fixed choices with distinct IDs. The source choices plus fixed choices must fit within 32 options.

For example, ask which problems a visitor encountered, then which selected problem mattered most. Changing the earlier answer updates the follow-up choices. The SDK requests `choice_logic=v1`; older SDKs are not served surveys requiring this capability.

### Logic and endings

Every question can carry an optional `logic` object, and the content an optional `endings` list.

| Field | Meaning |
| - | - |
| `logic.show_when` | A condition on earlier answers. When it does not hold, the question is passed over. |
| `logic.rules` | 1–16 rules of `{ when, to }`, tried in order. The first match decides where the visitor goes. |
| `logic.otherwise` | Where to go when no rule matches. Without it, the next question follows. |
| `endings` | Up to 16 named endings of `{ id, kind, name, message }` beside the default one. `kind` is `'completion'` or `'screen_out'`; `message` is a string or `null` for the thank-you text. |

A destination (`to`, `otherwise`) is `{ type: 'question', id }` for a later question, `{ type: 'ending', id }` for a named ending, or `{ type: 'end' }` for the default ending. Routes only go forward.

A condition is `{ type: 'answer', question_id, comparison }`, or `{ type: 'all' | 'any', conditions }`. The comparison's `operator` depends on the question it reads:

| Question | Operators |
| - | - |
| Any | `answered`, `skipped` |
| `single_choice` | `is`, `is_not` (one option ID in `value`); `one_of`, `not_one_of` (a list) |
| `multiple_choice` | `contains_any`, `contains_all`, `contains_none`, `exactly` (a list of option IDs) |
| `rating` | `eq`, `gt`, `gte`, `lt`, `lte` (a number in `value`); `between` (`min` and `max`) |
| `text` character count | `length_eq`, `length_gt`, `length_gte`, `length_lt`, `length_lte` (a number in `value`); `length_between` (`min` and `max`) |
| `text` literal matching | `text_eq`, `text_not_eq`, `text_contains`, `text_not_contains`, `text_starts_with`, `text_not_starts_with`, `text_ends_with`, `text_not_ends_with` (text in `value`) |

Text conditions ignore leading and trailing whitespace and normalize Unicode to NFC. Character counts use Unicode code points, so a simple emoji counts once; a combined emoji can contain several code points. Text matching ignores letter case and treats punctuation literally. Counts range from 0 to 10,000, ranges include both ends, and text to match must not be blank. Skipped, unseen and blank answers never satisfy a text condition, including a negative one. Use `skipped` to test for a missing answer.

For example, add this `logic` to a clarification question after an open-text question whose ID is `feedback`. It is shown only when the previous answer has fewer than 12 characters:

```json
{
  "show_when": {
    "type": "answer",
    "question_id": "feedback",
    "comparison": { "operator": "length_lt", "value": 12 }
  }
}
```

The original answer and the clarification are kept separately. Combine conditions with `all` or `any`, such as a low rating and a short explanation. A compatible SDK requests `flow_text=v1`; the API omits surveys using these comparisons from older SDKs.

A question the visitor never reached matches no comparison. A response that finishes with a `screen_out` ending is saved, counted apart from completions, and the visitor is not asked that survey again unless its [frequency](https://docs.userreason.com/prompts/targeting/index.md#frequency) says to ask again after a screen-out. Publishing refuses logic that points to a missing question, option or ending, goes back to an earlier question, or reads an answer that comes later.

### Rating styles

The default `numbers` style supports the full range. `stars` and `hearts` require a minimum of 1 and maximum of at most 7. `smileys` uses 1–3 or 1–5. `thumbs` and `yes_no` use 0–1.

`labels`, when provided, has one string per value in ascending order. It is required for smileys, thumbs and yes/no, so visitors know what each value means. The stored answer is always the chosen integer, regardless of style.

### Choices and other answers

Each choice has a unique `id` and a `label` up to 500 characters. An optional `image` is `{ url, alt }`; published image URLs must be HTTPS. At most one option can have `other: true`, which requests up to 500 characters of the visitor's own words. Responses retain the option ID and that text separately.

### Typed fields

Each field is `{ id, type, label, required }`. Supported types are `name`, `email`, `phone`, `company`, `address` and `text`; values are strings of at most 500 characters. Supported contact traits can prefill name, email, phone and company fields.

`purpose` explains why you are asking. `consent` is `null` or `{ label, link_label, link_url }`. Link label and URL must either both be `null` or both supplied; published links must be HTTPS. When consent is configured, the visitor must tick it before those details are sent. This question-level consent is separate from your site's SDK storage consent flow.

## Invitations and asker

An invitation requires `style`, `heading`, `message`, `reward`, `minutes` and `decline_forever`:

- `style` is `card`, `compact` or `question`.
- `heading` is up to 240 characters. `message` is up to 1000 or `null`; `reward` is up to 240 or `null`.
- `minutes` is an integer from 1 to 60, or `null` to use the question-based estimate.
- `decline_forever` enables the option to stop this survey for that visitor.

A `question` invitation needs a rating first question, or a listed single-choice first question with at most five options.

An optional `asker` is a person, `{ type: 'person', name, role, photo_url }`, with nullable role and photo; or a character, `{ type: 'character', variant: 'pip', name, color }`, with nullable six-digit hex color. Published photos use HTTPS. These options change presentation, not the question IDs or answer values.

## Languages

Set `language` to the default language tag. `translations` maps up to 29 additional language tags to translated content; do not include the default language again.

A translation can contain `title`, `description`, `thank_you`, invitation text (`heading`, `message`, `reward`), an asker's `role`, and `questions` keyed by question ID. Question translations support `prompt`, endpoint labels, value `labels`, option labels in `options`, image descriptions in `alts`, matrix `rows`, typed `fields`, `purpose`, `consent` and `consent_link`. Maps refer to existing IDs; rating value labels are keyed by their number as a string.

Publishing checks that configured translations have the required text. Draft review markers in `needs_review` identify text that still needs attention. SDK language selection is described under [Appearance and language](https://docs.userreason.com/sdk/presentation/index.md).

## Drafts, versions and responses

Create and update change the editable draft. Publish makes a validated version live. `prompt get` reports the current definition, publication state, revision and whether there are unpublished changes. Mutations require the current revision to prevent overwriting another edit.

Responses remain associated with the exact admitted survey version. Editing a question today does not reinterpret earlier answers. See [SDK notifications](https://docs.userreason.com/sdk/notifications/index.md#response) for response values and [CLI](https://docs.userreason.com/cli/index.md#surveys) for lifecycle commands.
