Skip to main content
Traces are the OpenTelemetry view of what happened inside a call. One call or session is one trace: a tree of spans (an LLM generation, a text-to-speech step, a speech-to-text step, a tool call, your own code) plus the logs those steps emit. Zelto stores this telemetry so you can see, per call, where the time and tokens went and what failed — replacing a separate Langfuse-plus-logging stack. Open Traces in the sidebar to see one row per trace, with status and duration beside the root span name. Tokens and cost are hidden by default; enable them in Columns. Click a row to explore its span tree or timeline and read each span’s input/output, attributes, and logs in the peek panel. In narrow Tree and Waterfall views, selecting a span opens its details; use Back to spans to return to the tree. The first 50 rows load before the total. You can keep paging while the count is pending; larger record and page totals show their scale, such as >1,000,000. View, column, and density controls sit alongside the primary filters. A toggle at the top switches the same page between two views. Traces (the default) is one row per call. Spans is one row per span across every trace — a flat list you can filter by span kind, model, agent, and date window to compare individual steps (say, every llm span in the last day) without opening each trace one at a time. Both views share the same filter bar, scope, and date window. With no date filter the list shows the last 48 hours. The date control offers quick ranges (last 48 hours, 7 days, 30 days) or a custom from / to, up to 30 days at a time — a wider range is limited to 30 days, and the chip says so. Older traces are still there: pick a range to reach them, back to the 90-day retention window. Opening a trace, or a call’s Trace tab, has no window at all. If a range is too heavy for your volume, the page says traces are temporarily unavailable — retry, or narrow the range.

Explore a trace

On the full trace page, use the copy icon beside the trace ID in the header to copy the complete ID. Open conversation opens the linked call. If the header says No linked conversation, the info icon opens the connection guide. The viewer has two panes: span navigation on the left and details on the right. It starts in Timeline and remembers your chosen view and panel width. Drag the divider, or focus it and use the arrow keys, to resize the panes. The view tabs, search, and collapse button share the left toolbar. Collapse span navigation to give details more room, then expand it to restore your selection, zoom, and width. Audio keeps playing while navigation is collapsed. Search works across all three views; filtering Tracks keeps the full time scale. The left pane offers three views with a shared selection:
  • Tree shows the parent/child hierarchy, span types, duration, and tokens.
  • Timeline adds timing bars on a shared axis beside the hierarchy. Search and collapse are shared with Tree.
  • Tracks groups spans into STT, LLM, tool, TTS, and other recorded lanes. Concurrent spans occupy separate rows. For names that are too short to read on a bar, use Tree or select the bar to inspect its details.
Use + and − to zoom in either timing view and Fit to show the full range. Switching views preserves zoom and selection. Select a span to see its timestamp, duration, model, tokens, and cost on the right. Preview contains input and output; Attributes contains metadata and attribute filters; Logs contains that span’s logs. Previous event and Next event move in time order while keeping the current detail tab. For LLM spans, JSON containing chat messages opens in Formatted view: each message shows its role (such as system, user, or assistant) above readable, wrapping text. This supports message arrays, single messages, and objects with a messages array. Use Raw to see the original input and output, or switch back to Formatted. Tool calls and other message metadata are available under Additional fields. Text content blocks are readable as text; other blocks remain visible as JSON. Plain text and unrecognized or incomplete JSON keep their original display. Select Trace overview at the top of the left pane to open Overview, Attributes, and Logs tabs. Overview shows trace statistics and Duration by step; on the full page, it also holds Call, Transcript, and Monitors for a linked call. Attributes groups recorded values by span, so repeated keys keep their original context. Logs shows all trace logs in time order, including logs without a matching span. Select a span name beneath a log to inspect that span. Select Call, Transcript, or Monitors to load that linked-call panel; select it again to collapse it. When the trace links to a conversation with audio, a player stays below the left pane. Play, pause, seek, change speed, or download the recording while inspecting spans. Playback continues when switching spans or navigation views. On narrow screens, selecting a span or Trace overview opens the detail view. Use Back to spans or Escape to return. Trace and span logs show millisecond timestamps; hover a timestamp for the full date and timezone. Linking connects a trace’s timing, errors, and token usage to what happened in the actual call. You can open the conversation from the trace header, read its transcript and monitor results, and listen to its recording while inspecting spans. Audio, transcripts, and monitor results appear when they are available on the conversation; linking does not generate missing call data.
  1. Send the conversation to the same organization. Use your provider integration or the call ingestion API. For API-ingested calls, set call.externalId to the provider’s call, room, or session ID. Exporting traces alone does not create a conversation.
  2. Send that same ID with the trace. Set session.id on your spans or OpenTelemetry resource to the exact external ID stored with the conversation. For example, inside an active Python span:
    Zelto also recognizes call.id, gen_ai.conversation.id, conversation.id, zelto.reference_id, livekit.room, livekit.room_name, room.name, and lk.room_name. These attributes are external call references, not the trace ID. With native JSON, use the span’s sessionRef field. For LiveKit, use the same room name in call.externalId and the trace’s room/session attribute.
  3. Reopen the trace after both arrive. The header shows Open conversation when Zelto finds one matching conversation. You can send the trace before or after the conversation.
If it still says No linked conversation, confirm the conversation exists in the same organization and that the external IDs match exactly. Use a unique ID per call: Zelto does not choose between duplicate matches. Any agent attribution already on a span must agree with the conversation’s agent, and traces spanning multiple conversations do not show a single conversation link.

Identify agents with your own IDs

Send your stable agent ID as zelto.agent_external_id on each span/log, or as an OpenTelemetry resource attribute when the resource represents one agent:
Use the same ID as agent.externalId in your call uploads. Keep it stable across calls and releases. session.id changes for each call; it should match that call’s call.externalId. Zelto reuses the matching agent in your organization, or creates one named after your external ID. A trace can arrive before its call. Include zelto.agent_provider with the same provider used by call ingestion (livekit in the example) so both paths create the same agent. If omitted, an existing unambiguous match is reused; new agents use the zelto provider. IDs are case-sensitive strings of up to 255 characters; provider names support up to 50 characters. An ambiguous ID across providers stays unassigned until you specify its provider. Merged IDs resolve to their surviving agent. For multiple agents in one exporter, put the external ID on each span/log instead of a shared resource. Span/log attributes override resource defaults. An explicit zelto.agent_id remains a Zelto UUID and takes precedence; do not put your external ID in that field, even if it is also a UUID. Native JSON exports use the same keys inside each span or log’s attributes object. Their existing agentId field still means a Zelto UUID.

What a trace holds

  • Spans, each tagged with a normalized kind: llm, tts, stt, vad, tool, code, telephony, livekit, session, or other. GenAI spans carry model, token, and cost fields promoted for fast filtering.
  • Logs, attached to a span or to the trace as a whole, with a severity and body.
  • A session reference (room / call / session id) that links a trace back to its conversation when the two can be matched.
Traces are retained for 90 days.

Send telemetry

Start with Send traces to Zelto for a runnable first trace, agent identity, call linking, logs, and troubleshooting.
If your calls come from Vapi, you get traces for free — Zelto turns each pulled call’s Vapi platform logs into a trace, with no exporter to install. See Vapi → Traces.
Traces is fed by standard OTLP/HTTP, so any agent with an OpenTelemetry exporter works — including a LiveKit agent, whose sessions LiveKit auto-instruments. There’s no Zelto-specific client code to write, but you do register an OTLP exporter pointed at Zelto — a few lines in your agent (see LiveKit for the exact snippet). Create a per-organization, write-only observability ingest key (shown once) and copy the exporter config from either Settings → Integrations → Observability or Traces → Connect agent — both mint the same kind of key. Creating and revoking ingest keys needs the owner or admin role. Point your exporter at Zelto’s ingest host and authenticate with that key (a bearer token):
The exporter posts spans to /v1/traces and logs to /v1/logs — the OpenTelemetry SDK appends those paths to the endpoint above for you. Both OTLP/HTTP encodings are accepted — protobuf (what most exporters send by default, including the OpenTelemetry Python SDK a LiveKit agent uses) and JSON — so you don’t have to match a specific one. gRPC (:4317) is not accepted; use the HTTP endpoint above. Telemetry requests to /v1/traces, /v1/logs, and /webhooks/otel accept up to 10 MB (10,000,000 bytes) per request, measured after decompression. Larger requests return HTTP 413; reduce the export batch size before retrying them.
The Python HTTP exporter used in these examples sends protobuf. Leave OTEL_EXPORTER_OTLP_PROTOCOL at http/protobuf; Zelto also accepts OTLP JSON from exporters that support it.
Ingestion is rate-limited per organization. If you export at a very high rate, some requests get a 429 with a Retry-After header — honor it and retry with backoff. Exporter retry budgets and queues are finite, so monitor export errors and dropped batches. If your agent legitimately exports at a high volume, ask your Zelto contact to raise the per-organization ingest rate.
If no traces match your filters, use Clear filters or adjust the date range. If your organization has no traces yet, confirm the exporter is pointed at the ingest host above and is sending a valid Authorization: Bearer ingest key.
HTTP 200 acknowledges acceptance. With buffered ingestion, query visibility follows asynchronously; the target is 30 seconds under normal load, not a guaranteed maximum. Exporter batching and outages can add delay. OTLP returns {} and native JSON returns {"received":true}.

Native JSON

If your agent doesn’t speak OTLP, post a simpler Zelto-native JSON body of spans and logs to /webhooks/otel with the same bearer key. See the complete example. Use OTLP when your stack already supports it.

Backfill from tool calls

Already recording tool calls but not yet exporting OpenTelemetry? Zelto can project your existing tool-call history into traces so the view isn’t empty while you wire up an exporter — each tool call becomes a tool span, and a call’s tool calls share one trace. Only calls from the last 90 days are kept (the trace retention window). Ask your Zelto contact to run the tool-call backfill for your organization.
  • Tool calls: compare tool usage, outcomes, and duration across calls and agents.
  • Conversations — the transcript-and-analysis view of a call; a trace links to its conversation when the session id matches.
  • LiveKit — point a LiveKit agent’s OpenTelemetry exporter at Zelto to stream traces.