Skip to main content
The <anam-agent> element dispatches standard DOM Custom Events that cross the Shadow DOM boundary, so you can listen to them with addEventListener on the element itself or any ancestor. Event data is available on event.detail.

Listening to events

const widget = document.querySelector("anam-agent");

widget.addEventListener("anam-agent:session-started", (e) => {
  console.log("Session started:", e.detail.sessionId);
});

widget.addEventListener("anam-agent:message-received", (e) => {
  console.log(`${e.detail.role}: ${e.detail.content}`);
});

widget.addEventListener("anam-agent:error", (e) => {
  console.error(`Error [${e.detail.code}]: ${e.detail.message}`);
});

Events reference

Event NamePayloadDescription
anam-agent:session-started{ sessionId: string }A WebRTC session has been established.
anam-agent:session-ended{ sessionId: string, reason: string }The session has ended (user-initiated or server-side).
anam-agent:message-received{ role: "user" | "agent", content: string }A transcript message was received from either party.
anam-agent:message-sent{ content: string }The user sent a text message via the input field.
anam-agent:expanded{}The widget was expanded (floating layout only).
anam-agent:collapsed{}The widget was collapsed (floating layout only).
anam-agent:error{ code: string, message: string }An error occurred (auth failure, network issue, etc.).
anam-agent:mic-muted{}The user muted their microphone.
anam-agent:mic-unmuted{}The user unmuted their microphone.
anam-agent:tool-call-started{ toolName: string, toolCallId: string, toolType: string, toolSubtype?: string, arguments: object }The persona invoked a tool.
anam-agent:tool-call-completed{ toolName: string, toolCallId: string, toolType: string, toolSubtype?: string, result: unknown, executionTime: number, documentsAccessed?: string[] }A tool call finished successfully.
anam-agent:tool-call-failed{ toolName: string, toolCallId: string, toolType: string, toolSubtype?: string, errorMessage: string, executionTime: number }A tool call failed.
In the tool-call payloads, toolType is "client" for browser-side tools and "server" for tools that run on Anam’s side. The optional toolSubtype further identifies the tool and is currently set for server tools, such as "webhook" or "knowledge". executionTime is in milliseconds, and documentsAccessed is present only for knowledge (RAG) tools.

Reacting to tool calls

When your persona invokes a tool, the widget surfaces the tool-call lifecycle as DOM events, so your page can react to it without wiring up the JavaScript SDK directly. Use the toolType field to act only on client-side tools (those meant to run in the browser, such as navigation or cart updates) and ignore server-side tools (webhook and knowledge tools that run on Anam’s side):
const widget = document.querySelector("anam-agent");

widget.addEventListener("anam-agent:tool-call-started", (e) => {
  const { toolName, toolType, arguments: args } = e.detail;

  // Server-side tools (webhook, knowledge) run on Anam's side — ignore them here.
  if (toolType !== "client") return;

  if (toolName === "redirect") {
    window.location.href = args.url;
  }
});
The tool-call-completed and tool-call-failed events report the outcome of each call, which is useful for logging and analytics:
widget.addEventListener("anam-agent:tool-call-completed", (e) => {
  const { toolName, executionTime, documentsAccessed } = e.detail;
  console.log(`${toolName} finished in ${executionTime}ms`);
  if (documentsAccessed) {
    console.log("Documents used:", documentsAccessed);
  }
});

widget.addEventListener("anam-agent:tool-call-failed", (e) => {
  const { toolName, errorMessage } = e.detail;
  console.error(`${toolName} failed: ${errorMessage}`);
});
You still define and configure the tools on your persona in Anam Lab. See Client Tools and Events for how tools are created.
These are notification events — the widget reports tool calls but does not send a value back to the persona. Use client-side tools with the widget as fire-and-forget actions (awaitResult: false). If you need to return a result to the LLM, integrate with the JavaScript SDK’s registerToolCallHandler instead of the embed widget.

Common patterns

Track session starts, message counts, and engagement duration:
const widget = document.querySelector("anam-agent");
let sessionStart;

widget.addEventListener("anam-agent:session-started", (e) => {
  sessionStart = Date.now();
  analytics.track("avatar_session_started", {
    sessionId: e.detail.sessionId,
  });
});

widget.addEventListener("anam-agent:session-ended", (e) => {
  const duration = Date.now() - sessionStart;
  analytics.track("avatar_session_ended", {
    sessionId: e.detail.sessionId,
    reason: e.detail.reason,
    durationMs: duration,
  });
});

widget.addEventListener("anam-agent:message-received", (e) => {
  if (e.detail.role === "agent") {
    analytics.track("avatar_message", {
      contentLength: e.detail.content.length,
    });
  }
});
React to widget expand/collapse to adjust your page layout:
const widget = document.querySelector("anam-agent");
const sidebar = document.getElementById("sidebar");

widget.addEventListener("anam-agent:expanded", () => {
  sidebar.style.marginRight = "420px";
});

widget.addEventListener("anam-agent:collapsed", () => {
  sidebar.style.marginRight = "0";
});
Display user-facing messages or trigger fallback behavior:
const widget = document.querySelector("anam-agent");

widget.addEventListener("anam-agent:error", (e) => {
  const { code, message } = e.detail;

  if (message.includes("Origin not allowed")) {
    showBanner("Widget configuration required. Contact your admin.");
  } else if (message.includes("Too many requests")) {
    showBanner("Please wait a moment before trying again.");
  } else {
    showBanner("Something went wrong. Please try again.");
  }
});
Last modified on June 25, 2026