Register event listeners after creating the Anam client and before starting the session with stream() or streamToVideoElement().
Event handlers must be registered before the session starts. Startup events such as microphone permission, connection, stream, and SESSION_READY can fire while stream() or streamToVideoElement() is running, and late listeners will not receive events that already fired. This also applies to registerToolCallHandler.
interface MessageStreamEvent { id: string; content: string; role: MessageRole; // 'user' | 'persona' endOfSpeech: boolean; interrupted: boolean; cueTag?: string; // Director Notes cue that takes effect from the start of this text chunk}
interface ToolCallStartedPayload { eventUid: string; // Unique ID for this event toolCallId: string; // ID of the tool call sessionId: string; // ID of the active session toolName: string; // The tool name (e.g., "navigate_to_page") toolType: string; // Tool type (e.g., "client", "server") toolSubtype?: string; // Tool subtype for server events (e.g., "webhook", "knowledge") arguments: Record<string, any>; // LLM-generated parameters timestamp: string; // ISO timestamp timestampUserAction: string; // Timestamp of the user action that triggered the tool call userActionCorrelationId: string; // Id of the user action that triggered the tool call}
interface ToolCallCompletedPayload { eventUid: string; // Unique ID for this event toolCallId: string; // ID of the tool call sessionId: string; // ID of the active session toolName: string; // The tool name toolType: string; // Tool type toolSubtype?: string; // Tool subtype result: any; // Result returned by the handler executionTime: number; // Execution time in milliseconds timestamp: string; // ISO timestamp documentsAccessed?: string[]; // List of file names accessed during the tool call, if applicable (Knowledge tools only) timestampUserAction: string; // Timestamp of the user action that triggered the tool call userActionCorrelationId: string; // Id of the user action that triggered the tool call}
interface ToolCallFailedPayload { eventUid: string; // Unique ID for this event toolCallId: string; // ID of the tool call sessionId: string; // ID of the active session toolName: string; // The tool name toolType: string; // Tool type toolSubtype?: string; // Tool subtype errorMessage: string; // Error description executionTime: number; // Execution time in milliseconds timestamp: string; // ISO timestamp timestampUserAction: string; // Timestamp of the user action that triggered the tool call userActionCorrelationId: string; // Id of the user action that triggered the tool call}
ClientToolEvent is deprecated as of SDK v4.9.0. Use ToolCallStartedPayload with registerToolCallHandler or TOOL_CALL_STARTED events instead.
interface ClientToolEvent { eventUid: string; // Unique ID for this event sessionId: string; // Session ID eventName: string; // The tool name (e.g., "navigate_to_page") eventData: Record<string, any>; // LLM-generated parameters timestamp: string; // ISO timestamp when event was created timestampUserAction: string; // ISO timestamp of user action that triggered this userActionCorrelationId: string; // Correlation ID for tracking}
These fire as soon as the server-side VAD picks up voice activity, before the full transcript arrives via MESSAGE_STREAM_EVENT_RECEIVED. The correlationId ties the speech start/end pair to the eventual transcript.If USER_SPEECH_ENDED doesn’t arrive within ~10 seconds of a USER_SPEECH_STARTED, treat it as a dropped detection and reset your UI. This can happen if the connection drops mid-speech.
Use correlationId to associate the cue with other events from the same user action.Each MESSAGE_STREAM_EVENT_RECEIVED payload also includes an optional cueTag when a Director Notes cue takes effect from the start of that text chunk. If the field is absent, no new cue takes effect at that chunk boundary. Cue boundaries can split persona speech into multiple streamed text chunks, so append event.content in arrival order instead of assuming one event per response.MESSAGE_HISTORY_UPDATED is unchanged. Its Message items do not include cue metadata.
Use registerToolCallHandler to register handlers for specific tools before starting the session. This automatically emits completed or failed events when the handler completes.
import { createClient } from "@anam-ai/js-sdk";const anamClient = createClient(sessionToken);const cancelNavHandler = anamClient.registerToolCallHandler("navigate_to_page", { onStart: async (payload) => { const { page, section } = payload.arguments; router.push(`/${page}${section ? `#${section}` : ""}`); }, onComplete: async (payload) => { console.log(`tool call completed ${payload.toolName}`); }, onFail: async (payload) => { console.log(`tool call failed ${payload.toolName}`); },});// you can also register partial handlersconst cancelModalHandler = anamClient.registerToolCallHandler("open_modal", { onStart: async (payload) => { openModal(payload.arguments.modalType, payload.arguments.data); return `Opened ${payload.arguments.modalType} modal`; },});// start the session after handlers are attachedawait anamClient.streamToVideoElement("video-element-id");// unsubscribe when no longer neededcancelNavHandler();cancelModalHandler();
registerToolCallHandler is the recommended approach for client tools. Register tool call handlers before stream() or streamToVideoElement() so the persona cannot invoke a tool before your handler is attached.
Listen for tool call lifecycle events across all tool types — client, webhook, and knowledge tools all emit these events, so you can use them for logging, analytics, or monitoring: