> ## Documentation Index
> Fetch the complete documentation index at: https://docs.upliftai.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Live listen

Live listen is a **listen-only token** into a call that is happening right now. You join the call's [room](/voice-agents/concepts/how-it-works#a-call-is-a-room) and hear both sides. Nobody on the call knows. The use we built it for is a supervisor dropping in on a live campaign call.

## Get a token

[POST to the session](/api-reference/sessions-%26-call-records/listen-in-on-a-live-call). There is no body. The `sessionId` is the `callId` the dispatch gave you, or the id on any session read.

```bash theme={null}
curl -X POST https://api.upliftai.org/v1/realtime-assistants/sessions/$SESSION_ID/listen \
  -H "Authorization: Bearer $UPLIFT_API_KEY"
```

```json theme={null}
{
  "token": "eyJhbGciOiJIUzI1NiJ9…",
  "wsUrl": "wss://upliftai-prod-yd34to8b.livekit.cloud",
  "roomName": "call-25661352-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}
```

Same shape as a [web session token](/api-reference/starting-a-conversation/create-a-web-session-token). The grant is different. It can **subscribe** and nothing else: no publishing audio, no data channel. You join **hidden**, so the agent never sees you and keeps talking to the caller. On a web session the client tool calls still go to the browser, not to you.

## Play it

Any LiveKit client joins the room with that token. Each audio track is one side of the call, the agent and the caller. Attach both:

```js theme={null}
import { Room, RoomEvent } from 'livekit-client'

const room = new Room()
room.on(RoomEvent.TrackSubscribed, (track) => {
  if (track.kind === 'audio') document.body.appendChild(track.attach())
})
room.on(RoomEvent.Disconnected, () => console.log('call over'))
await room.connect(wsUrl, token)
```

Browsers block autoplay, so run `connect` from a click. In React, `RoomAudioRenderer` from `@livekit/components-react` inside a `LiveKitRoom` does the attaching for you. The [Client tools](/voice-agents/tools/client-tools) page shows the same room from the caller's side.

## The limits

* **Ten minutes to connect.** The token expires after that, but once you are in you stay until the call ends. Mint a fresh one if you waited too long.
* **No backfill.** You hear from the moment you join. Nothing before that is replayed. For the whole call, take the recording off the [session detail](/api-reference/sessions-%26-call-records/get-a-calls-transcript-and-outcomes) once it ends.
* **Room ends with the call.** When the call ends we delete the room, and your client gets `Disconnected`.
* **One listener per API key.** The listener identity comes from your key. A second connect with the same key evicts the first, so give each supervisor their own key.
* **On web, wait for `state: active`.** Any session with a room works, phone or web. If you join before the browser does, you create the room, and the browser's join then dispatches no agent.

## When it fails

* **400, not live.** The session is `completed` or `failed`. Every other state gets a token, including a call that is still dialing.
* **404, not found.** No such session in your project.

Check `state` on the [session status](/api-reference/sessions-%26-call-records/get-a-sessions-status) read before you show a listen button.
