Show a multiple-choice question card on screen and get a structured answer back.
The question component (canvas.question) shows a multiple-choice question card during a conversation. The user taps an option (or types a free-text answer, when allowed) and the structured result goes to the PAL and your conversation webhook.
The PAL shows the card by invoking canvas_show_question when the user should answer a structured question. Steer the timing in the PAL’s system prompt.
Renders in the safe-area-right slot by default.
A repeat canvas_show_question invocation replaces the card currently on screen with a new instance and a new tool_call_id; in-progress input is lost and interactions arrive under the new id.
Only one canvas card is on screen at a time. A new canvas_show_question invocation always replaces the current card, even when it targets a different side slot (e.g. layout.preferred_slot: "safe-area-left"); two cards never appear at once.
Every Canvas PAL also gets an update_component control action that patches the existing card without replacing it.
The question text shown on the card. Must be non-empty.
options
array
✅
The selectable options. 2–10 items. Option ids must be unique.
options[].id
string
✅
Stable identifier, echoed back verbatim in selected_option_ids.
options[].label
string
✅
Human-readable option text shown to the user.
options[].allow_free_text
boolean
❌
When true, selecting this option reveals a text input for extra detail on that specific choice. Independent of allow_other.
options[].free_text_placeholder
string
❌
Placeholder for that option’s detail input.
allow_skip
boolean
❌
Whether the user may skip the question. Default false.
multi_select
boolean
❌
Whether the user may select more than one option. Default false.
allow_other
boolean
❌
Adds an “Other” affordance the user can expand to type a free-text answer. Single-select: mutually exclusive with the preset options. Multi-select: submitted alongside them. Default false.
other_label
string
❌
Label for the “Other” affordance (e.g. “None of the above”). A generic label is used when omitted.
other_placeholder
string
❌
Placeholder for the “Other” text input.
correct_answer
string
❌
Option id of the correct answer for quiz-style questions. When set, the card briefly reveals the correct option against the user’s choice after they submit, then clears. Must match one of options[].id. Omit for surveys, preferences, and open-ended questions; use only when there is one objectively correct option.
Tavus injects two runtime controls on every Canvas action:
{ "question": "Which database should we use for the new service?", "options": [ { "id": "postgres", "label": "PostgreSQL" }, { "id": "mysql", "label": "MySQL" }, { "id": "sqlite", "label": "SQLite" } ], "allow_other": true, "other_label": "None of the above", "other_placeholder": "Tell us what you'd prefer"}
question is a submit-capable component emitting six interaction types: submit, skip, dismiss, clear, error, and heartbeat. Only submit and skip carry an answer value; the rest are lifecycle signals with no question payload. Each interaction reaches your conversation webhook as a canvas.interaction event with the answer in properties.value.
The chosen option ids, echoed exactly as the PAL set them. Empty on skip, or when the user answered only via “Other”.
skipped
boolean
✅
true only on skip.
custom_text
string
❌
The free-text answer from the “Other” affordance. Non-empty, at most 4000 characters.
option_texts
object
❌
Per-option detail text, keyed by option id. Keys must be a subset of selected_option_ids. Each value is non-empty, at most 2000 characters.
Tavus rejects values with any other keys and enforces these rules before delivery:
skip: always skipped: true, with no selections, custom_text, or option_texts.
submit: always skipped: false, with at least one selected id or a non-empty custom_text; never an empty answer.
The hosted card offers Skip only when allow_skip is true. Tavus does not re-check an incoming skip against the original action arguments, so a custom renderer could post one regardless.
properties always carries the same nine keys. interaction_id uniquely identifies the interaction; tool_call_id ties it to the originating canvas_show_question call. created_at is a naive ISO-8601 timestamp with microseconds and no timezone suffix (no trailing Z); treat it as UTC.