Skip to main content

Overview

Both <tavus-widget> and <tavus-embed> support two-way communication with the host page:
  • Element → page: the element dispatches CustomEvents (with bubbles: true and composed: true) for conversation lifecycle, tool calls, and protocol messages.
  • Page → element: the element exposes an imperative API at element.tavus for starting, ending, and messaging the conversation.
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):
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);
});
EventdetailFires when
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-messagevariesFirehose: fires for every observable protocol event (utterances, perception, speaking state, …). Switch on detail.event_type.
tavus:protocol-message carries the raw Interaction Events 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.
Magic Canvas card taps are not emitted as tavus:* host events - they are delivered to your conversation webhook as canvas.interaction events. See Canvas interactions.

Imperative API

The element exposes its conversation controls on element.tavus:
const el = document.querySelector("tavus-widget");

await el.tavus.start();                  // start the conversation
el.tavus.sendChat("Hello!");             // send a chat message as the visitor
el.tavus.sendMessage({                   // send a typed protocol interaction
  event_type: "conversation.echo",
  properties: { text: "Read this aloud" },
});
await el.tavus.end();                    // end the conversation
MethodDescription
start()Starts the conversation, as if the visitor pressed the start button. Returns a promise.
end()Ends the active conversation. Returns a promise.
sendChat(text)Sends a chat message into the conversation as the visitor.
sendMessage(interaction)Sends a typed protocol interaction. See Interactions.
element.tavus is attached once the element has mounted and its configuration has loaded. Wait for the element to render (or for a tavus:state-change event) before calling into it.

Interactions

sendMessage accepts the same interaction shapes as the Interactions Protocol:
event_typepropertiesEffect
conversation.echo{ text }The PAL speaks the text verbatim. See Echo.
conversation.respond{ text }The text is sent to the LLM, which responds. See Respond.
conversation.interrupt-Interrupts the PAL mid-speech. See Interrupt.
conversation.append_llm_context{ context }Appends to the conversation’s LLM context. See Append Context.
conversation.overwrite_llm_context{ context }Replaces the conversation’s LLM context. See Overwrite Context.

TavusIntegration helper (npm)

npm consumers get a typed wrapper around the element from the same package as the registration side effect:
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");
MethodDescription
on(event, handler)Subscribes to a typed deployment event.
off(event, handler)Removes a previously added handler.
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.
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.