Skip to main content
This guide covers both the Python SDK (tuner-livekit-sdk) and the Node.js SDK (@usetuner.ai/livekit-sdk). Use the language switcher below to view the guide for your stack.

Video Tutorial

Prerequisites

  • Tuner Active Account
  • Configured Agent with provider “Custom API” in Tuner
  • A LiveKit Agents project, either:

Overview

The Tuner LiveKit SDK automatically captures session data from your LiveKit agent and sends it to Tuner when the call ends — no manual API calls required. It ships in two flavours that share the same wire format and feature set: a Python package and a Node.js package.
1

Install the SDK

Add the package to your project.
2

Set Your Credentials

Configure your Tuner API key, workspace ID, and agent remote ID.
3

Add the Plugin

Drop two lines into your entrypoint and you’re done.
Estimated time: 2 minutes from start to finish

Step 1: Install the SDK

Requirements: Python ≥ 3.10, livekit-agents >= 1.4, aiohttp >= 3.9

Step 2: Set Your Credentials

You can configure credentials via environment variables or pass them directly in code.
Use the Agent Remote ID from Agent Settings → Agent Connection — not your agent’s display name. It is a UUID (for example fa4da74c-...). Copy your value from the Tuner dashboard.

Step 3: Add the Plugin

Import TunerPlugin and add it right after creating your AgentSession — before calling session.start():
That’s it. The plugin listens to session events and submits call data to Tuner when the session ends.

SIP correlation for simulation

To run Call Simulation against this agent, pass the inbound call’s SIP Call-ID as sip_correlation_id. For why Tuner needs it and how the value can change name in transit, see Simulation SIP setup.
This applies to inbound simulation, where Tuner dials your agent. For outbound simulation, pass the dialed SIP URI as recipient instead — see Outbound Simulation.
LiveKit exposes the value on the SIP participant as the sip.callIDFull attribute.
Requires tuner-livekit-sdk >= 0.1.5.

Helper: extract the SIP correlation ID

Add this helper to your entrypoint module. It scans the room’s remote participants for the SIP participant and returns its sip.callIDFull attribute.
For web calls (no SIP participant), this returns None and TunerPlugin falls back to its normal behaviour.

Wire it into your entrypoint

The SIP participant only becomes visible after the agent joins the room. Call ctx.connect() first, then read the correlation ID, then pass it to TunerPlugin:
If ctx.connect() runs after you try to read participants, ctx.room.remote_participants will be empty and sip_correlation_id will be None. Simulated calls will still complete, but they won’t be linked to their Tuner simulation rows.

Disconnection Reasons

Tuner captures why each call ended. User disconnects and errors are tracked automatically. To record agent-initiated hangups, pass reason="agent_hangup" when ending the call programmatically:
Without this, agent hangups are indistinguishable from dropped calls in your analytics.
drain=True lets any in-flight audio finish before the session closes. Omit it for an immediate cutoff.

LangChain and LangGraph observability

Using LangGraph or LangChain for your logic layer? The plugin can capture node transitions, tool calls, and timing alongside session data. See LangChain / LangGraph.

Configuration Options

The plugin auto-detects the call type (phone_call for SIP participants, web_call otherwise). Override it explicitly when needed:
The phone number or SIP URI of the callee. Optional — pass it when you want to record who the call was directed to:
Tuner requires a recording_url for every call. Provide a resolver function that returns the URL. If you don’t provide one, the plugin submits "pending" as a placeholder.
Provide a callable that receives a UsageSummary and returns the call cost in USD cents. Implement the method that matches your pricing plan; the snippet below is illustrative.
Attach arbitrary key-value data to every call record:
Track which version of your agent handled each call — useful when you update a prompt, swap a model, or change your pipeline:
Tuner reads it automatically. Bump the number on every deployment.Override in code (takes priority over the env var):
Useful for local development or test environments:

Full Example

Troubleshooting

  • Verify that TUNER_AGENT_ID is the Agent Remote ID from Agent Settings → Agent Connection (not your agent’s display name).
  • Confirm TUNER_WORKSPACE_ID is correct.
  • Check your application logs for any error messages from the plugin.
  • Ensure TUNER_API_KEY starts with tr_api_ and is valid.
  • Confirm the API key belongs to the correct workspace.
  • You haven’t provided a recording_url_resolver. Add one that returns the actual recording URL for each call.
  • If using LiveKit Egress, ensure the recording has finished processing before the resolver is called.

OpenTelemetry traces

LiveKit Agents already emits OpenTelemetry spans for every session. Point its exporter at Tuner and those spans appear as a span tree on the matching call, under a Traces tab. See OpenTelemetry Traces for how to read one.
This is optional and separate from the SDK integration above. Trace export is configured through LiveKit’s own telemetry module, in Python.
1

Point your exporter at Tuner

Use the same TUNER_API_KEY you set in Step 2. It identifies your workspace, so nothing else needs configuring.
Use the signal-specific variable, OTEL_EXPORTER_OTLP_TRACES_ENDPOINT, and give the full URL. The generic OTEL_EXPORTER_OTLP_ENDPOINT breaks this setup in two ways: the exporter appends /v1/traces to it, which does not resolve to Tuner’s path, and it is shared with metrics and logs, so repointing it would send those to Tuner as well.
If you already send traces to your own observability backend, leave that variable alone and fan out with an OpenTelemetry Collector instead of switching endpoints.
2

Use the HTTP exporter

Tuner accepts OTLP over HTTP with protobuf encoding, which is the default for the HTTP exporter. gRPC is not supported.
3

Tag the trace with your call id

Tuner matches a trace to a call on a tuner.call_id span attribute. Its value has to be the same call id the plugin submits for this session, character for character.metadata is applied to every span in the session, so setting it once on the tracer provider is enough.
You only need the attribute on one span in the trace. Tuner applies it to the whole trace, including spans that arrived before the tagged one.
4

Check that it worked

Open the call in Tuner. A Traces tab appears on the call details page once spans have been received and matched. Correlation completes when the call itself is ingested, so the tab can take a moment to appear after a session ends.

Span reference

The span names come from LiveKit Agents, so they are the same ones you would see in any other OpenTelemetry backend. See LiveKit’s telemetry documentation for the full set, and OpenTelemetry Traces for how to read the tree in Tuner.

Traces troubleshooting

In order of likelihood:
  1. tuner.call_id does not match the call id submitted for the session. They have to be identical strings. This is the most common cause by a wide margin.
  2. The attribute is missing entirely. Nothing in the trace carries it, so there is nothing for Tuner to match on.
  3. The gRPC exporter is still in use. Requests are rejected with 415. Check your agent logs for exporter errors.
  4. The API key is wrong or revoked. Requests are rejected with 401 or 403.
You probably repointed OTEL_EXPORTER_OTLP_ENDPOINT rather than the traces-specific variable. Restore it and use an OpenTelemetry Collector to send spans to both destinations.
Your tracer provider was created without a service name resource. Traces still arrive and still correlate, but they are harder to tell apart when several agents report into the same workspace.

Webhooks

Tuner can notify your systems when it has something to report on a call — see Webhooks.

What’s Next?

Configuring Your Agent

Set up call outcomes, user intents, and evals.

Custom Integration

Learn about the underlying API if you need more control.

Classifying Calls

Define how Tuner categorizes your calls.

Real-Time Alerts

Get notified when issues are detected.