The calendar component displays a date or time picker mid-conversation. The user’s selection is delivered to your webhook as structured data: a YYYY-MM-DD string or an ISO-8601 slot.
The calendar shows only the dates and slots in the invocation; it does not fetch availability. For live booking against a Calendly scheduling page, use scheduling_embed.
calendar is enabled whenever the Magic Canvas skill is attached and has no component-specific configuration; see Enabling and configuring components.
Modes
mode | What the user sees | What comes back on submit |
|---|
date_picker | A month calendar, optionally with quick-pick preset buttons | selected_date (YYYY-MM-DD) |
slot_picker | A list of concrete time slots | selected_slot (or selected_slots with multi_select) |
date_time | Calendly-style: pick a date, then a slot on that date | selected_slot (or selected_slots with multi_select) |
range | A start–end date range picker | selected_range ({start, end}) |
PAL Behavior
The PAL is prompted to invoke canvas_show_calendar when the user should pick a date or time slot, inferring mode, multi_select, date_range, initial_month, and initial_year from the conversation. You don’t trigger the action directly.
Slot times come from the PAL’s context. Provide availability through your system prompt, conversational context, or a tool from your Tools library.
Arguments
The same fields appear in update_component patches; dates use YYYY-MM-DD.
| Field | Type | Required | Description |
|---|
title | string | ❌ | Heading on the card. 1–120 characters. |
timezone | string | ❌ | IANA timezone name (e.g. America/Los_Angeles) used to present times. 1–120 characters. |
mode | string | ❌ | date_picker, slot_picker, date_time, or range. If omitted: slots present → slot_picker, otherwise date_picker. |
multi_select | boolean | ❌ | Slot-bearing modes only (slot_picker, date_time): let the user pick more than one slot. Defaults to false. |
slots | array | ✅ when mode is slot_picker or date_time | 1–24 entries. Each slot: id (required), start (required, ISO-8601), end (required, ISO-8601), label (optional display text). |
presets | array | ❌ | Up to 6 quick-pick buttons for date_picker. Each: label (required, ≤40 chars) and date (required, YYYY-MM-DD). |
initial_date | string | ❌ | Pre-selected date, YYYY-MM-DD. |
initial_month | string | ❌ | Month the calendar opens on, YYYY-MM. |
initial_year | integer | ❌ | Year the calendar opens on, 1–9999. |
date_range | object | ❌ | { "start", "end" }, both YYYY-MM-DD, inclusive. Limits which dates are selectable. |
highlighted_dates | array of strings | ❌ | Up to 62 YYYY-MM-DD dates to visually highlight. |
allow_skip | boolean | ❌ | Shows a skip button so the user can decline to pick. Defaults to false. |
layout | object | ❌ | Placement control available on every component action: { "preferred_slot": "safe-area-right" | "safe-area-left" }. Calendar cards default to safe-area-right. |
display_mode | string | ❌ | "inline" (the only supported value). Available on every component action. |
Interactions
calendar produces six interaction types: submit, skip, dismiss, clear, error, and heartbeat. Each is delivered to your conversation webhook as a canvas.interaction event and is also available from GET https://tavusapi.com/v2/conversations/{conversation_id}/canvas/interactions.
submit
Exactly one selection field is present and non-null, and skipped is false. The field depends on the card’s mode:
| Mode | Selection field |
|---|
date_picker | selected_date, a YYYY-MM-DD string |
slot_picker / date_time | selected_slot, one slot object |
slot_picker / date_time with multi_select | selected_slots, an array of one or more slot objects |
range | selected_range, { "start", "end" } |
Slot objects echo back exactly what the PAL offered: id, start, and end always; label when the invocation included one. Selection fields for other modes are null.
skip
Sent when allow_skip is on and the user taps the skip button: skipped is true and no selection field carries a value. The PAL sees the skip too.
dismiss, clear, error, heartbeat
Lifecycle events about the card itself, not answers:
| Type | Meaning |
|---|
dismiss | The user closed the card without answering. |
clear | The card was removed (for example, the PAL called canvas_clear or replaced it). |
error | The renderer reported a problem with the card. |
heartbeat | A periodic liveness signal while the card is on screen. |
Their value has no fixed schema (a free-form object, often {}); don’t build logic on its contents.
Don’t count a conversation as scheduled until you see a submit; users also dismiss, skip, or let the conversation move on.
Webhook Handling
Branch on type first, then on whichever selection field is non-null:
Tavus records each interaction once: a retried POST with the same interaction_id and identical payload never fires your webhook twice. Key non-reversible handlers on properties.interaction_id anyway.
Reference
| |
|---|
| Component id | canvas.calendar |
| Action name | canvas_show_calendar |
| Version | v1 |
| Interaction class | submit_capable |
| Interaction types | submit, skip, dismiss, clear, error, heartbeat |
| Default placement | safe-area-right |
Supports update_component | Yes |
| PAL config | On by default with the magic_canvas skill attached; disable with config.components.calendar = { "enabled": false } |