Overview
The self-hosted LiveKit integration monitors voice agents built on the open-source LiveKit Agents framework that run against your own LiveKit server. Drop theroark-analytics[livekit] helper into your agent entrypoint and call lifecycle, transcripts, tool calls, and a recording are forwarded to Roark automatically. No other code changes required.
The SDK talks only to Roark’s own API; it never connects to your LiveKit server.
Using LiveKit Cloud? You can use this SDK there too, but the dashboard-managed webhook integration is usually simpler. See LiveKit Cloud.
Prerequisites
- A LiveKit Agents worker (Python 3.10+,
livekit-agents1.x) - Your own LiveKit server
- A Roark API key with WRITE scope (generate one)
Setup Instructions
Step 1: Install the SDK
Step 2: Configure your API key
The only setting you need to provide is your Roark API key..env
Step 3: Wire observe_session into your entrypoint
Call observe_session(...) before session.start() so the audio taps are installed before the session begins streaming frames. That single call wires up everything:
Step 4: Verify the connection
Run your worker against your LiveKit server and place a test call:- The agent appear under the LiveKit source filter
- The call appear in the calls table with status
In Progress - After the call ends, the transcript, tool invocations, and recording attached to the call
How It Works
observe_session subscribes to the standard AgentSession event surface and ships a compact event timeline to Roark:
Failures are logged and swallowed: the helper never raises into your session. Your agent keeps running even if Roark is unreachable.
What Gets Synced
- Calls: Lifecycle with timing and end reason
- Agents: Lazy-registered on first sight using the
agent_id/agent_nameyou pass - Prompts: System prompt captured at call start as a prompt revision
- Transcripts: Per-turn messages with role, content, and timestamp
- Tool Invocations: Tool call IDs, names, JSON arguments, and results
- Recordings: Audio recording of the call
Agent Management
LiveKit agents are lazy-registered the first time the helper reports them:- The first
call-startedevent with a newagent_idcreates the agent in Roark - Subsequent events update the agent’s name and prompt if they change
- These agents appear on the agents page under the LiveKit source filter
Kill Switch
Disable instrumentation at runtime without touching code. SetROARK_OBSERVABILITY_ENABLED=false to make observe_session a no-op (it returns None). Values treated as off: false, 0, no, off (case-insensitive); anything else (or the variable being absent) keeps it enabled.
Troubleshooting
Calls aren't finalizing on Roark
Calls aren't finalizing on Roark
The helper registers a shutdown callback on the
JobContext, which fires when LiveKit ends the job. If your transport tears down without firing the hook, call await state.aflush(reason="...") explicitly from your own disconnect handler: aflush() is idempotent, so the normal shutdown path will no-op if both fire.Transcripts arrive empty
Transcripts arrive empty
Transcripts come from the
conversation_item_added event on AgentSession. If you use a custom pipeline that bypasses AgentSession.start(room=…, agent=…), that event may never fire. Verify by adding your own session.on("conversation_item_added", print) listener.Recording is missing one side
Recording is missing one side
The user side is tapped from
session.input.audio; the agent side from session.output.audio. Because the taps are installed by observe_session, it must be called before session.start(). Check worker logs for the tapping user audio input / tapping agent audio output lines. If one is missing, that side will be silent on the merged recording.No calls or recordings reaching Roark
No calls or recordings reaching Roark
Confirm
api_key is a WRITE-scope key and that ROARK_OBSERVABILITY_ENABLED is not set to a falsy value.Next Steps
Enable Tracing
Send OpenTelemetry traces: they auto-link to the call via the room sid
Run Simulations
Test your LiveKit agents
Configure Evaluators
Set up evaluation criteria
LiveKit Cloud
Use the dashboard-managed webhook integration instead