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.
Attaching the Skill
Attach with a singlePUT:
skills value:
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_urlonscheduling_embed. - Disable a component: set
{ "enabled": false }.
Two components need config before they do anything.
scheduling_embed only
activates once scheduling_url is set, and image only activates once you
curate at least one Knowledge Base image or enable its web allowlist.
Attaching the skill without that config doesn’t error; the component stays
inactive.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.
When cards appear
There is no API to show a specific card. The PAL chooses when to call a Canvas action during the conversation. You influence that in three places:
Tavus appends
usage_guidance to the Canvas system prompt. It is ignored when blank or when no component is active.
Start by disabling components the PAL should not use (config.components.<name>.enabled: false). An enabled component can still appear even if your prompts never mention it.
Components
Each active component gives the PAL one action namedcanvas_show_<component> (for example, canvas_show_question). When at least one component is active, the PAL also gets canvas_clear. See the components overview for descriptions and per-component reference.
Every card renders inline in the safe-area-right side rail next to the PAL video. Placement is client-side; the PAL does not choose the side. Only one card is on screen at a time: showing a card replaces whatever is currently displayed.
Component Fields
images Rules
Each entry is { "document_id", "caption", "prompt" }:
document_id(required) is a Knowledge Base document id. (Once an image is uploaded to the Knowledge Base, it receives adocument_id.) It must be accessible to the account that owns the PAL and must have finished processing, both checked when you save the config.caption(optional, max 280 characters) is shown under the image. Blank falls back to the description Tavus generated for the image, then the document name.prompt(optional, max 500 characters) tells the PAL when this image applies. It is never shown to the user.
document_id; the URL it renders from is resolved by Tavus, never supplied by the model. See the image component for the full flow.
web Rules
image.web is { "enabled", "domains", "guidance" } and adds a second image source: URLs the PAL received during the conversation (typically from a tool result), restricted to websites you allowlist.
enabled(optional) defaults tofalseand requires at least one entry indomainsto turn on.falsekeeps a saved list without deleting it.domains(optional, max 20 entries) lists the allowed websites. Each entry is an exact host (photos.example.com) or a leading wildcard (*.cdn.example.com, matching any depth of subdomain, never the apex). Entries are hostname-only, max 253 characters, punycode for international names, and are normalized on save (lowercased, trailing dot stripped, deduplicated). IP addresses, local and internal names, and wildcards over shared suffixes (*.github.io,*.co.uk) are rejected.guidance(optional, max 500 characters) tells the PAL when to show a web image. Never shown to the user.
canvas_show_image action additionally accepts images[].url; every URL is checked at render time against a fixed safety floor (https, public hostname) and then your allowlist. See how a URL becomes an image.
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.
localhostand cloud metadata hostnames (such asmetadata.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
- Get PAL skill
- Attach skill to PAL
- Update PAL skill
- Detach skill from PAL
- Replace PAL skills
You can read skills on stock PALs, but you can only modify skills on PALs you own.
Reading Configuration
The single-skillGET returns the attachment as stored:
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:
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:
- The merge is shallow, at the top level of
config: a PATCH that includescomponentsreplaces 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
nullremoves it:{ "config": { "components": null } }clears every override and returns the skill to defaults.
Replacing All Skills
PUT /v2/pals/{pal_id}/skills takes { "skills": { ... } } and replaces the PAL’s entire skill set:
attached_at.
Detaching
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": "..." }:
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. imageresolves its curated Knowledge Base documents at this point. If none of them resolve to a displayable image andwebis not enabled, nocanvas_show_imageaction is compiled. With a liveweballowlist, the compiled action also accepts image URLs.canvas_clearis added once at least one component is active.- Canvas actions never overwrite a tool you defined with the same name; your tool wins.

