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

# WebRTC

> Start browser-based voice calls directly from your own frontend using the Truedy WebRTC API

# WebRTC

The WebRTC API lets you build your own voice interfaces — custom widgets, embedded chat buttons, mobile apps — using Truedy agents as the AI backend. Your frontend connects directly to the call over WebSocket; there is no phone number or telephony involved.

## How it works

```
Your frontend
     │
     ├─ 1. POST /api/public/v1/webrtc/call  ──▶  Truedy API
     │         Authorization: Bearer truedy_xxx        │
     │         { agent_id, variables? }               │
     │                                                  │
     │         ◀── { joinUrl, callId, agentName } ────┘
     │
     └─ 2. Pass joinUrl to Ultravox JS SDK
              new UltravoxSession().joinCall(joinUrl)
                    ↓
              Live WebRTC audio  ←→  Your Truedy agent
```

## Start a WebRTC Call

<Endpoint method="POST" path="/api/public/v1/webrtc/call" />

Starts a WebRTC session against one of your agents and returns a `joinUrl`. Pass this URL to the [Ultravox client SDK](https://github.com/fixie-ai/ultravox-client-sdk-js) to connect audio from the browser or native app.

**Request body**

| Field       | Type          | Required | Description                                                                                       |
| ----------- | ------------- | -------- | ------------------------------------------------------------------------------------------------- |
| `agent_id`  | string (UUID) | **Yes**  | The Truedy agent to connect to. Must belong to your organisation and be active.                   |
| `variables` | object        | No       | Key-value pairs for mustache template substitution in the agent's prompt (e.g. `{{first_name}}`). |

**Example — minimal**

```json theme={null}
{
  "agent_id": "AGENT_UUID"
}
```

**Example — with template variables**

```json theme={null}
{
  "agent_id": "AGENT_UUID",
  "variables": {
    "first_name": "Jane",
    "company": "Acme Inc",
    "account_tier": "pro"
  }
}
```

**Response — `200 OK`**

```json theme={null}
{
  "joinUrl": "wss://voice.ultravox.ai/calls/...",
  "callId": "uv-abc123",
  "agentName": "My Sales Agent"
}
```

| Field       | Type   | Description                                       |
| ----------- | ------ | ------------------------------------------------- |
| `joinUrl`   | string | WebSocket URL — pass directly to the Ultravox SDK |
| `callId`    | string | Ultravox call identifier (useful for debugging)   |
| `agentName` | string | Display name of the agent                         |

**Errors**

| Status | Meaning                                                                  |
| ------ | ------------------------------------------------------------------------ |
| `401`  | Missing or invalid API key, or agent belongs to a different organisation |
| `404`  | Agent not found, not active, or not yet synced to Ultravox               |
| `429`  | Rate limit exceeded for your API key                                     |
| `502`  | Ultravox communication error (retry is safe)                             |

***

## Connecting with the Ultravox SDK

Install the SDK:

```bash theme={null}
npm install ultravox-client
```

Then join the call:

```typescript theme={null}
import { UltravoxSession, UltravoxSessionStatus } from 'ultravox-client'

async function startCall(agentId: string) {
  // 1. Get joinUrl from Truedy
  const res = await fetch('https://api.truedy.ai/api/public/v1/webrtc/call', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer YOUR_TRUEDY_API_KEY',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ agent_id: agentId }),
  })
  const { joinUrl } = await res.json()

  // 2. Connect audio
  const session = new UltravoxSession()

  session.addEventListener('status', (e) => {
    console.log('Call status:', session.status)
  })

  session.addEventListener('transcripts', (e) => {
    const latest = session.transcripts.at(-1)
    console.log(`[${latest?.speaker}]: ${latest?.text}`)
  })

  await session.joinCall(joinUrl)
  return session
}

// End the call
async function endCall(session: UltravoxSession) {
  await session.leaveCall()
}
```

The `joinUrl` is single-use and expires after a short window — connect immediately after receiving it.

***

## Transcript and state events

When connected, the Ultravox SDK emits two real-time data streams you can use to build your UI:

| Event         | Data                                          | Use case                                                                 |
| ------------- | --------------------------------------------- | ------------------------------------------------------------------------ |
| `transcripts` | Array of `{ speaker, text, isFinal }` objects | Show live captions                                                       |
| `status`      | `UltravoxSessionStatus` enum                  | Show call state (connecting, idle, listening, thinking, speaking, ended) |

```typescript theme={null}
// Live captions example
session.addEventListener('transcripts', () => {
  const texts = session.transcripts
    .filter(t => t.isFinal)
    .map(t => `${t.speaker === 'agent' ? '🤖' : '👤'} ${t.text}`)
  captionsDiv.innerText = texts.join('\n')
})

// Call state indicator
session.addEventListener('status', () => {
  indicator.className = session.status  // 'listening' | 'thinking' | 'speaking'
})
```

***

## Template variables

Use `variables` to personalise the agent's behaviour per-call without changing the agent's prompt. Any `{{placeholder}}` in the agent's instructions is substituted at call time:

**Agent prompt (configured in Truedy dashboard):**

```
You are helping {{first_name}} from {{company}}. Their account tier is {{account_tier}}.
```

**API call:**

```json theme={null}
{
  "agent_id": "AGENT_UUID",
  "variables": {
    "first_name": "Jane",
    "company": "Acme Inc",
    "account_tier": "pro"
  }
}
```

The substitution happens server-side before the call is created — the Ultravox agent never sees raw `{{placeholders}}`.

***

## Security note

The `joinUrl` grants direct access to a live AI call. Always create it **server-side** — never expose your Truedy API key to the browser. Your backend fetches the `joinUrl` and returns it to your frontend; your frontend only ever sees the `wss://` URL.

```
Browser ──POST /start-call──▶ Your backend ──POST /api/public/v1/webrtc/call──▶ Truedy
                               (holds API key)
                        ◀── { joinUrl } ─────────────────────────────────────────
Browser ──joinCall(joinUrl)──▶ Ultravox WebRTC
```

See the [Custom Widget guide](/guides/webrtc-custom-widget) for a full end-to-end example.
