Skip to main content
Subscribe with on(event, callback) and unsubscribe with off(event, callback). The SDK forwards events from two sources:
  • Avatar / engine events sent by the Avaturn backend over the underlying transport (avatar speech, transcripts, errors).
  • Client lifecycle events emitted locally by the SDK (init, idle, microphone state).

Avatar speech

avatar_started_speaking

Fired when the avatar starts speaking.
{ type: "avatar_started_speaking", phrase_id: string }

avatar_ended_speaking

Fired when the avatar finishes the current utterance.
{ type: "avatar_ended_speaking" }

avatar_started_speaking_phrase

Fired when the avatar starts a specific queued phrase. Use this when you need to track individual phrases (e.g. for captioning).
{ type: "avatar_started_speaking_phrase", phrase_id: string }

avatar_ended_speaking_phrase

Fired when the avatar finishes a phrase.
{ type: "avatar_ended_speaking_phrase", phrase_id: string }

Transcripts

Emitted by the OpenAI Realtime engine. Not emitted by the Cartesia engine.
User-input transcripts require configuration. Set audio.input.transcription.model in the OpenAI session config (e.g. "whisper-1"); without it the input transcript event never fires. Assistant transcripts flow by default.

ce_events.realtime.input_transcript

User speech transcription.
{
  type: "ce_events.realtime.input_transcript",
  transcript: string,
  timestamp: number // unix seconds
}

ce_events.realtime.response_transcript

Avatar (assistant) speech transcription.
{
  type: "ce_events.realtime.response_transcript",
  transcript: string,
  timestamp: number // unix seconds
}

Client lifecycle

Emitted by the SDK locally.

init

Fired when the avatar video track is ready. The promise returned by init() resolves on this event.

idle

Fired after 5 minutes without a backend message or task() call. If keepAlive is false, the SDK also tears down its local state right after the event fires.
The idle event bypasses the event catch-all. Subscribe to idle directly if you need to react to it.

Errors

error

Fired when the backend reports a lifecycle failure. Payload structure:
{
  type: "error",
  error: {
    type: "session_lifecycle_error",
    code: "internal_error" | "limits_exceeded" | "openai-realtime-version-mismatch",
    message: string,
    session_id: string
  }
}
  • openai-realtime-version-mismatch — your OpenAI session config mixes beta and GA-only features. Use GA-only (beta-to-GA migration).
  • limits_exceeded — workspace quota reached.
  • internal_error — generic server-side failure; check message for details.

Microphone

local_audio_change

Fired when the user’s microphone mute state changes. Callback receives a boolean directly (true = unmuted, false = muted).
avatar.on("local_audio_change", (active: boolean) => {
  // ...
});

local_mic_state_change

Fired when the microphone permission or device state changes. Callback receives a state string from Daily’s DailyTrackState['state'] union (e.g. "blocked", "off", "sendable", "loading", "interrupted", "playable", "receivable").
avatar.on("local_mic_state_change", (state) => {
  // ...
});

Catch-all

event

Fires alongside every other event except idle. Useful for logging or debugging.
avatar.on("event", ({ event, value }) => {
  console.log(event, value);
});

Example

function onPhraseEnd({ phrase_id }: { phrase_id: string }) {
  console.log("phrase done:", phrase_id);
}

avatar.on("avatar_ended_speaking_phrase", onPhraseEnd);
// later
avatar.off("avatar_ended_speaking_phrase", onPhraseEnd);