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

# UI block reference

> Every confirmation-view block — layout, display, inputs, actions, and bindings.

Interactive blocks are the vocabulary of
[confirmation cards](/integrations/confirmations) and setup forms — declared
statically as a `UiView` in the manifest:

```json theme={null}
{ "schemaVersion": 1, "root": { "type": "card", "children": [ … ] } }
```

`schemaVersion` is `1`. Hosts render newer views' unknown blocks as safe
placeholders rather than failing.

## Common props

Every block accepts:

| Prop        | Type     | Notes                                                                                                         |
| ----------- | -------- | ------------------------------------------------------------------------------------------------------------- |
| `id`        | `string` | Optional stable identifier.                                                                                   |
| `visibleIf` | binding  | Renders the block only while the bound value is truthy. Hidden required inputs don't gate the confirm action. |

## Tokens

| Token         | Values                                                                                    |
| ------------- | ----------------------------------------------------------------------------------------- |
| `ColorToken`  | `default` · `muted` · `subtle` · `accent` · `success` · `warning` · `danger` · `onAccent` |
| `SpaceToken`  | `none` · `xs` · `sm` · `md` · `lg` · `xl`                                                 |
| `TextVariant` | `title` · `subtitle` · `body` · `caption` · `mono`                                        |

Colors are semantic tokens — the host owns the palette, so cards inherit
every restyle.

## Layout blocks

### `card`

| Prop       | Type        | Required                    |
| ---------- | ----------- | --------------------------- |
| `title`    | `string`    | —                           |
| `icon`     | icon ref    | —                           |
| `children` | `UiBlock[]` | ✅                           |
| `footer`   | `UiBlock[]` | — (put your `actions` here) |

### `stack`

| Prop        | Type                                        | Notes        |
| ----------- | ------------------------------------------- | ------------ |
| `direction` | `"vertical" \| "horizontal"`                | —            |
| `gap`       | `SpaceToken`                                | default `sm` |
| `align`     | `"start" \| "center" \| "end" \| "stretch"` | —            |
| `children`  | `UiBlock[]`                                 | ✅ required   |

## Display blocks

| Block      | Props                                                                          |
| ---------- | ------------------------------------------------------------------------------ |
| `text`     | `text` ✅, `variant?`, `color?`, `maxLines?` — supports `{{arg}}` interpolation |
| `markdown` | `text` ✅ — sanitized; raw HTML stripped                                        |
| `keyValue` | `rows: { label, value, color? }[]` ✅                                           |
| `metadata` | `rows: { label, value?, link?, tags?, icon?, color? }[]` ✅                     |
| `list`     | `children: UiBlock[]` ✅ (typically `listItem`s)                                |
| `listItem` | `title` ✅, `subtitle?`, `icon?`, `accessories?`                                |
| `image`    | `src` ✅ (URL), `alt?`, `ratio?`                                                |
| `badge`    | `text` ✅, `color?`                                                             |
| `progress` | `value?` (0–1), `label?`                                                       |
| `divider`  | —                                                                              |

Icon refs accept `https:` or `data:` image URLs; unresolved token names
render nothing (falling back to your integration's icon where the host shows
one) rather than a broken image.

## Input blocks

All inputs take `bind` (required), `label?`, and `required?`:

| Block           | Extra props                     | Renders                                         |
| --------------- | ------------------------------- | ----------------------------------------------- |
| `textField`     | `placeholder?`, `multiline?`    | Text input                                      |
| `passwordField` | `placeholder?`                  | Secure input                                    |
| `select`        | `options: { label, value }[]` ✅ | Picker                                          |
| `toggle`        | —                               | Switch (never gates confirm — off is an answer) |
| `chips`         | `placeholder?`                  | Tag/multi-value input                           |

`required` inputs gate the confirm action until they hold a non-whitespace
value; the marker is a quiet permanent `*`.

## Action blocks

### `actions`

```json theme={null}
{ "type": "actions", "items": [
  { "label": "Cancel", "role": "cancel" },
  { "label": "Send", "role": "confirm", "color": "accent" }
] }
```

| Role      | Behavior                                                                                                            |
| --------- | ------------------------------------------------------------------------------------------------------------------- |
| `confirm` | Approves; the tool executes with staged edits. First confirm renders as the primary pill. Gated by required inputs. |
| `cancel`  | Declines; the tool never runs. Never gated.                                                                         |
| `copy`    | Copies `value` to the clipboard. Never gated.                                                                       |
| `openUrl` | Opens `url` in the browser. Never gated.                                                                            |
| `custom`  | **Reserved** — rejected by validation in v1.                                                                        |

Give a view exactly one `confirm` and one `cancel`. `button` is the
standalone single-action variant (`label` ✅, `role?`, `actionId?`, `color?`,
`disabledIf?`).

## `widget`

A view may instead be a single sandboxed HTML block —
see [Widget confirmations](/integrations/widget-confirmations):

| Prop           | Type              | Cap                                                  |
| -------------- | ----------------- | ---------------------------------------------------- |
| `html`         | `string` ✅        | ≤ 131,072 chars                                      |
| `height`       | `number`          | 60–420                                               |
| `data`         | JSON-serializable | ≤ 16,384 chars serialized                            |
| `label`        | `string`          | ≤ 60 chars                                           |
| `confirmLabel` | `string`          | ≤ 60 chars; labels VoiceOS's floating confirm button |

Rules: at most one widget per view; no `button`/`actions` blocks anywhere
alongside it.

## `canvas`

**Reserved.** `{ "type": "canvas", "src", "height", "maxHeight?" }` — an
MCP-Apps-style embedded resource. Declared in the contract, rejected by
validation in `schemaVersion: 1`.

## Bindings

A binding is `{{name}}` — whitespace-tolerant, dotted paths allowed
(`{{user.name}}`):

* **In `text`**, bindings interpolate into the string.
* **On inputs**, `bind: "{{url}}"` shows the argument's current value and
  stages edits. On approval, staged edits are folded back into the argument
  object (nested paths fold into their top-level key) and **replace** the
  agent's proposed values.
* Every bound name must exist in the tool's `inputSchema` properties —
  validated at install.

Validate any view standalone with `validateView(view)` from
`@voiceos/integration-sdk`.
