Skip to main content

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

Starts a WebRTC session against one of your agents and returns a joinUrl. Pass this URL to the Ultravox client SDK to connect audio from the browser or native app. Request body
FieldTypeRequiredDescription
agent_idstring (UUID)YesThe Truedy agent to connect to. Must belong to your organisation and be active.
variablesobjectNoKey-value pairs for mustache template substitution in the agent’s prompt (e.g. {{first_name}}).
Example — minimal
{
  "agent_id": "AGENT_UUID"
}
Example — with template variables
{
  "agent_id": "AGENT_UUID",
  "variables": {
    "first_name": "Jane",
    "company": "Acme Inc",
    "account_tier": "pro"
  }
}
Response — 200 OK
{
  "joinUrl": "wss://voice.ultravox.ai/calls/...",
  "callId": "uv-abc123",
  "agentName": "My Sales Agent"
}
FieldTypeDescription
joinUrlstringWebSocket URL — pass directly to the Ultravox SDK
callIdstringUltravox call identifier (useful for debugging)
agentNamestringDisplay name of the agent
Errors
StatusMeaning
401Missing or invalid API key, or agent belongs to a different organisation
404Agent not found, not active, or not yet synced to Ultravox
429Rate limit exceeded for your API key
502Ultravox communication error (retry is safe)

Connecting with the Ultravox SDK

Install the SDK:
npm install ultravox-client
Then join the call:
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:
EventDataUse case
transcriptsArray of { speaker, text, isFinal } objectsShow live captions
statusUltravoxSessionStatus enumShow call state (connecting, idle, listening, thinking, speaking, ended)
// 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:
{
  "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 for a full end-to-end example.