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

# Magic Canvas quickstart

> Attach the Magic Canvas skill to a PAL, render cards in the call, and receive every interaction on your webhook.

**Magic Canvas** lets a PAL show interactive cards, such as questions, calendars, and charts, during a conversation. The PAL's LLM decides when to show one; user input flows back to the conversation and your webhook.

<Steps>
  <Step title="Attach the Magic Canvas skill">
    Attaching the skill enables the available components with default settings:

    ```bash theme={null}
    curl -X PUT https://tavusapi.com/v2/pals/{pal_id}/skills/magic_canvas \
      -H "Content-Type: application/json" \
      -H "x-api-key: <your-api-key>" \
      -d '{ "config": {} }'
    ```

    `config.components` is a sparse overlay, not an allowlist; add an entry only to configure or disable a component:

    ```bash Disable charts theme={null}
    curl -X PUT https://tavusapi.com/v2/pals/{pal_id}/skills/magic_canvas \
      -H "Content-Type: application/json" \
      -H "x-api-key: <your-api-key>" \
      -d '{ "config": { "components": { "chart": { "enabled": false } } } }'
    ```

    Later components are enabled automatically on PALs with the skill attached; disable them the same way. See [Configuring your PAL](/sections/conversational-video-interface/magic-canvas/api/configuration) for all component settings.

    <Note>
      The skill has no effect on `echo` and speech-to-speech PALs.
    </Note>
  </Step>

  <Step title="Create the conversation">
    Every video conversation with this PAL gets Canvas. Set `callback_url`; interactions arrive there in step 4:

    ```bash theme={null}
    curl -X POST https://tavusapi.com/v2/conversations \
      -H "Content-Type: application/json" \
      -H "x-api-key: <your-api-key>" \
      -d '{
        "face_id": "r79e1c033f",
        "pal_id": "p5317866",
        "callback_url": "https://yourapp.example.com/webhooks/tavus"
      }'
    ```

    Audio-only, text-chat, and Zoom/Teams/Meet (`meeting_url`) conversations never receive Canvas actions.

    <Warning>
      Conversation creation uses your API key; call it from your backend, never the browser.
    </Warning>
  </Step>

  <Step title="Render the canvas">
    With the Tavus-hosted embed or widget, Canvas renders automatically:

    ```html theme={null}
    <script src="https://unpkg.com/@tavus/embed"></script>
    <tavus-embed deployment-id="YOUR_DEPLOYMENT_ID"></tavus-embed>
    ```

    For React, add the `@tavus/cvi-ui` component:

    ```bash theme={null}
    npx @tavus/cvi-ui@latest add magic-canvas
    ```

    Mount it inside the same `CVIProvider` as your conversation UI:

    ```tsx theme={null}
    import { CVIProvider } from "./components/cvi/components/cvi-provider";
    import { Conversation } from "./components/cvi/components/conversation";
    import { MagicCanvas } from "./components/cvi/components/magic-canvas";

    <CVIProvider>
      <Conversation conversationUrl={conversationUrl} />
      <MagicCanvas
        onInteraction={(event) => console.log("user interacted:", event)}
      />
    </CVIProvider>
    ```

    <Note>
      The default class sets `position: fixed` (full-viewport overlay). To keep cards inside your player, wrap both in a `position: relative` container and pass a `className` that sets `position: absolute !important`.
    </Note>
  </Step>

  <Step title="Receive interactions">
    Each interaction arrives at your `callback_url` as a `canvas.interaction` event, fired once when first recorded; duplicate submissions and client retries never re-fire it:

    ```json canvas.interaction [expandable] theme={null}
    {
      "properties": {
        "conversation_id": "c123456",
        "interaction_id": "ci_call_abc123_submit_8f3d2a",
        "tool_call_id": "call_abc123",
        "component": "canvas.question",
        "component_version": "v1",
        "type": "submit",
        "value": { "selected_option_ids": ["opt_2"], "skipped": false },
        "metadata": {},
        "created_at": "2026-06-09T21:14:03.412210"
      },
      "conversation_id": "c123456",
      "message_type": "canvas",
      "event_type": "canvas.interaction",
      "timestamp": "2026-06-09T21:14:03.498Z"
    }
    ```

    Fetch the full history any time with your API key:

    ```bash theme={null}
    curl https://tavusapi.com/v2/conversations/{conversation_id}/canvas/interactions \
      -H "x-api-key: <your-api-key>"
    ```

    The response is `{ "data": [ ... ] }`, oldest first, with the same fields as the webhook's `properties`.
  </Step>
</Steps>

## Components

Attaching the skill enables the components below with default settings. `scheduling_embed` needs a booking link (`provider` plus `scheduling_url`) configured before it activates.

| Component          | What the PAL can do with it                                         |
| ------------------ | ------------------------------------------------------------------- |
| `question`         | Ask a multiple-choice question, optionally with a free-text "Other" |
| `input`            | Ask for a single typed value (text, email, number, or phone)        |
| `calendar`         | Let the user pick a date, a time slot, or a date range              |
| `scheduling_embed` | Embed your real scheduling page (e.g. Calendly) for live booking    |
| `text`             | Show a card of formatted text                                       |
| `chart`            | Show a bar, line, or pie chart                                      |
| `alert`            | Show a dismissible notice                                           |

<Note>
  `scheduling_embed` activates only once you configure a booking link (a public HTTPS URL); unconfigured, it stays inactive and produces no error:

  ```json theme={null}
  "config": {
    "components": {
      "scheduling_embed": {
        "provider": "calendly",
        "scheduling_url": "https://calendly.com/your-team/30min"
      }
    }
  }
  ```
</Note>

<Note>
  Cards render in a sandboxed iframe on Tavus infrastructure that isolates styles and scripts in both directions. Your webhook receives `skip` and `dismiss` interactions in addition to `submit`.
</Note>
