> ## Documentation Index
> Fetch the complete documentation index at: https://docs.usetuner.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Overview

> Send call data from any voice stack to Tuner — with our Python observability libraries or directly via the API (for platforms not natively supported).

<iframe width="100%" style={{ aspectRatio: "16/9" }} src="https://www.youtube.com/embed/Izr5JsWDEzM" title="Custom Integration" frameBorder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowFullScreen />

Use this guide if you have a custom voice stack or a platform without a dedicated SDK — Otherwise see [Connecting to Pipecat](/docs/api-and-integrations/connecting-to-pipecat), [Connecting to LiveKit](/docs/api-and-integrations/connecting-to-livekit), [Connecting to Dograh](/docs/api-and-integrations/connecting-to-dograh).

***

## What this is

Custom Integration lets you push **completed call data** (transcript + metadata) from your own voice stack into Tuner so Tuner can process the call and run your configured analyses.

There are two ways to get that data in:

<CardGroup cols={2}>
  <Card title="Observability libraries" icon="cubes" href="/docs/api-and-integrations/custom-integration/quickstart">
    **Python stacks.** Attach observers to your STT, TTS, and LLM framework — they capture the timeline, timing, and latency automatically and send everything in one `flush()` at call end. Start with the [Quickstart](/docs/api-and-integrations/custom-integration/quickstart).
  </Card>

  <Card title="Direct API" icon="code" href="/docs/api-reference/create-call">
    **Any language, any stack.** Build the call timeline yourself and send it to the [Create Call API](/docs/api-reference/create-call) after the call ends. Follow the [Happy Path](#happy-path-direct-api) below.
  </Card>
</CardGroup>

Both paths produce the same result in Tuner and follow the same contract. The libraries are simply a client for the API that does the capturing for you.

***

## The Contract

To make calls show up under the right agent, your integration must consistently map these identifiers:

* **workspace\_id**: the Tuner workspace receiving the call
* **agent\_remote\_identifier**: Your **Agent ID** configured in Tuner. Get this value from Agent Settings > Agent Connection > Agent Remote ID
* **call\_id**: your unique identifier for the call (use the same value every retry)
* **sip\_call\_id** *(inbound simulation only)*: the SIP `Call-ID` from the inbound call, required to link inbound simulated calls back to their Tuner simulation row. See [SIP for other platforms](/docs/simulation/other-platforms#sync-the-call-back-with-the-sip-id).
* **recipient** *(outbound simulation only)*: the SIP URI your system dialed, exactly as Tuner sent it, required to link outbound simulated calls back to their Tuner simulation row. See [Outbound Simulation](/docs/simulation/outbound/individual-platforms).

***

## Authentication

All API requests use **Bearer authentication**.

Send your Tuner API key in the `Authorization` header (copy it from **Workspace Settings** → **API Keys & Integrations**):

* `Authorization: Bearer <YOUR_TUNER_API_KEY>`

The libraries read the same key from the `TUNER_API_KEY` environment variable and send it for you.

***

## Happy Path (direct API)

Building against the API directly — because your stack isn't Python, or you want full control over the payload:

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** (including agent and user messages, tool calls, and any node or state transitions)
   * call start and end timestamps
   * a publicly accessible recording URL
   * any relevant operational or enrichment data (for example: cost data, call outcome, dynamic variables, or provider-native analysis)
3. Call the **Create Call** endpoint with all data based on the API reference
4. Open Tuner → Calls to see the processed results.

On a Python stack, the [Quickstart](/docs/api-and-integrations/custom-integration/quickstart) replaces steps 2–3: the observers capture the timeline and `session.flush()` sends it.

***

## API Reference

See the full **Create Call** request/response schema and examples:

→ [Create Call API Reference](/docs/api-reference/create-call)

***

## Libraries

For Python stacks, Tuner ships small, focused **observability libraries** — one per role in your voice pipeline. Each hooks into your provider's native event system to capture transcript, timing, and latency; your audio loops and synthesis code stay untouched. They share one lifecycle: create a `TunerSession` per call, attach the observers that match your stack, and `flush()` at call end.

Attach only the observers that match your stack — each is independent. If we don't ship an adapter for one of your providers, extend the base adapter for that role (see *Custom providers* on the Speech-to-Text and Text-to-Speech pages); everything still flows through the same session and single `flush()`.

<CardGroup cols={2}>
  <Card title="Quickstart" icon="rocket" href="/docs/api-and-integrations/custom-integration/quickstart">
    The session lifecycle, credentials, and every observer wired together — start here.
  </Card>

  <Card title="Speech-to-Text" icon="microphone" href="/docs/api-and-integrations/libraries/tuner-stt">
    `tuner-stt-observer` — Deepgram and Speechmatics adapters for user transcript and STT latency.
  </Card>

  <Card title="Text-to-Speech" icon="volume-high" href="/docs/api-and-integrations/libraries/tuner-tts">
    `tuner-tts-observer` — Cartesia adapter, agent transcript, TTS/e2e latency, word-accurate interruption capture.
  </Card>

  <Card title="LangChain / LangGraph" icon="link" href="/docs/api-and-integrations/libraries/tuner-langchain">
    `tuner-langchain` — node transitions, tool calls, and per-node LLM latency in the Tuner transcript pipeline.
  </Card>
</CardGroup>

***

## Webhooks

You can add a webhook URL to your agent under **Agent Settings** → **Webhooks** to receive Tuner events — a notification with a link to each call after it's analyzed, and real-time alerts when they fire. See the [Webhooks](/docs/agent-configurations/webhooks) guide for setup and payload examples.

***

## Best practices (what keeps integrations stable)

* **Make retries safe:** Treat `call_id` as immutable and retry the same request on failure.
* **Send the final transcript:** If you only have partial transcripts, send only when final to avoid incomplete analysis.
* **Flush in `finally`:** If you use the libraries, call `session.flush()` in a `finally` block so dropped connections and errors don't skip it.
* **Log request IDs + responses:** You'll want these when debugging ingestion issues.

***

## 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/invalid
* (libraries) `session.flush()` never ran — put it in a `finally` block

### Unauthorized (401)

* Ensure you're sending `Authorization: Bearer <API_KEY>` (not `X-API-Key`).
* (libraries) Ensure `TUNER_API_KEY` is set in the environment where your agent runs.

### 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

<CardGroup cols={2}>
  <Card title="Quickstart (libraries)" href="/docs/api-and-integrations/custom-integration/quickstart" />

  <Card title="Review your first call" href="/user-guide/quick-start/how-to-review-your-first-call" />

  <Card title="Connecting to Retell" href="/docs/api-and-integrations/connecting-to-retell" />

  <Card title="Connecting to Vapi" href="/docs/api-and-integrations/connecting-to-vapi" />

  <Card title="Connecting to LiveKit" href="/docs/api-and-integrations/connecting-to-livekit" />

  <Card title="Connecting to Pipecat" href="/docs/api-and-integrations/connecting-to-pipecat" />
</CardGroup>
