Skip to main content
The live-listen SDK is a browser script that lets you subscribe to a live conversation’s audio and transcript from your own page. It connects as a read-only observer: you hear the call and receive transcript events, but you cannot speak and you are hidden from the other participants. Use it to build custom monitoring, supervisor, or QA tools outside the Anyreach dashboard. For the built-in monitoring experience, see Live monitoring.

How it works

The SDK is served as JavaScript at /public/listen-sdk. When you call AnyReach.listen(...), it injects a hidden iframe into your page and brokers all audio and transcript traffic through it over postMessage. You never handle the LiveKit connection directly.
your page  ──AnyReach.listen()──►  hidden iframe (/public/listen)  ──►  LiveKit room
   ▲                                       │
   └────── stateChange / transcript ───────┘

Load the script

Add the script tag to your page. Point it at your Anyreach host.
<script src="https://app.anyreach.ai/public/listen-sdk" async></script>
This registers AnyReach.listen on window.

Get a token

AnyReach.listen requires a url and token, which you mint from the API.
POST https://api.anyreach.ai/core/conversations/{conversation_id}/listener-token
This endpoint requires the conversations:read_sensitive or conversations:manage scope. Authenticate with Authorization: Bearer <token>; user PATs (pat_) also need X-Anyreach-Org: <organization_id>. The conversation must be in progress. If it is not active, the endpoint returns 409. If the conversation has no associated LiveKit room, it returns 404.
curl -X POST \
  https://api.anyreach.ai/core/conversations/{conversation_id}/listener-token \
  -H "Authorization: Bearer ak_..." \
  -H "Content-Type: application/json"
The response contains everything you pass to the SDK:
FieldTypeDescription
urlstringLiveKit server URL. Pass as url.
tokenstringSubscribe-only listener JWT. Pass as token.
room_namestringThe LiveKit room the conversation is in.
expires_atstring (ISO 8601, UTC)When the listener token expires.
Mint the token from your backend, not the browser. The listener-token scopes grant access to sensitive conversation audio, so the credential you authenticate with should never reach the client.

Start a session

Wire your event handlers first, then call connect().
1

Create the session

Pass the url and token from the listener-token response.
const session = AnyReach.listen({ url, token });
2

Attach handlers

Register handlers before connecting so you do not miss early events.
session.on("stateChange", (state) => console.log(state));
session.on("transcript", (t) => console.log(t.role, t.text));
session.on("error", (e) => console.error(e.message));
3

Connect

connect() returns a promise that resolves once the listener is subscribed. It rejects on error or after a 15-second timeout.
await session.connect();
4

Enable audio

Browser autoplay policy may suspend audio until a user gesture. Call startAudio() from a click handler.
playButton.addEventListener("click", () => session.startAudio());

Session API

AnyReach.listen({ url, token }) returns a session object with these methods.
MethodReturnsDescription
connect()Promise<void>Connects the listener. Resolves when subscribed, rejects on error or after a 15-second timeout. Calling it again while connecting returns the in-flight promise.
startAudio()Promise<void>Resumes audio playback. Call from a user-gesture handler to satisfy browser autoplay policy.
mute()voidMutes the listener’s audio output.
unmute()voidUnmutes the listener’s audio output.
isMuted()booleanReturns the current mute state.
getState()stringReturns the current connection state.
end()Promise<void>Ends the session and removes the hidden iframe. Resolves once teardown completes (forced after 3 seconds).
on(event, fn)sessionRegisters an event handler. Returns the session for chaining.
off(event, fn)sessionRemoves a previously registered handler. Returns the session for chaining.
mute() and unmute() control your local audio output only. They do not affect what the agent or caller hear.

Events

Subscribe with session.on(event, handler).
EventPayloadDescription
stateChangestringFires when the connection state changes.
transcript{ id, role, text, final }A transcript segment. final is false for in-progress (interim) text and true once the segment is settled.
agentDisconnected{ participantIdentity }The agent participant left the room.
error{ message }An error occurred. Also rejects an in-flight connect().
endedThe session ended and the iframe was torn down.
getState() and the stateChange event report idle, connecting, connected, or disconnected.

Full example

<script src="https://app.anyreach.ai/public/listen-sdk" async></script>
<button id="play">Listen</button>

<script>
  async function startListening(conversationId) {
    // Fetch the token from your backend, which holds the API credential.
    const { url, token } = await fetch(
      `/api/listener-token?conversation=${conversationId}`
    ).then((r) => r.json());

    const session = AnyReach.listen({ url, token });

    session.on("stateChange", (state) => {
      console.log("state:", state);
    });
    session.on("transcript", ({ role, text, final }) => {
      if (final) console.log(`${role}: ${text}`);
    });
    session.on("agentDisconnected", () => {
      console.log("agent left");
    });
    session.on("error", ({ message }) => {
      console.error("listen error:", message);
    });
    session.on("ended", () => {
      console.log("session ended");
    });

    await session.connect();

    // Autoplay policy: resume audio from a user gesture.
    document
      .getElementById("play")
      .addEventListener("click", () => session.startAudio());
  }
</script>

Next steps

Live monitoring

Listen to in-progress conversations from the Anyreach dashboard.

Agent Assist overview

See the full set of real-time assist capabilities.