Skip to main content

Web Calls

Web calls let your users talk to your voice agent directly from a web browser — no phone number needed. Using WebRTC, thinnestAI streams audio between the browser and your agent in real time, creating a seamless voice experience embedded in your website or app.

How Web Calls Work

User clicks "Call" button in your website

  Browser requests a WebRTC session from thinnestAI

  thinnestAI voice engine establishes audio stream

  User speaks → STT → AI Agent → TTS → User hears response

  Call ends when user hangs up or agent completes
No phone network is involved. Audio travels over the internet using WebRTC infrastructure powered by LiveKit.

Option 1: Drop-in Embed Widget

For the fastest integration, use the thinnestAI embed widget. It handles all WebRTC complexity, permissions, and UI automatically.

Script Tag Integration

Add this to your website:
<script
  src="https://api.thinnest.ai/embed/widget.js"
  data-publishable-key="pk_live_abc123xyz"
  data-agent-id="ag_pub_xyz789"
  data-widget-type="voice"
  async
></script>
This renders a floating call button. When clicked, it connects directly to your voice agent.

Combined Chat + Voice

To offer both text and voice on the same page, set data-widget-type to "combined":
<script
  src="https://api.thinnest.ai/embed/widget.js"
  data-publishable-key="pk_live_abc123xyz"
  data-agent-id="ag_pub_xyz789"
  data-widget-type="combined"
  async
></script>
[!NOTE] Widget appearance (colors, launcher style) can be customized from the Deploy → Design tab in your Agent Studio.

Option 2: Custom UI with the Voice SDK

If you want full control over the user interface, build a custom interface using the @thinnest-ai/voice-sdk.

Installation

npm install @thinnest-ai/voice-sdk

Starting a Call

import ThinnestVoice from "@thinnest-ai/voice-sdk";

// Initialize the SDK with your API key
const voice = new ThinnestVoice("YOUR_API_KEY");

// Start the call
await voice.start("YOUR_AGENT_PUBLIC_ID", {
  metadata: {
    user_name: "Jane Doe",
    page: "pricing"
  }
});

Handling Events

The Voice SDK emits events that you can listen to in order to update your custom UI:
// Connection status updates
voice.on("status-change", ({ status }) => {
  console.log("Call status:", status); // "active", "ended", etc.
});

// Transcript events (what the user or agent said)
voice.on("transcript", (msg) => {
  console.log(`${msg.role}: ${msg.text}`);
});

// Real-time speech states
voice.on("speech-start", ({ speaker }) => {
  console.log(`${speaker} started speaking`);
});

voice.on("speech-end", ({ speaker }) => {
  console.log(`${speaker} stopped speaking`);
});

// Errors
voice.on("error", (error) => {
  console.error("Call error:", error.message);
});

React Component Example

Here is a simple React implementation using the SDK:
import { useState, useEffect, useRef } from "react";
import ThinnestVoice from "@thinnest-ai/voice-sdk";

function VoiceAgent({ agentId }: { agentId: string }) {
  const voiceRef = useRef<ThinnestVoice>();
  const [status, setStatus] = useState("idle");
  const [transcripts, setTranscripts] = useState([]);

  useEffect(() => {
    voiceRef.current = new ThinnestVoice("YOUR_API_KEY");

    voiceRef.current.on("status-change", ({ status }) => setStatus(status));
    voiceRef.current.on("transcript", (msg) => {
      setTranscripts(prev => [...prev, msg]);
    });
    
    return () => voiceRef.current?.stop();
  }, []);

  const toggleCall = async () => {
    if (status === "active") {
      await voiceRef.current?.stop();
    } else {
      await voiceRef.current?.start(agentId);
    }
  };

  return (
    <div className="call-container">
      <button onClick={toggleCall} className={status === "active" ? "active" : ""}>
        {status === "active" ? "End Call" : "Start Call"}
      </button>
      
      <div className="transcript-box">
        {transcripts.map((msg, i) => (
          <p key={i} className={`msg ${msg.role}`}>
            <strong>{msg.role}:</strong> {msg.text}
          </p>
        ))}
      </div>
    </div>
  );
}

Option 3: Backend-to-Backend Initialization

If you don’t use the Voice SDK and prefer to get a LiveKit token from your backend to connect your own LiveKit client, you can call the underlying REST API.

Step 1: Create a Session Token

curl -X POST "https://api.thinnest.ai/v1/voice/session" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "agent_id": "YOUR_AGENT_PUBLIC_ID",
    "metadata": {
      "user_name": "Jane Doe"
    }
  }'
The response includes the livekit_url, room_name, and JWT token.

Step 2: Connect

Pass these parameters to your LiveKit client implementation (see WebRTC & LiveKit for examples).

Browser Compatibility

Web calls use WebRTC, which is supported by:
BrowserVersionNotes
Chrome56+Full support
Firefox44+Full support
Safari11+Full support
Edge79+Full support (Chromium-based)
Mobile Chrome56+Full support
Mobile Safari11+Requires user gesture to start audio

Microphone Permissions

The browser will prompt the user for microphone access. If access is denied, the Voice SDK emits an error event. Handle the permission denied case in your UI so the user understands why the call cannot start.

Best Practices

  1. Request microphone permission gracefully — If building a custom UI, wait until the user clicks “Call” to initialize the Voice SDK.
  2. Show visual feedback — Indicate when the agent is speaking vs. listening. Users need to know when to talk.
  3. Display a live transcript — Showing the conversation in text alongside the audio helps users follow along and improves accessibility.
  4. Test on mobile — Mobile browsers have stricter autoplay policies. Audio playback requires a user gesture (tap/click) to start, which the Drop-in Widget and Voice SDK handle for you.

Next Steps