View as markdown

Events

The widget posts CustomEvents to the window so you can wire analytics, side effects, or your own UI changes when the agent state shifts.

Quick example

ts
window.addEventListener("livelayer:opened", () => {
  analytics.track("agent_opened");
});

window.addEventListener("livelayer:conversation:started", (e) => {
  analytics.track("conversation_started", {
    conversationId: e.detail.id,
  });
});

Every event is a CustomEvent; payload (if any) is on event.detail.

Event catalog

EventDescriptionPayload
livelayer:loadedWidget script booted and the <livelayer-widget> element is mounted. Fires once per page load.
livelayer:openedVisitor clicked the bubble to expand the widget panel. Does not imply a conversation has started.
livelayer:closedVisitor closed the panel back to a bubble.
livelayer:conversation:startedSession connected; agent and visitor can speak. Fires once per session.{ id: string; agentId: string }
livelayer:conversation:endedSession ended (visitor left, timeout, or programmatic close).{ id: string; durationSeconds: number }
livelayer:agent:stateAgent state changed: idle | listening | thinking | speaking. Useful for syncing your own UI cues.{ state: "idle" | "listening" | "thinking" | "speaking" }
livelayer:transcriptNew transcript line(s) available.{ entries: Array<{ id, role, text, final }> }
livelayer:errorRecoverable error. Codes include MIC_PERMISSION_DENIED, AGENT_TIMEOUT, key_wrong_org.{ code: string; message: string }

Patterns

Programmatic control

Beyond listening, you can interact with the widget element directly:

ts
const widget = document.querySelector("livelayer-widget");

// Open the bubble panel
widget?.dispatchEvent(new CustomEvent("livelayer:open"));

// Close it
widget?.dispatchEvent(new CustomEvent("livelayer:close"));

// Swap the agent
widget?.setAttribute("agent-id", "agt_xyz789");

For richer programmatic control (publishing data over the agent's data channel, controlled session, custom auth), use the NPM package.