Add a live AI voice button to your website in under 15 minutes
This guide walks you from zero to a working “Talk to AI” button on your website using Truedy’s WebRTC API and the Ultravox browser SDK. By the end, a visitor clicks the button, the browser requests microphone access, and they are immediately in a live two-way voice conversation with your agent.
Node.js 18+ or Python 3.9+ for the server component
A modern browser (Chrome, Edge, Firefox, or Safari 15.4+)
Your Truedy API key must never appear in client-side code. All requests to api.truedy.ai must be made from your server. The browser only receives a short-lived joinUrl.
1
Install the Ultravox client SDK
The Ultravox SDK handles WebRTC negotiation and the audio session in the browser.
npm install ultravox-client
If you are not using a bundler, you can load it from a CDN:
<script type="module"> import { UltravoxSession } from "https://esm.sh/ultravox-client";</script>
2
Create a server endpoint that issues a joinUrl
Your server calls the Truedy API to start a WebRTC session and returns the single-use joinUrl to the browser. The browser never sees your API key.
import express from "express";const app = express();app.post("/api/start-call", async (req, res) => { try { const response = await fetch( "https://api.truedy.ai/api/public/v1/webrtc/call", { method: "POST", headers: { Authorization: `Bearer ${process.env.TRUEDY_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ agent_id: process.env.TRUEDY_AGENT_ID, }), } ); if (!response.ok) { const error = await response.json(); return res.status(502).json({ error: error.message ?? "Failed to start call" }); } const { join_url } = await response.json(); // join_url is single-use — send it directly, do not cache it res.json({ joinUrl: join_url }); } catch (err) { console.error("start-call error:", err); res.status(500).json({ error: "Internal server error" }); }});app.listen(3000, () => console.log("Server running on http://localhost:3000"));
Set the required environment variables before starting your server:
Create a minimal index.html. This is a complete, self-contained example — no build step required.
index.html
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Talk to AI</title> <style> body { font-family: system-ui, sans-serif; display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 100vh; margin: 0; background: #f9fafb; gap: 1rem; } #talk-btn { padding: 0.75rem 2rem; font-size: 1rem; border-radius: 9999px; border: none; background: #6366f1; color: white; cursor: pointer; transition: background 0.2s; } #talk-btn:hover { background: #4f46e5; } #talk-btn:disabled { background: #a5b4fc; cursor: not-allowed; } #status { color: #6b7280; font-size: 0.9rem; } </style></head><body> <button id="talk-btn">Talk to AI</button> <p id="status">Click to start</p> <script type="module"> import { UltravoxSession } from "https://esm.sh/ultravox-client"; const btn = document.getElementById("talk-btn"); const status = document.getElementById("status"); let session = null; btn.addEventListener("click", async () => { if (session) { // End the active call await session.leaveCall(); session = null; btn.textContent = "Talk to AI"; status.textContent = "Call ended"; return; } btn.disabled = true; status.textContent = "Connecting..."; try { // 1. Fetch a single-use joinUrl from your server const res = await fetch("/api/start-call", { method: "POST" }); if (!res.ok) throw new Error("Could not start call"); const { joinUrl } = await res.json(); // 2. Join the call — the SDK requests microphone access automatically session = new UltravoxSession(); session.addEventListener("status", (e) => { status.textContent = e.state; }); await session.joinCall(joinUrl); btn.disabled = false; btn.textContent = "End call"; } catch (err) { console.error(err); status.textContent = "Connection failed — please try again"; btn.disabled = false; } }); </script></body></html>
4
Test it
Start your server (node index.js or flask run)
Open http://localhost:3000 in your browser
Click Talk to AI
Accept the microphone permission prompt
Speak — your agent will respond in real time
Open your browser’s developer console to see SDK status events and any errors. The status event cycles through connecting → connected → active on a successful call.
Every call to POST /webrtc/call produces a unique joinUrl that can only be joined once. Never cache or reuse it. If a user refreshes the page, your frontend must request a new joinUrl from your server.
Your Truedy API key authenticates you to api.truedy.ai. It must only ever exist in server-side code or environment variables — never in HTML, JavaScript files served to the browser, or version control.
The Ultravox SDK calls getUserMedia internally when joinCall is invoked. The browser will display a native permission prompt the first time. If the user denies microphone access, joinCall throws an error — handle it gracefully in your UI.