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

# Interaction Events

> Control CVI conversations by sending and listening to interaction events.

Interaction Events let you control and customize live conversations with a PAL in real time. You can send interaction events to the Conversational Video Interface (CVI) and listen to events the PAL sends back during the call.

### Interaction Types

* [Echo interactions](/sections/event-schemas/conversation-echo)
* [Response interactions](/sections/event-schemas/conversation-respond)
* [Interrupt interactions](/sections/event-schemas/conversation-interrupt)
* [Override conversation context interactions](/sections/event-schemas/conversation-overwrite-context)
* [Sensitivity interactions](/sections/event-schemas/conversation-sensitivity)
* [Tool Call Result](/sections/event-schemas/conversation-tool-result)

### Observable Events

* [Utterance Events](/sections/event-schemas/conversation-utterance)
* [Utterance Streaming Events](/sections/event-schemas/conversation-utterance-streaming)
* [Tool Call Events](/sections/event-schemas/conversation-toolcall)
* [Perception Tool Call Events](/sections/event-schemas/conversation-perception-tool-call)
* [Perception Analysis Events](/sections/event-schemas/conversation-perception-analysis)
* [Canvas Interaction Events](/sections/event-schemas/canvas-interaction) - Magic Canvas card taps (`submit`, `skip`, `dismiss`, etc.); see [Canvas interactions](/sections/conversational-video-interface/magic-canvas/api/interactions) for recording and history (separate from the Daily app-message events above)
* [Started/Stopped Speaking](/sections/event-schemas/conversation-started-stopped-speaking) - `conversation.started_speaking` / `conversation.stopped_speaking` with `properties.role` of `"pal"` or `"user"` (legacy duplicate PAL events use `"replica"`)

## Which Interaction Should I Send?

| Interaction                                                                | Use when                                                                                                                                       |
| -------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| [`conversation.respond`](/sections/event-schemas/conversation-respond)     | A user typed a message and the PAL should respond as if the user had spoken that text. This is the natural text-input event for chat-style UI. |
| [`conversation.echo`](/sections/event-schemas/conversation-echo)           | Your app supplies text or audio for the PAL to speak directly, such as echo-mode or custom ASR flows.                                          |
| [`conversation.interrupt`](/sections/event-schemas/conversation-interrupt) | Your app needs to stop the PAL while it is speaking.                                                                                           |

## Daily `sendAppMessage` Payloads

Use Daily's `sendAppMessage(interaction, '*')` to send interaction events over the call data channel.

### Text input: `conversation.respond`

```js theme={null}
call.sendAppMessage(
  {
    message_type: 'conversation',
    event_type: 'conversation.respond',
    conversation_id: 'YOUR_CONVERSATION_ID',
    properties: {
      text: 'User message as if they had just finished speaking.',
    },
  },
  '*'
);
```

### Face speaks supplied content: `conversation.echo`

```js theme={null}
call.sendAppMessage(
  {
    message_type: 'conversation',
    event_type: 'conversation.echo',
    conversation_id: 'YOUR_CONVERSATION_ID',
    properties: {
      modality: 'text',
      text: 'Text for the PAL to speak directly.',
      done: true,
    },
  },
  '*'
);
```

For audio echo, set `modality: 'audio'`, pass base64 `audio`, include `sample_rate`, and keep `done: false` until the final audio chunk.

### Stop the face: `conversation.interrupt`

```js theme={null}
call.sendAppMessage(
  {
    message_type: 'conversation',
    event_type: 'conversation.interrupt',
    conversation_id: 'YOUR_CONVERSATION_ID',
  },
  '*'
);
```

## Event Ordering and Turn Tracking

All events broadcasted by Tavus include the following fields for timing, ordering, and grouping:

* **`timestamp`** (number) - Unix timestamp (seconds since epoch) indicating when the event was created. Use this to build timestamped transcripts or reconstruct the full timeline of a conversation.

* **`seq`** (integer) - A globally monotonic sequence number. Every event gets the next value in the sequence, so a higher `seq` always means the event was sent later. Use this to reconcile events that may arrive out of order over the data channel.

* **`turn_idx`** (integer, optional) - The conversation turn index. This value increments each time a [`conversation.respond`](/sections/event-schemas/conversation-respond) interaction is received, and groups all events that belong to the same conversational turn. Use it to correlate related events - for example, an utterance, its tool calls, and the PAL speaking state changes that all stem from the same user input. This field is present on conversation-related events (utterances, tool calls, speaking state changes, perception events, etc.) and omitted on events that are not tied to a specific turn.

* **`inference_id`** (string, optional) - A stable identifier for a generated utterance or inference. Use it with [`conversation.utterance`](/sections/event-schemas/conversation-utterance), [`conversation.utterance.streaming`](/sections/event-schemas/conversation-utterance-streaming), and tool-call events to reconcile optimistic UI state with the final events Tavus emits.

## Call Client Example

Interaction events use a WebRTC data channel for communication. In Tavus's case, this is powered by <a href="https://www.daily.co/" target="_blank">Daily</a>, which makes setting up the call client quick and simple.

<Tabs>
  <Tab title="Daily JS">
    Here’s an example of using <a href="https://docs.daily.co/reference/daily-js/daily-call-client" target="_blank">DailyJS</a> to create a call client in JavaScript:

    <Note>
      The Daily `app-message` event is used to send and receive events and interactions between your server and CVI.
    </Note>

    ```js theme={null}
    <html>
      <script crossorigin src="https://unpkg.com/@daily-co/daily-js"></script>
      <body>
        <!-- Add input field and send button -->
        <input type="text" id="messageInput" placeholder="Enter your message">
        <button onclick="sendAppMessage()">Send Message</button>

        <script>
          call = window.Daily.createFrame();
          call.on('app-message', (event) => {
            console.log('app-message', event);
          });
          
          call.join({ url: 'YOUR_CONVERSATION_URL' });

          function sendAppMessage() {
            const messageInput = document.getElementById('messageInput');
            const message = messageInput.value;
            if (message) {
              const interaction = {
                "message_type": "conversation",
                "event_type": "conversation.respond",
                "conversation_id": "YOUR_CONVERSATION_ID",
                "properties": {
                  "text": `${message}`
                }
              }
              const hi = call.sendAppMessage(interaction, '*');
              console.log('Sending message: ', hi);
              console.log('Sent message: ', interaction);
              messageInput.value = '';
            }
          }
        </script>
      </body>
    </html>
    ```
  </Tab>

  <Tab title="Daily Python">
    Here’s an example of using <a href="https://docs.daily.co/reference/daily-python" target="_blank">Daily Python</a> to create a call client in Python:

    <Note>
      The Daily `app-message` event is used to send and receive events and interactions between your server and CVI.
    </Note>

    ```py theme={null}
    call_client = None

    class RoomHandler(EventHandler):
        def __init__(self):
            super().__init__()
        
        def on_app_message(self, message, sender: str) -> None:
            print(f"Incoming app message from {sender}: {message}")

    def join_room(url):
        global call_client
        try:
            Daily.init()
            output_handler = RoomHandler()
            call_client = CallClient(event_handler=output_handler)
            call_client.join(url)
        except Exception as e:
            print(f"Error joining room: {e}")
            raise

    def send_message(message):
        global call_client
        call_client.send_app_message(message)
    ```
  </Tab>

  <Tab title="Daily React">
    Here’s an example of using <a href="https://docs.daily.co/reference/daily-react" target="_blank">Daily React</a> to create a call client in React:

    <Note>
      The Daily `app-message` event is used to send and receive events and interactions between your server and CVI.
    </Note>

    ```tsx theme={null}
    "use client"

    import React, { useEffect, useRef, useState } from 'react';


    const TavusConversation = () => {
      const [message, setMessage] = useState('');
      const callRef = useRef(null);
      const containerRef = useRef(null);


      useEffect(() => {
        const loadDaily = async () => {
          const DailyIframe = (await import('@daily-co/daily-js')).default;


          callRef.current = DailyIframe.createFrame({
            iframeStyle: {
              width: '100%',
              height: '500px',
              border: '0',
            }
          });


          if (containerRef.current) {
            containerRef.current.appendChild(callRef.current.iframe());
          }


          callRef.current.on('app-message', (event) => {
            console.log('app-message received:', event);
          });


          callRef.current.join({
            url: 'YOUR_CONVERSATION_URL',
          });
        };


        loadDaily();


        return () => {
          if (callRef.current) {
            callRef.current.leave();
            callRef.current.destroy();
          }
        };
      }, []);


      const sendAppMessage = () => {
        if (!message || !callRef.current) return;


        const interaction = {
          message_type: 'conversation',
          event_type: 'conversation.respond',
          conversation_id: 'YOUR_CONVERSATION_ID',
          properties: { text: message }
        };


        callRef.current.sendAppMessage(interaction, '*');
        setMessage('');
      };


      return (
        <div className="w-full h-full flex flex-col items-center">
          <div ref={containerRef} className="w-full mb-4" />
          <div>
            <input
              type="text"
              className="border p-2 mr-2"
              value={message}
              onChange={(e) => setMessage(e.target.value)}
              placeholder="Type a message"
            />
            <button onClick={sendAppMessage} className="bg-blue-500 text-white px-4 py-2 rounded">
              Send Message
            </button>
          </div>
        </div>
      );
    };


    export default TavusConversation;
    ```
  </Tab>
</Tabs>
