/webhooks/calls endpoint as every
other source, tagged with X-Zelto-Provider: livekit so the calls show
up as LiveKit.
Mint a key
- Open Settings → Integrations → LiveKit and click Create API key.
- Give it a descriptive name (e.g.
livekit-prod-worker). - Copy the key once — Zelto only stores a hash. It is the same org-wide key used by the REST API and MCP server.
Forward a session
Set the key asZELTO_API_KEY in your worker’s environment, then install the
official Python helper. It exports the finalized transcript and tool calls from
LiveKit’s on_session_end callback; when LiveKit has a local recording path it
uploads the audio directly to Zelto too.
agent_external_id is your stable identifier for this deployed agent, not a
per-room name. The SDK sends session data to Zelto’s dedicated AWS ingest
endpoint (https://ingest.zelto.ai), retries transient failures, and exports
the transcript even if recording upload is unavailable. For a self-hosted or
regional endpoint, set ZELTO_INGEST_URL (or pass ingest_url=); the old
ZELTO_API_URL / api_url= names remain compatibility aliases. The manual
payload mapping remains available below for LiveKit Agents versions before 1.0
or custom workflows:
user, assistant, system, or tool. Everything
except call.externalId and an agent reference is optional; you can also
send durationSeconds, recordingUrl, endedReason, cost, and a
customer block.
A successful upload returns { "received": true }.
Forward tool calls
If your LiveKit agent invokes tools during a session, forward each one so it appears inline in the transcript, feeds Zelto’s AI analysis, and lands in the queryable tool-call traces. LiveKit records tools insession.history.items as function_call items (the invocation, with
name / arguments / call_id) and function_call_output items (the
result, keyed by the same call_id). Map each function_call to a tool
turn carrying a structured toolCall, and pair it with its output by
call_id:
toolCall.name is required; arguments and result accept any JSON (a
JSON string is parsed for you), and status is success, error, or
pending. The turn’s content can be empty when a toolCall is present —
Zelto labels the turn with the tool name. Send build_turns(session) as
transcript.turns in the POST above.
Verify the first call
Forward a session, then open Conversations — it shows up tagged as LiveKit within a few seconds. If it doesn’t, check the response your worker got from the POST — a{ "received": true } body means Zelto accepted it,
a 4xx returns the reason — and confirm the worker is sending the
Authorization and X-Zelto-Provider: livekit headers. See
Connect a voice provider.
Stable ids
agent.externalIdidentifies the agent. Zelto finds or creates a LiveKit agent for each distinct value, so use a stable id per agent (not per session). The first session’sagent.namenames it.call.externalId(the room or session id) is the dedup key. Re-posting the same id updates the call in place — refreshing the transcript, recording, or duration — and never double-charges. Send the session once when it ends, then re-send later if you enrich it.
Recordings vs. transcripts
This flow forwards the transcript your agent already has. If you also run egress, include the recording ascall.recordingUrl and Zelto re-hosts the audio. If you
only have audio and no transcript, upload the file through the
REST API instead and Zelto transcribes it.
Connection status
The LiveKit card shows Active once Zelto has ingested at least one LiveKit call. Each agent’s detail page shows when its last session arrived, so you can tell at a glance whether the worker is still forwarding.Stream traces (OpenTelemetry)
The forwarding above feeds Conversations — the transcript-and-analysis view of a call. Separately, a LiveKit agent can stream OpenTelemetry traces into Traces: the per-call span tree (LLM, TTS, STT, tool, and your own spans) with latency, tokens, and cost. LiveKit auto-instruments each session — you only register an OTLP exporter pointed at Zelto’s ingest host. Create a write-only observability ingest key in Traces → Connect agent (needs theowner or admin role), then
register the exporter at the start of your entrypoint, before
session.start() — it must actually run, not just be defined:
OTLPSpanExporter() reads the standard OpenTelemetry environment variables, and
the SDK appends /v1/traces to the endpoint for you:
pip install opentelemetry-exporter-otlp-proto-http.
The Python SDK sends http/protobuf and doesn’t implement http/json, so
keep the protocol at http/protobuf (Zelto accepts both encodings anyway). gRPC
(:4317) is not accepted. This is separate from the transcript forwarding above:
traces use the write-only ingest key — not your ZELTO_API_KEY — and need no
X-Zelto-Provider header. See Traces for what a trace holds and
how to read it.
Related
- Traces — the OpenTelemetry span-tree view of a call (set up in Stream traces above).
- Connect a voice provider — pick a path and verify ingestion.
- Custom & other providers — the canonical call shape this endpoint accepts.
- Conversations — where LiveKit calls land.
- API reference · MCP — read your data programmatically.

