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

# PAL Canvas configuration

> Full reference for the magic_canvas skill on PALs: attaching it, the component overlay, the skills API, and every validation error.

To configure **Magic Canvas**, attach the `magic_canvas` skill to the PAL. The skill controls which Canvas components the PAL may use; the PAL decides at runtime when to show one.

Attaching the skill enables every component with defaults; there is no per-component opt-in. Audio-only, chat, and external-meeting conversations never get Canvas actions; see [how the configuration reaches a conversation](#how-the-configuration-reaches-a-conversation).

## Attaching the Skill

Attach with a single `PUT`:

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

```json theme={null}
{
  "skill_id": "magic_canvas",
  "config": {},
  "attached_at": "2026-06-10T17:04:52.183947+00:00",
  "updated_at": "2026-06-10T17:04:52.183947+00:00"
}
```

Common configurations, shown as the PAL's `skills` value:

```json theme={null}
// Everything on, defaults (scheduling_embed inactive: needs config)
"skills": { "magic_canvas": { "config": {} } }
```

```json theme={null}
// Everything on + the scheduling embed configured
"skills": { "magic_canvas": { "config": {
  "components": { "scheduling_embed": { "provider": "calendly", "scheduling_url": "https://calendly.com/x/30min" } }
} } }
```

```json theme={null}
// Everything except chart
"skills": { "magic_canvas": { "config": {
  "components": { "chart": { "enabled": false } }
} } }
```

## Component Overlay

`config.components` is a sparse overlay, not an allowlist. Components you don't mention stay enabled with defaults. Add an entry to:

* **Configure a component**: for example, set `scheduling_url` on `scheduling_embed`.
* **Disable a component**: set `{ "enabled": false }`.

New components Tavus ships later are enabled automatically on PALs with the skill attached. Disable them per component to opt out.

<Note>
  `scheduling_embed` only activates once `scheduling_url` is set. Attaching the
  skill without that config doesn't error; the component stays inactive.
</Note>

`config` is strictly validated: an unknown component name or stray field returns `400`. A misspelled skill id in the URL returns `404`: `Unknown skill '...'` on `PUT` and `PATCH`, the not-attached `404` on `GET` and `DELETE`.

## Components

Each active component gives the PAL one action named `canvas_show_<component>` (for example, `canvas_show_question`). When at least one component is active, the PAL also gets `canvas_clear` and `update_component` to dismiss or revise cards it already showed.

| Component          | What the PAL shows                                                   | Default slot      |
| ------------------ | -------------------------------------------------------------------- | ----------------- |
| `question`         | A multiple-choice question, optionally with a free-text "Other"      | `safe-area-right` |
| `input`            | A prompt for a single typed value                                    | `safe-area-right` |
| `calendar`         | A date, time-slot, or date-range picker                              | `safe-area-right` |
| `text`             | A card of formatted text                                             | `safe-area-right` |
| `chart`            | A bar, line, or pie chart                                            | `safe-area-right` |
| `alert`            | A dismissible notice                                                 | `safe-area-right` |
| `scheduling_embed` | Your real scheduling page (e.g. Calendly), embedded for live booking | `safe-area-right` |

Every card renders inline in a side rail next to the PAL video. The only placements offered are `safe-area-right` (the default) and `safe-area-left`; the PAL can pick the other side at render time. Components whose catalog default is a different slot are clamped to an inline side rail when the card renders, so the effective placement is always one of the two side rails. Only one card is on screen at a time: showing or updating a card replaces whatever is currently displayed.

### Component Fields

| Field            | Type    | Required | Description                                                                                                                      |
| ---------------- | ------- | -------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `enabled`        | boolean | ❌        | Accepted by every component. Defaults to `true`. Set `false` to disable the component.                                           |
| `provider`       | string  | ❌        | `scheduling_embed` only. Defaults to `"calendly"`, the only supported provider.                                                  |
| `scheduling_url` | string  | ❌        | `scheduling_embed` only. Your booking link, delivered to the embed exactly as configured. Defaults to `""` (component inactive). |

### `scheduling_url` Rules

A non-empty `scheduling_url` must be a public HTTPS URL:

* Must start with `https://` and include a hostname.
* At most 2048 characters.
* `localhost` and cloud metadata hostnames (such as `metadata.google.internal`) are rejected.
* IP-based hosts (including hex and other numeric encodings) in private, loopback, link-local, or otherwise internal address space are rejected.
* An empty string is allowed and means "not configured yet."

## Skills API

Skill attachments live under `/v2/pals/{pal_id}/skills`. Full HTTP reference:

* [List PAL skills](/api-reference/pal-skills/list-pal-skills)
* [Get PAL skill](/api-reference/pal-skills/get-pal-skill)
* [Attach skill to PAL](/api-reference/pal-skills/attach-skill-to-pal)
* [Update PAL skill](/api-reference/pal-skills/update-pal-skill)
* [Detach skill from PAL](/api-reference/pal-skills/detach-skill-from-pal)
* [Replace PAL skills](/api-reference/pal-skills/replace-pal-skills)

Endpoints:

| Method & path                                | What it does                                                 |
| -------------------------------------------- | ------------------------------------------------------------ |
| `GET /v2/pals/{pal_id}/skills`               | List every skill attached to the PAL.                        |
| `PUT /v2/pals/{pal_id}/skills`               | Replace the PAL's **entire** skill set in one request.       |
| `GET /v2/pals/{pal_id}/skills/{skill_id}`    | Read one attachment.                                         |
| `PUT /v2/pals/{pal_id}/skills/{skill_id}`    | Attach a skill, or overwrite its config if already attached. |
| `PATCH /v2/pals/{pal_id}/skills/{skill_id}`  | Merge changes into an existing attachment's config.          |
| `DELETE /v2/pals/{pal_id}/skills/{skill_id}` | Detach a skill.                                              |

You can read skills on stock PALs, but you can only modify skills on PALs you own.

### Reading Configuration

The single-skill `GET` returns the attachment as stored:

```json theme={null}
{
  "skill_id": "magic_canvas",
  "config": {
    "components": { "chart": { "enabled": false } }
  },
  "attached_at": "2026-06-10T17:04:52.183947+00:00",
  "updated_at": "2026-06-11T09:31:08.412605+00:00"
}
```

The per-skill `PUT`, `PATCH`, and `GET` return the attachment object directly. The list `GET` and the bulk `PUT` wrap attachments in a `data` map keyed by skill id:

```json theme={null}
{
  "data": {
    "magic_canvas": {
      "skill_id": "magic_canvas",
      "config": {},
      "attached_at": "2026-06-10T17:04:52.183947+00:00",
      "updated_at": "2026-06-10T17:04:52.183947+00:00"
    }
  }
}
```

### Updating with PATCH

`PATCH` requires a `{ "config": ... }` body (unlike `PUT`, where it defaults to `{}`), merges it into the existing config, and returns `404` if the skill isn't attached:

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

Merge rules:

* The merge is **shallow, at the top level of `config`**: a PATCH that includes `components` replaces the whole overlay map rather than deep-merging per component. Send the complete set of overrides you want to end up with.
* Setting a top-level config key to `null` removes it: `{ "config": { "components": null } }` clears every override and returns the skill to defaults.

The merged result is re-validated in full.

### Replacing All Skills

`PUT /v2/pals/{pal_id}/skills` takes `{ "skills": { ... } }` and replaces the PAL's **entire** skill set:

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

<Warning>
  The bulk PUT is a full replace: any skill missing from the payload is detached.
  To change only Canvas, use the per-skill `PUT` or `PATCH` instead.
</Warning>

Skills that were already attached keep their original `attached_at`.

### Detaching

```bash theme={null}
curl -X DELETE https://tavusapi.com/v2/pals/{pal_id}/skills/magic_canvas \
  -H "x-api-key: <your-api-key>"
```

Returns `204` with no body. Detaching removes Canvas actions from all future conversations and deletes the attachment's config; re-attaching starts from the config you send next.

## Validation Errors

Errors return `{ "error": "..." }`:

| Status | When                                                                               | Error message contains                                               |
| ------ | ---------------------------------------------------------------------------------- | -------------------------------------------------------------------- |
| `404`  | `PUT` or `PATCH` with a skill id that isn't registered (misspelled `magic_canvas`) | `Unknown skill '<skill_id>'`                                         |
| `404`  | `GET`, `PATCH`, or `DELETE` on a skill that isn't attached to the PAL              | `Skill '<skill_id>' is not attached to this PAL`                     |
| `400`  | Unknown key anywhere inside `config` (misspelled component, stray field)           | the offending key and `Unknown field.`                               |
| `400`  | `scheduling_url` doesn't start with `https://`                                     | `scheduling_url must use HTTPS`                                      |
| `400`  | `scheduling_url` is longer than 2048 characters                                    | `scheduling_url must be at most 2048 characters`                     |
| `400`  | `scheduling_url` has no hostname                                                   | `scheduling_url must include a hostname`                             |
| `400`  | `scheduling_url` host is `localhost` or a cloud metadata hostname                  | `scheduling_url host '<host>' is not allowed`                        |
| `400`  | `scheduling_url` host is an internal IP address                                    | `scheduling_url IP '<host>' is not allowed (internal address space)` |
| `400`  | The `pal_id` in the URL doesn't exist                                              | `Bad Request. PAL not found`                                         |

## How the configuration reaches a conversation

At conversation create, Tavus resolves the PAL's Canvas action list once:

* Audio-only (`audio_only: true`), chat, and external-meeting (`meeting_url`: Zoom, Teams, Meet) conversations never get Canvas actions.
* Every other conversation with the skill attached gets one `canvas_show_<component>` action per active component.
* `canvas_clear` and `update_component` are added once at least one component is active.
* Canvas actions never overwrite a [tool](/sections/conversational-video-interface/pal/tools) you defined with the same name; your tool wins.
