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

# Component: calendar

> Let the user pick a date, a time slot, or a date range without leaving the conversation.

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.

<Note>
  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`.
</Note>

`calendar` is enabled whenever the Magic Canvas skill is attached and has no component-specific configuration; see [Enabling and configuring components](/sections/conversational-video-interface/magic-canvas/components#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](/sections/conversational-video-interface/pal/tools) 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.                                                                                        |

```json Example invocation [expandable] theme={null}
{
  "title": "Pick a demo time",
  "timezone": "America/Los_Angeles",
  "mode": "slot_picker",
  "slots": [
    {
      "id": "slot-1",
      "label": "Tuesday, 10:00 AM",
      "start": "2026-05-26T10:00:00-07:00",
      "end": "2026-05-26T10:30:00-07:00"
    },
    {
      "id": "slot-2",
      "label": "Tuesday, 2:00 PM",
      "start": "2026-05-26T14:00:00-07:00",
      "end": "2026-05-26T14:30:00-07:00"
    }
  ]
}
```

## 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`.

```json Webhook delivery [expandable] theme={null}
{
  "message_type": "canvas",
  "event_type": "canvas.interaction",
  "conversation_id": "c123abc456def",
  "timestamp": "2026-05-26T17:03:12Z",
  "properties": {
    "conversation_id": "c123abc456def",
    "interaction_id": "ci_tc-8842_submit_1c9f2e7a",
    "tool_call_id": "tc-8842",
    "component": "canvas.calendar",
    "component_version": "v1",
    "type": "submit",
    "value": {
      "selected_date": null,
      "selected_slot": {
        "id": "slot-1",
        "label": "Tuesday, 10:00 AM",
        "start": "2026-05-26T10:00:00-07:00",
        "end": "2026-05-26T10:30:00-07:00"
      },
      "skipped": false
    },
    "metadata": {},
    "created_at": "2026-05-26T17:03:12.482910"
  }
}
```

### `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.

```json theme={null}
{
  "selected_date": null,
  "selected_slot": null,
  "skipped": true
}
```

### `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.

<Warning>
  Don't count a conversation as scheduled until you see a `submit`; users also dismiss, skip, or let the conversation move on.
</Warning>

## Webhook Handling

Branch on `type` first, then on whichever selection field is non-null:

```js theme={null}
app.post("/tavus-webhook", (req, res) => {
  const { event_type, properties } = req.body;

  if (
    event_type !== "canvas.interaction" ||
    properties.component !== "canvas.calendar"
  ) {
    return res.sendStatus(200);
  }

  const { type, value } = properties;

  if (type === "submit") {
    if (value.selected_date) {
      bookDate(value.selected_date);
    } else if (value.selected_slot) {
      bookSlot(value.selected_slot.start, value.selected_slot.end);
    } else if (value.selected_slots) {
      value.selected_slots.forEach((s) => bookSlot(s.start, s.end));
    } else if (value.selected_range) {
      bookRange(value.selected_range.start, value.selected_range.end);
    }
  } else if (type === "skip" || type === "dismiss") {
    markDeclined(properties.conversation_id);
  }

  res.sendStatus(200);
});
```

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 }` |
