> ## Documentation Index
> Fetch the complete documentation index at: https://docs.talking-avatar.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# LiveAvatar

> Runs a Realistic Avatars session on your server: starts the avatar, sends your agent's voice, handles interruptions.

```js theme={null}
import { LiveAvatar, LiveAvatarError } from "@interviewflowai/talking-avatar/server";
```

## LiveAvatar.start(options)

Opens a session and resolves with a `LiveAvatar` once the avatar is live. Billing starts here. Rejects with a [`LiveAvatarError`](#liveavatarerror) if the session can't start.

```js theme={null}
const avatar = await LiveAvatar.start({
  apiKey: process.env.TALKING_AVATAR_API_KEY,
  avatar: "ava4",
  sampleRate: 24000,
});
```

**apiKey** `string` *required*

Your `tav_…` key, from the dashboard. Keep it on your server.

**avatar** `string` *required*

An avatar ID, such as `ava4`. See [Avatars](https://docs.talking-avatar.dev/realistic-avatars/avatars.md).

**sampleRate** `8000 | 12000 | 16000 | 24000 | 48000` (default: 24000)

Your TTS audio's sample rate (PCM16 mono).

**livekit** `object`

Join your LiveKit room instead of streaming to `LiveAvatarView`. See [LiveKit](https://docs.talking-avatar.dev/realistic-avatars/livekit.md).

**properties**

**url** `string` *required*

Your LiveKit server URL.

**token** `string` *required*

A token you mint for the avatar's participant, with publish rights only. We never see your LiveKit secrets.

**publishAudio** `boolean` (default: true)

Also publish the voice, in sync with the lips (recommended). With `false`, you play the voice yourself and pass `playAtMs` to `pushAudio`.

**audioTrackName** `string` (default: avatar-audio)

The voice track's name. The video track is always named `avatar`.

**WebSocket** `class` (default: globalThis.WebSocket)

A WebSocket class, for Node.js 18 and 20: `import WebSocket from "ws"`.

## Methods

### pushAudio(pcm, options?)

Queues your agent's voice. It's played after what's already queued, in sync with the lips, so don't play it yourself. Audio can arrive faster than real time.

```js theme={null}
avatar.pushAudio(chunk);
avatar.pushAudio(base64Delta);
avatar.pushAudio(chunk, { sampleRate: 16000 });
```

**pcm** `Int16Array | Buffer | Uint8Array | ArrayBuffer | string` *required*

PCM16 mono audio. Bytes may be in chunks of any size, even splitting a sample. A string is read as base64 (for example, OpenAI Realtime audio deltas).

**options.sampleRate** `number`

This chunk's sample rate, if it differs from the session's.

**options.playAtMs** `number`

Only with `livekit.publishAudio: false`: the wall-clock time (ms since the epoch) this audio will be heard, sent about 400 ms early.

### clear()

Interrupts the avatar (barge-in): drops the audio that hasn't played yet.

**Returns** `Promise<number | null>`: how many milliseconds of the interrupted reply were heard, or `null` when you play the voice yourself.

```js theme={null}
const heardMs = await avatar.clear();
```

### handleView(body)

Direct mode only. Your page's route: pass the request body from `LiveAvatarView`, and return the result as JSON. It's called once for the WebRTC offer and once with the browser's answer. One route serves any number of viewers.

```js theme={null}
app.post("/avatar", async (req, res) => {
  try { res.json(await avatar.handleView(req.body)); }
  catch (error) { res.status(503).json({ error: error.message }); }
});
```

Throws if the session was started with the `livekit` option.

### on(event, listener)

Subscribes to an [event](#events). **Returns** a function that unsubscribes.

### close()

Ends the session. The avatar stops, and billing stops.

## Properties

| Property | Type | Description |
| - | - | - |
| `width` | `number` | The video width in pixels (832) |
| `height` | `number` | The video height in pixels (468) |

## Events

| Event | Listener | Fires when |
| - | - | - |
| `speechStarted` | `() => void` | The avatar started speaking (a reply's first audio is playing) |
| `speechEnded` | `(playedMs: number) => void` | Everything queued has played. `playedMs` of this reply were heard. |
| `republished` | `() => void` | The stream recovered from a dropped connection. `LiveAvatarView` reconnects by itself. |
| `close` | `(code: number, reason: string) => void` | The session ended. See [close codes](#close-codes). |

## LiveAvatarError

Why a session failed to start, or ended. Extends `Error`.

| Property | Type | Description |
| - | - | - |
| `code` | `number` | The close code |
| `message` | `string` | What went wrong |

### Close codes

| Code | Meaning |
| - | - |
| `1000` | You closed the session |
| `4001` | Invalid or missing API key |
| `4002` | Out of credits (below a minute's worth at the start, or used up during the session), or the monthly spending limit is reached |
| `4003` | The session reached its 3-hour limit |
| `1011` | The avatar's stream was lost and couldn't recover |

See [Sessions and errors](https://docs.talking-avatar.dev/realistic-avatars/sessions.md).


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