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
CLIENT_TOOL_EVENT_RECEIVEDFired when the AI persona invokes a client toolevent: 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;
}

ClientToolEvent

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);
    }
  }
);

Client Tool Events

Handle UI actions triggered by the AI persona:
import { ClientToolEvent } from "@anam-ai/js-sdk";

anamClient.addListener(
  AnamEvent.CLIENT_TOOL_EVENT_RECEIVED,
  (event: ClientToolEvent) => {
    const { eventName, eventData } = event;

    switch (eventName) {
      case "navigate_to_page":
        router.push(`/${eventData.page}`);
        break;
      case "open_modal":
        openModal(eventData.modalType, eventData.data);
        break;
      default:
        console.warn(`Unhandled tool: ${eventName}`);
    }
  }
);
For a guide on client tools, including how to create them and common use cases, see the Client Tools Guide.

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 handleToolCall = (event: ClientToolEvent) => {
    // Handle tool call
  };

  client.addListener(AnamEvent.CLIENT_TOOL_EVENT_RECEIVED, handleToolCall);

  return () => {
    client.removeListener(AnamEvent.CLIENT_TOOL_EVENT_RECEIVED, handleToolCall);
  };
}, [client]);

Learn More