Skip to main content

Adding Event Listeners

After initializing the Anam client, you can register event listeners using the addListener method:
import { AnamClient, AnamEvent } from "@anam-ai/js-sdk";

anamClient.addListener(AnamEvent.CONNECTION_ESTABLISHED, () => {
  console.log("Connection Established");
});

anamClient.addListener(AnamEvent.MESSAGE_HISTORY_UPDATED, (messages) => {
  console.log("Updated Messages:", messages);
});

Available Events

Event NameDescriptionCallback Parameters
CONNECTION_ESTABLISHEDFired when the WebRTC connection is establishedNone
CONNECTION_CLOSEDFired when the connection is terminatedreason: ConnectionClosedCode, details?: string
SESSION_READYFired after the session initializes and backend components are readysessionId: string
VIDEO_STREAM_STARTEDFired when the video stream becomes availablevideoStream: MediaStream
VIDEO_PLAY_STARTEDFired when the first video frames start playingNone
AUDIO_STREAM_STARTEDFired when the audio stream becomes availableaudioStream: MediaStream
INPUT_AUDIO_STREAM_STARTEDFired when microphone input is initializedaudioStream: MediaStream
MESSAGE_HISTORY_UPDATEDFired when a participant finishes speaking with full historymessages: Message[]
MESSAGE_STREAM_EVENT_RECEIVEDFired with real-time transcription updatesevent: MessageStreamEvent
TALK_STREAM_INTERRUPTEDFired when a user interrupts a TalkMessageStreamcorrelationId: string
TOOL_CALL_STARTEDFired when any tool call beginsevent: ToolCallStartedPayload
TOOL_CALL_COMPLETEDFired when a tool call completes successfullyevent: ToolCallCompletedPayload
TOOL_CALL_FAILEDFired when a tool call failsevent: ToolCallFailedPayload
CLIENT_TOOL_EVENT_RECEIVEDDeprecated - Use TOOL_CALL_STARTED insteadevent: ClientToolEvent
SERVER_WARNINGFired when the server sends a warning messagemessage: string
MIC_PERMISSION_PENDINGFired when microphone permission is being requestedNone
MIC_PERMISSION_GRANTEDFired when microphone permission is grantedNone
MIC_PERMISSION_DENIEDFired when microphone permission is deniederror: string
INPUT_AUDIO_DEVICE_CHANGEDFired when the input audio device changesdeviceId: string
REASONING_HISTORY_UPDATEDFired when reasoning/thought history is updatedmessages: ReasoningMessage[]
REASONING_STREAM_EVENT_RECEIVEDFired with real-time reasoning updatesevent: ReasoningStreamEvent

Type Definitions

Message

interface Message {
  id: string;
  content: string;
  role: MessageRole; // 'user' | 'persona'
  interrupted?: boolean;
}

MessageStreamEvent

interface MessageStreamEvent {
  id: string;
  content: string;
  role: MessageRole; // 'user' | 'persona'
  endOfSpeech: boolean;
  interrupted: boolean;
}

ToolCallStartedPayload

interface ToolCallStartedPayload {
  eventUid: string;                 // Unique ID for this event
  toolCallId: string;               // ID of the tool call
  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
}

ToolCallCompletedPayload

interface ToolCallCompletedPayload {
  eventUid: string;                 // Unique ID for this event
  toolCallId: string;               // ID of the tool call
  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
}

ToolCallFailedPayload

interface ToolCallFailedPayload {
  eventUid: string;                 // Unique ID for this event
  toolCallId: string;               // ID of the tool call
  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
}

ToolCallHandler

interface ToolCallHandler {
  onStart?: (payload: ToolCallStartedPayload) => Promise<string | void>;
  onFail?: (payload: ToolCallFailedPayload) => Promise<void>;
  onComplete?: (payload: ToolCallCompletedPayload) => Promise<void>;
}

ClientToolEvent (Deprecated)

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
}

ConnectionClosedCode

enum ConnectionClosedCode {
  NORMAL = 'CONNECTION_CLOSED_CODE_NORMAL',
  MICROPHONE_PERMISSION_DENIED = 'CONNECTION_CLOSED_CODE_MICROPHONE_PERMISSION_DENIED',
  SIGNALLING_CLIENT_CONNECTION_FAILURE = 'CONNECTION_CLOSED_CODE_SIGNALLING_CLIENT_CONNECTION_FAILURE',
  WEBRTC_FAILURE = 'CONNECTION_CLOSED_CODE_WEBRTC_FAILURE',
  SERVER_CLOSED_CONNECTION = 'CONNECTION_CLOSED_CODE_SERVER_CLOSED_CONNECTION',
}

ReasoningMessage

interface ReasoningMessage {
  id: string;
  content: string;
  role: string;
}

ReasoningStreamEvent

interface ReasoningStreamEvent {
  id: string;
  content: string;
  endOfThought: boolean;
  role: string;
}

Example Usage

Loading States

Use connection events to manage loading states:
anamClient.addListener(AnamEvent.CONNECTION_ESTABLISHED, () => {
  setIsConnecting(false);
});

anamClient.addListener(AnamEvent.SESSION_READY, (sessionId: string) => {
  console.log("Session ready:", sessionId);
  setIsLoading(false);
});

Connection Closed Handling

Handle connection closures with reason codes:
import { AnamEvent, ConnectionClosedCode } from "@anam-ai/js-sdk";

anamClient.addListener(
  AnamEvent.CONNECTION_CLOSED,
  (reason: ConnectionClosedCode, details?: string) => {
    switch (reason) {
      case ConnectionClosedCode.NORMAL:
        console.log("Connection closed normally");
        break;
      case ConnectionClosedCode.MICROPHONE_PERMISSION_DENIED:
        showError("Microphone access is required");
        break;
      case ConnectionClosedCode.WEBRTC_FAILURE:
        showError("Connection failed. Please check your network.");
        break;
      default:
        console.log("Connection closed:", reason, details);
    }
  }
);

Microphone Permission Flow

Track microphone permission state:
anamClient.addListener(AnamEvent.MIC_PERMISSION_PENDING, () => {
  showPermissionPrompt("Please allow microphone access");
});

anamClient.addListener(AnamEvent.MIC_PERMISSION_GRANTED, () => {
  hidePermissionPrompt();
  showMicrophoneIndicator();
});

anamClient.addListener(AnamEvent.MIC_PERMISSION_DENIED, (error: string) => {
  showError(`Microphone access denied: ${error}`);
});

Message History

Track conversation history:
import { Message } from "@anam-ai/js-sdk";

anamClient.addListener(
  AnamEvent.MESSAGE_HISTORY_UPDATED,
  (messages: Message[]) => {
    setConversationHistory(messages);
  }
);

Real-time Transcription

Monitor speech in real-time:
import { MessageStreamEvent, MessageRole } from "@anam-ai/js-sdk";

anamClient.addListener(
  AnamEvent.MESSAGE_STREAM_EVENT_RECEIVED,
  (event: MessageStreamEvent) => {
    if (event.role === MessageRole.PERSONA) {
      updatePersonaSpeech(event.content);
    } else {
      updateUserSpeech(event.content);
    }

    if (event.endOfSpeech) {
      finalizeSpeech(event.id);
    }
  }
);
Use registerToolCallHandler to register handlers for specific tools. This automatically emits completed or failed events when the handler completes.
import { AnamClient } from "@anam-ai/js-sdk";

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 handlers
const cancelModalHandler = anamClient.registerToolCallHandler("open_modal", {
  onStart: async (payload) => {
    openModal(payload.arguments.modalType, payload.arguments.data);
    return `Opened ${payload.arguments.modalType} modal`;
  },
});

// unsubscribe when no longer needed
cancelNavHandler();
cancelModalHandler();
registerToolCallHandler is the recommended approach for client tools. This will automatically emit completed or failed events when the handler completes.

Tool Call Events

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:
import { AnamEvent, ToolCallStartedPayload, ToolCallCompletedPayload, ToolCallFailedPayload } from "@anam-ai/js-sdk";

anamClient.addListener(
  AnamEvent.TOOL_CALL_STARTED,
  (event: ToolCallStartedPayload) => {
    console.log(`Tool started: ${event.toolName} (${event.toolType})`, event.arguments);
  }
);

anamClient.addListener(
  AnamEvent.TOOL_CALL_COMPLETED,
  (event: ToolCallCompletedPayload) => {
    console.log(`Tool completed: ${event.toolName} in ${event.executionTime}ms`);
  }
);

anamClient.addListener(
  AnamEvent.TOOL_CALL_FAILED,
  (event: ToolCallFailedPayload) => {
    console.error(`Tool failed: ${event.toolName} - ${event.errorMessage}`);
  }
);
Use toolType and toolSubtype to distinguish between tool types in your listeners:
anamClient.addListener(AnamEvent.TOOL_CALL_COMPLETED, (event) => {
  switch (event.toolType) {
    case "client":
      console.log(`Client action completed: ${event.toolName}`);
      break;
    case "server":
      if (event.toolSubtype === "webhook") {
        console.log(`Webhook responded: ${event.toolName} in ${event.executionTime}ms`);
      } else if (event.toolSubtype === "knowledge") {
        console.log(`Knowledge search completed: ${event.toolName}`);
      }
      break;
  }
});
These events fire for all tool types. For guides on configuring each type, see Client Tools, Webhook Tools, and Knowledge Tools.

Server Warnings

Handle server-side warnings:
anamClient.addListener(AnamEvent.SERVER_WARNING, (message: string) => {
  console.warn("Server warning:", message);
  showWarningToast(message);
});

Reasoning Events (Extended Thinking)

Track AI reasoning when using models with extended thinking:
import { ReasoningMessage, ReasoningStreamEvent } from "@anam-ai/js-sdk";

// Get complete reasoning history
anamClient.addListener(
  AnamEvent.REASONING_HISTORY_UPDATED,
  (messages: ReasoningMessage[]) => {
    setReasoningHistory(messages);
  }
);

// Stream reasoning in real-time
anamClient.addListener(
  AnamEvent.REASONING_STREAM_EVENT_RECEIVED,
  (event: ReasoningStreamEvent) => {
    updateReasoningDisplay(event.content);
    if (event.endOfThought) {
      finalizeThought(event.id);
    }
  }
);

Removing Event Listeners

Remove listeners to prevent memory leaks, especially in single-page applications:
const handleMessages = (messages: Message[]) => {
  setConversationHistory(messages);
};

// Add listener
anamClient.addListener(AnamEvent.MESSAGE_HISTORY_UPDATED, handleMessages);

// Remove listener when done
anamClient.removeListener(AnamEvent.MESSAGE_HISTORY_UPDATED, handleMessages);

React Example

useEffect(() => {
  const cancelNav = client.registerToolCallHandler("navigate_to_page", {
    onStart: async (payload) => {
      router.push(`/${payload.arguments.page}`);
    },
  });

  const cancelModal = client.registerToolCallHandler("open_modal", {
    onStart: async (payload) => {
      openModal(payload.arguments.modalType);
      return "Modal opened";
    },
  });

  return () => {
    cancelNav();
    cancelModal();
  };
}, [client]);

Learn More

Client Tools Guide

Guide with examples for navigation, modals, UI updates, and more

Tools Overview

Learn about all tool types: client, webhook, and knowledge tools