The chart component renders a bar, line, or pie chart on the canvas. It is display-only.
| |
|---|
| Component id | canvas.chart |
| Version | v1 |
| Action name | canvas_show_chart |
| Interaction class | Lifecycle-only (no submit or skip) |
| Default placement | Side rail (safe-area-right), rendered inline |
| Live updates | Yes, the PAL can update a visible chart in place |
chart is enabled on any PAL with the Magic Canvas skill attached; see Enabling and configuring components. Its only config key is enabled (default true):
{ "config": { "components": { "chart": { "enabled": false } } } }
Behavior
The PAL decides when to draw a chart; there is no developer-triggered path. Its system prompt directs it to chart small numeric comparisons or trends, with short labels and numeric values.
The PAL revises a visible chart in place by calling update_component with new data.
Arguments
| Field | Type | Required | Description |
|---|
chart_type | "bar" | "line" | "pie" | ❌ | Which chart to draw (default bar). |
data | array of {label, value} | ✅ | The data points. At least 1; the renderer accepts at most 12. |
data[].label | string | ✅ | Label for the point (renderer limit: 1–80 characters). |
data[].value | number | ✅ | Numeric value for the point. |
title | string | ❌ | Heading above the chart (renderer limit: 1–120 characters). |
x_label | string | ❌ | X-axis label (renderer limit: 1–80 characters). |
y_label | string | ❌ | Y-axis label (renderer limit: 1–80 characters). |
No other arguments are accepted. Tavus also injects two runtime controls shared by every Canvas action: layout (preferred slot, one of safe-area-right or safe-area-left) and display_mode (enum [inline], always inline).
Arguments exceeding the limits above produce a load error card instead of a
chart.
Example invocation
{
"title": "Pipeline",
"chart_type": "bar",
"data": [
{ "label": "Qualified", "value": 18 },
{ "label": "Demo", "value": 11 },
{ "label": "Closed", "value": 4 }
],
"x_label": "Stage",
"y_label": "Count"
}
Interaction Types
chart is lifecycle-only: sending submit or skip returns a 400 (canvas.chart does not support this interaction type.). The interactions endpoint accepts four types:
| Type | What it means |
|---|
dismiss | The user closed the card |
clear | The canvas was cleared while the chart was on screen |
error | The client failed to display the chart |
heartbeat | A custom client’s periodic status ping |
Expect canvas.chart interactions only from clients you build yourself. The
stock Tavus SDK and hosted embed currently POST none of these: the card has
no dismiss control, render failures surface only via the local onError
callback, clearing the canvas removes instances locally without an
interaction, and renderer heartbeats use a separate model-context
channel that never reaches the interactions endpoint or your webhook.
Custom clients POST interactions to POST https://tavusapi.com/v2/conversations/{conversation_id}/canvas/interactions (validation rules).
Each recorded interaction fires your conversation webhook once as a canvas.interaction event (message_type: "canvas") and is available via GET https://tavusapi.com/v2/conversations/{conversation_id}/canvas/interactions (API key required).
Example Webhook Event
A dismiss POSTed by a custom client:
{
"message_type": "canvas",
"event_type": "canvas.interaction",
"properties": {
"conversation_id": "c123456",
"interaction_id": "ci_call_abc_dismiss_8f2e1c9a",
"tool_call_id": "call_abc",
"component": "canvas.chart",
"component_version": "v1",
"type": "dismiss",
"value": {},
"metadata": {},
"created_at": "2026-06-09T21:14:03.214311"
}
}
created_at is a Python isoformat() timestamp: UTC but timezone-naive,
with microseconds and no trailing Z or offset. The same shape appears
in the GET response.
For lifecycle types, Tavus validates the envelope (ids, component, type) but
only enforces that value is an object under 16 KB; don’t depend on
specific keys inside it.