Placeholders
The snippets below use these placeholders. Replace them with your own values before running the code.Prerequisites
- A configured Voice Agent with its agent ID
- An SLNG key (get one at app.slng.ai)
- A backend you can deploy server-side code to (Node.js, Deno, Python, etc.)
- A frontend project with React (the examples below use React, but the LiveKit client SDK works with any framework)
How it works
- The browser asks your backend to start a session.
- Your backend calls the Voice Agents web-sessions endpoint and forwards the LiveKit credentials back.
- The browser connects to the LiveKit room, publishes the mic, and plays the agent’s audio.
Step 1: Create a backend endpoint
Your backend proxies the Voice Agents API so the SLNG key never reaches the browser. The only call you need is:Step 2: Install the LiveKit client SDK
Step 3: Connect to the LiveKit room
Call your backend to get a session, then connect to the room:Noise cancellation is applied automatically on the server: the agent runs background voice cancellation on web participants’ audio before transcription. You don’t need any client-side audio processing or filter libraries.
Step 4: Play the agent’s audio
Attach the agent’s remote audio track to the DOM so the browser plays it:Step 5: Handle real-time messages
The agent publishes real-time events over a LiveKit data channel on theslng.transcript.v1 topic. Every message is a UTF-8-encoded JSON object with a common envelope: v (payload version, currently 1), type, and created_at (Unix timestamp in seconds):
There is one more message type,
call_event (level, stage, and optional data) — customer-safe call lifecycle events. It shares the same envelope but is recorded with the call on the SLNG backend, not published over the room data channel, so your page never receives it.conversation_item_added
Sent when a completed turn — from the user or the agent — is added to the conversation history:user_input_transcribed
Streams the speech-to-text output while the user is talking. You receive a series of interim messages (is_final: false), each carrying the transcript of the utterance so far, followed by one final message (is_final: true):
This is how you render live captions: show
transcript while is_final is false, then clear the caption on the final message (the completed turn also arrives as conversation_item_added):
agent_state_changed and user_state_changed
Sent whenever the agent or the user transitions between states:
Agent states are
initializing, idle, listening, thinking, and speaking. User states are speaking, listening, and away. The agent’s state is also mirrored to the lk.agent.state attribute on the agent participant (see Step 7).
close
Sent when the agent ends the session — because it called its hang-up tool, the user was idle too long, the maximum session duration was reached, or something failed. This is how your page knows the call is over:
The room disconnects shortly afterwards, so treat
close as your “call ended” signal: stop the mic, update the UI, and don’t offer reconnect-style retries for reason: "normal".
debug
Verbose diagnostics (pipeline stages, errors with stack traces). These are only published to the room when room-data debugging is enabled for your deployment — off by default, so don’t build UI on them:Step 6: Add mute and disconnect controls
Step 7: Track what the agent is doing
Use theagent_state_changed data message from Step 5 to know whether the agent is listening, thinking, or speaking, and drive a visual indicator or avatar animation from it. Don’t infer this from audio levels (RoomEvent.ActiveSpeakersChanged) — they can tell you the agent is talking, but they can’t distinguish thinking from idle:
lk.agent.state attribute on the agent participant, so as an alternative you can read it without parsing data messages:
Optional: Add a visual persona
A voice-only interface gives users no visual cue about what the agent is doing. Adding an animated persona (an orb, waveform, or avatar) makes the experience feel more responsive. Two ready-made libraries work well here:Vercel AI SDK Persona
A React component with built-in states:
idle, listening, speaking, thinking. Drop it in and map LiveKit events to states.ElevenLabs Conversational UI
Orb and avatar components designed for voice interfaces, with audio-reactive animations.
Putting it all together
A minimal React component with all the steps above wired together:Next steps
- Set up your agent with custom prompts and tools in the Dashboard
- See the Voice Agents API for agent configuration options
- Add phone call support with Telephony
- Add a visual persona with Vercel AI SDK or ElevenLabs UI