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

# Host Communication

> Listen to deployment lifecycle events and send interactions to the conversation from your page, with plain DOM APIs or the TavusIntegration helper.

## Overview

Both `<tavus-widget>` and `<tavus-embed>` support two-way communication with the host page:

* **Element → page**: the element dispatches `CustomEvent`s (with `bubbles: true` and `composed: true`) for conversation lifecycle, tool calls, and protocol messages.
* **Page → element**: the element exposes methods for starting, creating, ending, and messaging the conversation (`startConversation()`, `endConversation()`, …).

CDN consumers use plain DOM APIs - no extra bundle needed. npm consumers can use the typed `TavusIntegration` helper exported from `@tavus/widget` and `@tavus/embed`.

## Events

Listen on the element itself, or on `document` (events bubble out of the shadow DOM):

```js theme={null}
const el = document.querySelector("tavus-embed");

el.addEventListener("tavus:conversation-started", (e) => {
  console.log("started", e.detail.conversationId);
});

el.addEventListener("tavus:tool-call", (e) => {
  console.log("tool call", e.detail.name, e.detail.arguments);
});
```

| Event | `detail` | Fires when |
| - | - | - |
| `tavus:ready` | `{ deploymentId }` | The configuration has loaded and the element's methods are installed. Fires once. |
| `tavus:conversation-created` | `{ conversationId }` | `createConversation()` created a room nobody has joined yet. |
| `tavus:conversation-started` | `{ conversationId }` | The visitor joins and the conversation connects. |
| `tavus:conversation-ended` | `{ conversationId }` | The conversation ends. |
| `tavus:state-change` | `{ state }` | The conversation state changes (connecting, connected, ended, …). |
| `tavus:mode-change` | `{ mode }` | The experience switches mode (for example between screens or modalities). |
| `tavus:error` | `{ code, message }` | The conversation hits an error. |
| `tavus:tool-call` | `{ name, arguments, seq?, turn_idx? }` | The PAL's LLM invokes a tool. `arguments` is a JSON string. |
| `tavus:protocol-message` | varies | Firehose: fires for every observable protocol event (utterances, perception, speaking state, …). Switch on `detail.event_type`. |

<Note>
  `tavus:protocol-message` carries the raw [Interaction Events](/sections/conversational-video-interface/interactions-protocols/overview) protocol - `detail.event_type` values such as `conversation.utterance`, `conversation.tool_call`, `conversation.started_speaking`, and `conversation.stopped_speaking` match the schemas documented there. For speaking state, check `detail.properties.role` (`"user"`, `"pal"`, or legacy `"replica"`). See [Started/Stopped Speaking Event](/sections/event-schemas/conversation-started-stopped-speaking).
</Note>

<Note>
  Magic Canvas card taps are not emitted as `tavus:*` host events - they are delivered to your conversation webhook as [`canvas.interaction`](/sections/event-schemas/canvas-interaction) events. See [Canvas interactions](/sections/conversational-video-interface/magic-canvas/api/interactions).
</Note>

## Imperative API

The element installs its methods once its configuration has loaded, so wait for `tavus:ready` before calling them:

```js theme={null}
const el = document.querySelector("tavus-embed");

el.addEventListener("tavus:ready", () => {
  el.startConversation(); // as if the visitor pressed Start
});

// Later, once the conversation is live:
el.sendMessage("Hello!"); // sent as conversation.respond
el.endConversation();
```

| Method | Description |
| - | - |
| `startConversation(options?)` | Starts the conversation the same way the start button does. |
| `createConversation()` | Creates the conversation ahead of the join, so the PAL is ready when the visitor starts. The next `startConversation()` joins it. Resolves with `{ conversationId }`. |
| `endConversation()` | Ends the conversation, or cancels a pending start. |
| `sendMessage(interaction)` | Sends a typed protocol interaction. A plain string is sent as `conversation.respond`. See [Interactions](#interactions). |
| `updateConfig(patch)` | Deep-merges an `override-config` patch onto the configuration in effect. |
| `destroy()` | Ends any live conversation and removes the element. |
| `open()` / `close()` | `<tavus-widget>` only: expands or collapses the widget. |

### Host-provided devices

When your page renders its own device preview, skip the built-in haircheck and pass the selected devices into the call:

```js theme={null}
el.startConversation({
  haircheck: "provided",
  devices: {
    microphone: { deviceId: selectedMicrophoneId, enabled: true },
    camera: { track: previewVideoTrack, enabled: cameraEnabled },
  },
});
```

### element.tavus

`element.tavus.sendChat(text)` sends a chat message into the conversation as the visitor, and `element.tavus.sendMessage(interaction)` sends a typed protocol interaction.

## Interactions

`sendMessage` accepts the same interaction shapes as the [Interactions Protocol](/sections/conversational-video-interface/interactions-protocols/overview):

| `event_type` | `properties` | Effect |
| - | - | - |
| `conversation.echo` | `{ text }` | The PAL speaks the text verbatim. See [Echo](/sections/event-schemas/conversation-echo). |
| `conversation.respond` | `{ text }` | The text is sent to the LLM, which responds. See [Respond](/sections/event-schemas/conversation-respond). |
| `conversation.interrupt` | - | Interrupts the PAL mid-speech. See [Interrupt](/sections/event-schemas/conversation-interrupt). |
| `conversation.append_llm_context` | `{ context }` | Appends to the conversation's LLM context. See [Append Context](/sections/event-schemas/conversation-append-context). |
| `conversation.overwrite_llm_context` | `{ context }` | Replaces the conversation's LLM context. See [Overwrite Context](/sections/event-schemas/conversation-overwrite-context). |

## TavusIntegration helper (npm)

npm consumers get a typed wrapper around the element from the same package as the registration side effect:

```ts theme={null}
import "@tavus/embed";
import { TavusIntegration, type TavusInteraction } from "@tavus/embed";

// Pass the tag name to target: "tavus-embed" (default) or "tavus-widget".
const integration = new TavusIntegration("tavus-embed");

integration.on("tavus:conversation-started", (e) => {
  console.log("started", e.detail.conversationId);
});

integration.on("tavus:protocol-message", (e) => {
  console.log(e.detail);
});

const interaction: TavusInteraction = {
  event_type: "conversation.respond",
  properties: { text: "Tell me about pricing" },
};
integration.sendMessage(interaction);

// Plain strings are wrapped as conversation.respond for convenience.
integration.sendMessage("Tell me about pricing");
```

| Method | Description |
| - | - |
| `on(event, handler)` | Subscribes to a typed deployment event. |
| `off(event, handler)` | Removes a previously added handler. |
| `startConversation(options?)` | Same as the element's `startConversation()`. |
| `createConversation()` | Same as the element's `createConversation()`. |
| `endConversation()` | Same as the element's `endConversation()`. |
| `sendMessage(interaction \| string)` | Sends a typed interaction. A plain string is wrapped as `conversation.respond`. |

The helper looks up the element by tag name in the DOM, so construct it (or make the first call) after the element exists on the page.

<Info>
  `TavusIntegration` is bundled into the ESM builds of `@tavus/widget` and `@tavus/embed` - no extra install. The CDN (IIFE) build stays a pure side-effect drop-in; CDN pages use the DOM APIs above instead.
</Info>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.