Events
Every session event, the wildcard handler with a switch-case example, and when to use specific vs wildcard subscriptions.
All events are kebab-case, matching Daily/Pipecat/Web-API convention. Subscribe via session.on(event, handler) or via the on: option on startSession / joinRoom.
Event categories
Connection lifecycle
| Event | Handler signature | Fires when |
|---|---|---|
'connected' | () => void | WebRTC fully established |
'disconnected' | () => void | Session ended |
'error' | (message: string) => void | Session-level error |
'state-change' | (status: ConnectionStatus) => void | Any status transition |
'assistant-ready' | () => void | Bot pipeline reports ready |
Conversation lifecycle (server-emitted)
| Event | Handler signature | Fires when |
|---|---|---|
'conversation-start' | () => void | User has joined and conversation is active |
'conversation-end' | (reason: ConversationEndReason) => void | Idle timeout or client disconnected |
'pipeline-error' | (details: PipelineErrorDetails) => void | Fatal error from a pipeline processor |
Media
| Event | Handler signature |
|---|---|
'track-started' | (track: MediaStreamTrack, local: boolean) => void |
'track-stopped' | (track: MediaStreamTrack, local: boolean) => void |
'mic-change' | (enabled: boolean) => void |
Speech activity (VAD — no text)
| Event | Handler signature |
|---|---|
'user-speech-start' | () => void |
'user-speech-end' | () => void |
'assistant-speech-start' | () => void |
'assistant-speech-end' | () => void |
TTS lifecycle (fires in all modes, including stream)
| Event | Handler signature |
|---|---|
'tts-start' | () => void |
'tts-chunk' | (text: string) => void |
'tts-end' | () => void |
Transcripts & telemetry
| Event | Handler signature |
|---|---|
'transcript' | (entry: TranscriptEntry) => void |
'metrics' | (data: unknown) => void |
See Transcripts for the transcript entry shape and streaming semantics.
Driving your UI from state-change
idle → connecting → connected → disconnecting → disconnected
↘ error session.on('state-change', (status) => {
if (status === 'connected') showCallUI();
if (status === 'disconnected') showEndedScreen();
if (status === 'error') showErrorScreen();
}); Wildcard — every event in one handler
Subscribe to '*' to receive every event. Useful for logging, analytics, or a single dispatch table.
session.on('*', (event, ...args) => console.log(`[buddy] ${event}`, args)); Full wildcard switch-case reference
import {
type ConnectionStatus,
type ConversationEndReason,
type PipelineErrorDetails,
type TranscriptEntry
} from '@juspay/breeze-buddy-client-sdk';
session.on('*', (event, ...args) => {
switch (event) {
// ---------- Connection lifecycle ----------
case 'connected':
showCallUI();
break;
case 'disconnected':
showEndedScreen();
break;
case 'error':
showErrorBanner(args[0] as string);
break;
case 'state-change':
updateStatusDot(args[0] as ConnectionStatus);
break;
case 'assistant-ready':
console.log('assistant pipeline ready');
break;
// ---------- Conversation lifecycle (server-emitted) ----------
case 'conversation-start':
startCallTimer();
break;
case 'conversation-end': {
const reason = args[0] as ConversationEndReason;
if (reason === 'idle_timeout') showIdleTimeoutScreen();
else showDisconnectedScreen();
break;
}
case 'pipeline-error': {
const details = args[0] as PipelineErrorDetails;
reportToSentry(`pipeline ${details.processor}: ${details.error}`);
break;
}
// ---------- Media / mic ----------
case 'track-started': {
const [track, local] = args as [MediaStreamTrack, boolean];
if (!local) attachAudioVisualizer(track);
break;
}
case 'track-stopped':
// args: [track, local]
break;
case 'mic-change':
setMicButtonState(args[0] as boolean);
break;
// ---------- Speech activity (VAD, no text) ----------
case 'user-speech-start':
showListeningIndicator();
break;
case 'user-speech-end':
hideListeningIndicator();
break;
case 'assistant-speech-start':
showSpeakingIndicator();
break;
case 'assistant-speech-end':
hideSpeakingIndicator();
break;
// ---------- TTS lifecycle ----------
case 'tts-start':
// bot's TTS pipeline began emitting audio
break;
case 'tts-chunk':
appendWordToBubble(args[0] as string);
break;
case 'tts-end':
finalizeBubble();
break;
// ---------- Transcripts (discriminated union on role) ----------
case 'transcript': {
const entry = args[0] as TranscriptEntry;
switch (entry.role) {
case 'user':
renderUserBubble(entry.id, entry.text, entry.isComplete);
break;
case 'assistant':
renderAssistantBubble(entry.id, entry.text, entry.isComplete);
break;
case 'tool_call':
renderToolBadge(entry.id, entry.functionName, entry.isComplete);
break;
}
break;
}
// ---------- Telemetry ----------
case 'metrics':
console.debug('metrics', args[0]);
break;
}
}); Type casts — why?
Wildcard handlers are typed
(event, ...args: unknown[]) => void — you lose per-event arg typing because one handler receives every event. Casting inside each case is the honest way to do it. If you want full type safety, subscribe to specific events (below) — each is fully typed with no casts needed.Specific subscriptions — fully typed
For business logic, prefer specific subscriptions. TypeScript narrows the arg types for you:
session.on('transcript', (entry) => {
// entry is TranscriptEntry — role-discriminated
if (entry.role === 'user') renderUserBubble(entry.id, entry.text);
});
session.on('state-change', (status) => {
// status is ConnectionStatus
});
session.on('conversation-end', (reason) => {
// reason is 'idle_timeout' | 'client_disconnected'
});
session.on('pipeline-error', ({ processor, error }) => {
// details destructured
}); When to use wildcard vs specific
| Use case | Recommended |
|---|---|
| Debug logging of every event | Wildcard — one line |
| Analytics (track every transition) | Wildcard or 'state-change' specific |
| Feature logic (call UI, mic, etc.) | Specific subscriptions — typed, no casts |
| Transcripts (any role) | session.on('transcript', (entry) => ...) — branch on entry.role |
Handler safety
Handler exceptions are caught and logged — one broken listener won’t stop others from firing or bring down the session. Errors go to the console with the event name.
Was this helpful?