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

# Blocks

> The visual building units of content — text, image, button, embed, question, and columns — with their exact payloads.

**Blocks** are the visual units that make up content. They appear in a flow
step's `content[]`, and in the rich content of checklists and banners. (The
resource center has its own block vocabulary — see
[Type-specific data](/api-reference-v2/type-data#resource-center).) A block is a
small tagged object:

```json theme={null}
{ "object": "block", "id": "bk_…", "type": "text", "markdown": "Hello" }
```

* **`id`** is the field-merge write handle. Echo a block's `id` to update it in
  place (styling and other unmodeled details are preserved); **omit** `id` to
  create a new block. (`object: "block"` is returned on read; you don't send it.)
* **`type`** selects the block shape below.

Rich text is a small **markdown subset** — see
[Content representation](/api-reference-v2/content-representation#rich-text).
Conditions and actions referenced here are documented in
[Conditions & actions](/api-reference-v2/conditions-and-actions).

## text

A rich-text paragraph block.

| Field      | Type     | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| ---------- | -------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`       | string   |          | Field-merge write handle.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `type`     | `"text"` | yes      |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `markdown` | string   | yes      | A small markdown subset: paragraphs, `# `/`## ` headings (h1/h2 only — no h3+), `-`/`*` and `1.` lists, \`\`\` code fences; inline `**bold**`, `*italic*`, `[text](url)`, and `{{ attribute_code \| default: "x" }}` for user attributes. Anything outside this subset is SILENTLY normalized, not rejected: h3+ → h2; blockquotes flatten to paragraphs; tables, horizontal rules, strikethrough, inline images/code, and liquid filters other than `default` are dropped. Unsupported syntax won't round-trip — don't rely on it. |

```json theme={null}
{ "type": "text", "markdown": "Welcome **{{ first_name | default: \"there\" }}** 👋" }
```

## image

| Field    | Type                        | Required | Description               |
| -------- | --------------------------- | -------- | ------------------------- |
| `id`     | string                      |          | Field-merge write handle. |
| `type`   | `"image"`                   | yes      |                           |
| `url`    | string                      | yes      | Image URL (http/https).   |
| `alt`    | string                      |          | Alt text.                 |
| `link`   | `object{ url, newTab }`     |          | Wrap the image in a link. |
| `width`  | [Dimension](#shared-shapes) |          | Image width.              |
| `margin` | [Spacing](#shared-shapes)   |          | Outer margin.             |

```json theme={null}
{
  "type": "image",
  "url": "https://example.com/logo.png",
  "alt": "Logo",
  "width": { "unit": "percent", "value": 80 },
  "link": { "url": "https://example.com", "newTab": true }
}
```

## button

| Field          | Type                                                                | Required | Description                                                                                                                                                                                                                                                                     |
| -------------- | ------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`           | string                                                              |          | Field-merge write handle.                                                                                                                                                                                                                                                       |
| `type`         | `"button"`                                                          | yes      |                                                                                                                                                                                                                                                                                 |
| `text`         | string                                                              | yes      | Button label.                                                                                                                                                                                                                                                                   |
| `actions`      | [Action](/api-reference-v2/conditions-and-actions#actions)\[]       |          | What clicking does (e.g. go to a step, dismiss).                                                                                                                                                                                                                                |
| `disabledWhen` | [Condition](/api-reference-v2/conditions-and-actions#conditions)\[] |          | REACTIVE slot — polled live in the browser (the button disables the moment the conditions match). Client-evaluable condition types only: attribute / current\_url / element / text\_input / text\_filled / time\_window; event / segment / content\_state are rejected (E1017). |
| `hiddenWhen`   | [Condition](/api-reference-v2/conditions-and-actions#conditions)\[] |          | REACTIVE slot — polled live in the browser (the button shows/hides as conditions change). Same client-evaluable-only rule as `disabledWhen`.                                                                                                                                    |
| `variant`      | `primary` \| `secondary`                                            |          | Visual style.                                                                                                                                                                                                                                                                   |
| `margin`       | [Spacing](#shared-shapes)                                           |          | Outer margin.                                                                                                                                                                                                                                                                   |

A button needs both `text` **and** at least one action to be publishable.

```json theme={null}
{
  "type": "button",
  "text": "Next",
  "variant": "primary",
  "actions": [{ "type": "goto_step", "step": "pricing" }]
}
```

## embed

An embedded URL (video, iframe-able page, …).

| Field    | Type                        | Required | Description                                                                                                                                                                                                                                                                                                                                                                                 |
| -------- | --------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`     | string                      |          | Field-merge write handle.                                                                                                                                                                                                                                                                                                                                                                   |
| `type`   | `"embed"`                   | yes      |                                                                                                                                                                                                                                                                                                                                                                                             |
| `url`    | string                      | yes      | Paste the page's normal URL (e.g. a youtube.com/watch link) — on write it is resolved through the standard oEmbed provider registry (YouTube, Vimeo, Loom, Figma, …) and the provider's official embed markup is stored, so you never hand-build /embed/ URLs. A URL no provider claims is iframed as-is — it renders only if that site allows being framed (no X-Frame-Options/CSP block). |
| `width`  | [Dimension](#shared-shapes) |          | Width.                                                                                                                                                                                                                                                                                                                                                                                      |
| `height` | [Dimension](#shared-shapes) |          | Embed height. Optional ONLY for provider embeds (YouTube/Vimeo/… size themselves by aspect ratio); a URL with NO oEmbed provider has no ratio, and omitting a pixel height leaves the iframe at the browser's built-in default — a strip \~150px tall, almost never the intended size (validate warns). For plain-iframe URLs always set `{ "unit": "pixels", "value": … }`.                |
| `margin` | [Spacing](#shared-shapes)   |          | Outer margin.                                                                                                                                                                                                                                                                                                                                                                               |

```json theme={null}
{ "type": "embed", "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ", "width": { "unit": "percent", "value": 100 } }
```

## question

A survey question. The `question` field is one of the four [Question](#questions)
shapes below; `actions` run after the user answers.

| Field      | Type                                                          | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                       |
| ---------- | ------------------------------------------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`       | string                                                        |          | Field-merge write handle.                                                                                                                                                                                                                                                                                                                                                                                                         |
| `type`     | `"question"`                                                  | yes      |                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `question` | [Question](#questions)                                        | yes      | The question definition.                                                                                                                                                                                                                                                                                                                                                                                                          |
| `actions`  | [Action](/api-reference-v2/conditions-and-actions#actions)\[] |          | Actions that fire when this question is answered (on pick for nps/rating/single-select; on its Submit button for text/multi-select). Put a `goto_step` HERE to advance to the next step — without it the question records the answer but the flow does NOT advance (validate flags the next step "not reachable"). Do NOT add a separate `button` block just to advance: it doubles up with the question's own submit affordance. |

```json theme={null}
{
  "type": "question",
  "question": { "kind": "rating", "name": "Satisfaction", "style": "star", "range": { "low": 1, "high": 5 } }
}
```

### Questions

Every question needs a `name` (used for analytics). `bindAttribute` (optional)
stores the answer on a user attribute. `cvid` is server-owned (returned on read).

#### `nps`

| Field           | Type    | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------- | ------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `kind`          | `"nps"` | yes      |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `name`          | string  | yes      | The question's internal name / analytics label (it is the `questionName` on captured responses). It is NOT rendered to the user — the widget shows only the input (scale / options / text field), not this string. To show a visible question prompt, add a `text` block in the SAME step before the question block; a question with only a `name` renders as bare options with no question text.                                                                                                                                                                                                                                                             |
| `cvid`          | string  |          |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `lowLabel`      | string  |          |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `highLabel`     | string  |          |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `bindAttribute` | string  |          | Optional: codeName of an EXISTING attribute (create the attribute definition first) to ALSO save this answer onto the user for targeting/segmentation — use the codeName, NOT the id. The write does not check it, but the version validation WARNS when the attribute is missing or its dataType mismatches the answer — read warnings. A wrong code that slips through silently captures nothing at runtime. Match the attribute dataType to the answer: number (nps / rating), string (single-select choice), list (multi-select choice). Leaving it unset still records the answer as a response event — bind only when you need to target/segment on it. |

#### `rating`

| Field           | Type                  | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------- | --------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `kind`          | `"rating"`            | yes      |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `name`          | string                | yes      | The question's internal name / analytics label (it is the `questionName` on captured responses). It is NOT rendered to the user — the widget shows only the input (scale / options / text field), not this string. To show a visible question prompt, add a `text` block in the SAME step before the question block; a question with only a `name` renders as bare options with no question text.                                                                                                                                                                                                                                                             |
| `cvid`          | string                |          |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `style`         | `star` \| `scale`     | yes      | star = star rating; scale = a numeric scale. A "scale" question IS a rating with style:"scale" — there is no separate "scale" kind.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `range`         | `object{ low, high }` | yes      | Numeric range, e.g. `{ low: 1, high: 5 }`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `lowLabel`      | string                |          |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `highLabel`     | string                |          |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `bindAttribute` | string                |          | Optional: codeName of an EXISTING attribute (create the attribute definition first) to ALSO save this answer onto the user for targeting/segmentation — use the codeName, NOT the id. The write does not check it, but the version validation WARNS when the attribute is missing or its dataType mismatches the answer — read warnings. A wrong code that slips through silently captures nothing at runtime. Match the attribute dataType to the answer: number (nps / rating), string (single-select choice), list (multi-select choice). Leaving it unset still records the answer as a response event — bind only when you need to target/segment on it. |

#### `text`

| Field           | Type     | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| --------------- | -------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `kind`          | `"text"` | yes      |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `name`          | string   | yes      | The question's internal name / analytics label (it is the `questionName` on captured responses). It is NOT rendered to the user — the widget shows only the input (scale / options / text field), not this string. To show a visible question prompt, add a `text` block in the SAME step before the question block; a question with only a `name` renders as bare options with no question text.                                                                                                                                                                                                                                                             |
| `cvid`          | string   |          |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `multiline`     | boolean  | yes      |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `placeholder`   | string   |          |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `buttonText`    | string   |          |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `required`      | boolean  |          | Require an answer before submit. ONLY `text` supports this — nps / rating / choice cannot be marked required.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `bindAttribute` | string   |          | Optional: codeName of an EXISTING attribute (create the attribute definition first) to ALSO save this answer onto the user for targeting/segmentation — use the codeName, NOT the id. The write does not check it, but the version validation WARNS when the attribute is missing or its dataType mismatches the answer — read warnings. A wrong code that slips through silently captures nothing at runtime. Match the attribute dataType to the answer: number (nps / rating), string (single-select choice), list (multi-select choice). Leaving it unset still records the answer as a response event — bind only when you need to target/segment on it. |

#### `choice`

| Field              | Type                        | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| ------------------ | --------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `kind`             | `"choice"`                  | yes      |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `name`             | string                      | yes      | The question's internal name / analytics label (it is the `questionName` on captured responses). It is NOT rendered to the user — the widget shows only the input (scale / options / text field), not this string. To show a visible question prompt, add a `text` block in the SAME step before the question block; a question with only a `name` renders as bare options with no question text.                                                                                                                                                                                                                                                             |
| `cvid`             | string                      |          |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `options`          | `object{ label, value }`\[] | yes      | Each option has a human-facing `label` and a stored `value` — the `value` is what gets recorded/bound as the answer.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `allowMultiple`    | boolean                     | yes      | false = single-select, true = multi-select. A multi-select answer needs a `list`-typed bound attribute.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `enableOther`      | boolean                     |          |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `otherPlaceholder` | string                      |          |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `shuffle`          | boolean                     |          |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `buttonText`       | string                      |          |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `bindAttribute`    | string                      |          | Optional: codeName of an EXISTING attribute (create the attribute definition first) to ALSO save this answer onto the user for targeting/segmentation — use the codeName, NOT the id. The write does not check it, but the version validation WARNS when the attribute is missing or its dataType mismatches the answer — read warnings. A wrong code that slips through silently captures nothing at runtime. Match the attribute dataType to the answer: number (nps / rating), string (single-select choice), list (multi-select choice). Leaving it unset still records the answer as a response event — bind only when you need to target/segment on it. |

## columns

A row of side-by-side columns, each holding its own blocks — the only nesting in
the block model.

| Field     | Type        | Required | Description                                                                                                   |
| --------- | ----------- | -------- | ------------------------------------------------------------------------------------------------------------- |
| `id`      | string      |          | Field-merge write handle.                                                                                     |
| `type`    | `"columns"` | yes      |                                                                                                               |
| `columns` | Column\[]   | yes      | One entry per column, laid out left-to-right. Each column is a mini vertical stack of `blocks` (usually one). |

```json theme={null}
{
  "type": "columns",
  "columns": [
    { "width": { "unit": "percent", "value": 50 }, "blocks": [{ "type": "text", "markdown": "Left" }] },
    { "blocks": [{ "type": "image", "url": "https://example.com/x.png" }] }
  ]
}
```

## Shared shapes

Small objects reused across blocks.

**Dimension** — a width/height.

| Field   | Type                            | Required | Description                                                                                                     |
| ------- | ------------------------------- | -------- | --------------------------------------------------------------------------------------------------------------- |
| `unit`  | `percent` \| `pixels` \| `fill` | yes      | `fill` ignores `value`, and is a **column-only** unit — an image/embed width or height with `fill` is rejected. |
| `value` | number                          |          | ≥ 0. For `percent`, values over 100 are allowed (overflow).                                                     |

**Spacing** — margin/padding box (pixels; omitted sides inherit the theme).

| Field                               | Type    | Required | Description                        |
| ----------------------------------- | ------- | -------- | ---------------------------------- |
| `enabled`                           | boolean |          | Whether spacing is applied.        |
| `top` / `bottom` / `left` / `right` | number  |          | Per-side pixels (may be negative). |

**Column** — one column inside a `columns` block.

| Field     | Type                                                              | Required | Description              |
| --------- | ----------------------------------------------------------------- | -------- | ------------------------ |
| `width`   | Dimension                                                         |          | Column width.            |
| `justify` | `start` \| `center` \| `end` \| `between` \| `around` \| `evenly` |          | Horizontal distribution. |
| `align`   | `start` \| `center` \| `end` \| `baseline`                        |          | Vertical alignment.      |
| `padding` | Spacing                                                           |          | Inner padding.           |
| `blocks`  | Block\[]                                                          | yes      | The column's blocks.     |
