Skip to main content
On a Python stack? Use the Python SDKs instead. They capture the timeline for you and send it in one call. This guide is for every other language.
The API is what the SDKs call under the hood. Going direct means you own the part the observers would have handled: assembling the structured conversation timeline, and deriving per-turn timing and interruptions from whatever your stack exposes. Concretely, you’ll be building:
  • the turn-by-turn timeline (user speech, agent speech, tool calls, tool results), each with start and end timestamps relative to call start
  • per-turn latency fields, if you want them: STT time-to-transcript, LLM time-to-first-token, TTS time-to-first-byte, end-to-end
  • interruption detection, by comparing when a user turn starts against when the agent’s turn ended
For a worked example of how much that is in practice, see Connecting to Vapi (Transient), a single-file integration that does this against Vapi’s payload and call log.

Prerequisites

  • An active Tuner account
  • A Tuner agent with provider Custom API
  • Your Tuner API key and workspace ID. See API keys and the contract.

Send your first call

The video below walks through the full flow.
  1. Create a Tuner Agent and select Custom API as the integration type.
  2. In your voice system, after the call ends, capture:
    • the full structured conversation timeline (agent and user messages, tool calls, and any node or state transitions)
    • call start and end timestamps
    • a public https:// recording URL. Tuner treats any other value, such as a placeholder or a localhost link, as no recording.
    • any relevant operational or enrichment data, such as cost, call outcome, dynamic variables, or provider-native analysis
  3. Call the Create Call endpoint with all data, following the API reference.
  4. Open Call Logs in Tuner to see the processed results.

API reference

The full Create Call request and response schema, with examples, is in the Create Call API reference.

Enable simulation

Required if you’ll use Call Simulation. Skip it otherwise. Tuner links each simulated call to its run through a field you send in the Create Call request: sip_call_id or sip_headers for inbound agents and recipient for outbound agents (how simulated calls are linked). Do the steps for your agent’s call direction.

Inbound

Tuner calls your agent over SIP.
  1. Add your agent’s SIP URI in Agent Settings > Simulation Setup. Get it from your SIP provider, then see Set up inbound simulation.
  2. Send the SIP Call-ID of the call Tuner placed as sip_call_id. Read it from the inbound call in your SIP stack; it may arrive under a different name.
Tuner also adds an X-Correlation-Id header to the call. If a carrier or proxy between Tuner and your agent changes the Call-ID, send the header instead, as sip_headers. Tuner keeps only this header, and uses it only to link the call.
sip_call_id doesn’t replace call_id. Tuner stores the call under call_id and uses it to detect duplicates.

Outbound

Your agent calls a Simulated Caller at a SIP URI that Tuner sends to your trigger endpoint.
  1. Configure the trigger endpoint in Agent Settings > Simulation Setup. See Set up outbound simulation.
  2. Send the SIP URI your agent dialed as recipient, exactly as Tuner sent it.

Check simulation works

Start a simulation run. See Run a simulation. When the call ends, check its result in the run. If it shows Unreported, Tuner never received a matching call from your agent. See Fix an Unreported run.

Optional fields

The Create Call request accepts more optional fields, such as agent_version. The API reference lists every field. The fields simulation needs are covered in Enable simulation. --- | --- | | sip_call_id | The inbound call’s SIP Call-ID. Required for inbound simulation; see Enable simulation. | | recipient | The phone number or SIP URI your agent dialed. Required for outbound simulation; see Enable simulation. | | agent_version | An integer (0 or greater) for the version of your agent that handled the call. |

Best practices

  • Make retries safe. Treat call_id as immutable and retry the same request on failure. A duplicate call_id returns 409, which counts as success.
  • Send the final transcript. If you only have partial transcripts, send only when final, or the analysis runs on incomplete data.
  • Log request IDs and responses. You’ll want them when debugging ingestion.
  • Back off on 429. Create Call is rate limited per workspace. A request over the limit returns 429; retry it later.

OpenTelemetry traces

To show a span tree on the call, send spans to Tuner’s OTLP endpoint alongside your Create Call request. Any OpenTelemetry SDK works if it exports OTLP over HTTP with protobuf encoding. The full requirements are in Traces.
1

Point your exporter at Tuner

With environment variables, in any language:
Use the signal-specific OTEL_EXPORTER_OTLP_TRACES_ENDPOINT with the full URL. The exporter appends /v1/traces to the generic OTEL_EXPORTER_OTLP_ENDPOINT, which does not resolve to Tuner’s path, and that variable also applies to metrics and logs.Or in code, with the Python SDK:
Use the HTTP exporter (opentelemetry.exporter.otlp.proto.http). Tuner does not accept gRPC.
2

Tag a span with the call ID

Use the same call_id you send to Create Call. One tagged span is enough: Tuner applies the ID to every span in the same trace. It links the spans to the call once both have arrived, in either order.
3

Check that it worked

A Traces tab appears on the call details page once spans have matched. Calls with no spans show no tab.

Before you finish

  • A test call appears in Call Logs.
  • Your API key, workspace ID and Agent Remote ID come from API keys.
  • If you use Call Simulation: Enable simulation is done for your agent’s call direction, and one simulated call shows a result other than Unreported.

Common issues

Calls don’t appear in Tuner

Usually one of:
  • wrong workspace_id
  • agent_remote_identifier doesn’t match the agent mapping you intended
  • the call was sent but the transcript is empty or invalid

Unauthorized (401)

  • Ensure you’re sending Authorization: Bearer <API_KEY>, not X-API-Key.
  • Confirm the key belongs to the workspace you’re sending to.

Validation errors (4xx)

  • Follow the API reference for required fields and types.
  • If you’re unsure which field failed, start from the minimal example in the API reference and add fields incrementally.

Next steps

Create Call API reference

Required fields, types, and a minimal example.

Worked example

A full single-file integration built on this API.

Webhooks

Receive events when analysis finishes or an alert fires.

Review your first call

What Tuner does with the data once it lands.