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 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
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.- Create a Tuner Agent and select Custom API as the integration type.
- 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 alocalhostlink, as no recording. - any relevant operational or enrichment data, such as cost, call outcome, dynamic variables, or provider-native analysis
- Call the Create Call endpoint with all data, following the API reference.
- 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.- Add your agent’s SIP URI in Agent Settings > Simulation Setup. Get it from your SIP provider, then see Set up inbound simulation.
- Send the SIP
Call-IDof the call Tuner placed assip_call_id. Read it from the inbound call in your SIP stack; it may arrive under a different name.
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.- Configure the trigger endpoint in Agent Settings > Simulation Setup. See Set up outbound simulation.
- 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 asagent_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_idas immutable and retry the same request on failure. A duplicatecall_idreturns409, 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 returns429; 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 Use the HTTP exporter (
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:opentelemetry.exporter.otlp.proto.http). Tuner does not accept gRPC.2
Tag a span with the call ID
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_identifierdoesn’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>, notX-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.