ToothFairyAI — Voice API Integration Guide
This guide shows how to integrate ToothFairyAI Voice Agents into your web and mobile applications using WebRTC and the LiveKit SDK.
How it works (at a glance)
User speaks → WebRTC → LiveKit → ToothFairyAI Agent → Response → LiveKit → WebRTC → Audio playback:
- API Key Setup: Generate API key from workspace Admin settings
- Token Generation: Request a voice session token from the Voice API
- LiveKit Connection: Connect to LiveKit room using the token
- Audio Streaming: Enable microphone and handle incoming audio streams
- Agent processes speech and responds in real-time
Voice API integration is available exclusively for Business and Enterprise plans.
Prerequisites
- A ToothFairyAI workspace with Business or Enterprise plan
- API key generated from Admin → API Integration
- A voice-enabled agent configured in your workspace
- Basic knowledge of JavaScript/TypeScript
Step 1: Get Your API Key
1.1) Navigate to API Integration
- Log into your ToothFairyAI workspace
- Go to Admin → API Integration
- Click Generate API Key if you don't have one
- Copy and securely store your API key
Your API key is sensitive information. Never expose it in client-side code. Always make token requests from your backend server.
Step 2: Generate a Voice Token
2.1) Token Request
Make a POST request to the Voice API to generate a session token:
curl -X POST https://aiv.toothfairyai.com/token \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"workspace_id": "your_workspace_id",
"agent_id": "your_agent_id",
"chat_id": "unique_chat_id"
}'
2.2) Token Response
The API returns a token and connection details:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"url": "wss://toothfairyai-15k1aw45.livekit.cloud",
"room_name": "ws_abc123::chat_xyz789"
}
2.3) Request Parameters
| Parameter | Required | Description |
|---|---|---|
workspace_id | Yes | Your workspace identifier |
agent_id | Yes | The voice agent to handle the conversation |
chat_id | Yes | Unique conversation identifier |
user_id | No | Optional user identifier (defaults to "voice-user") |
Tokens are valid for 1 hour (3600 seconds). Generate a new token for extended sessions.
Step 3: Install LiveKit SDK
3.1) NPM Installation
npm install livekit-client
3.2) CDN (Alternative)
<script src="https://cdn.jsdelivr.net/npm/livekit-client/dist/livekit-client.umd.min.js"></script>
Step 4: Connect to Voice Session
4.1) Initialize Room Connection
import { Room, RoomEvent } from 'livekit-client';
// Create room instance
const room = new Room();
// Connect using token from Step 2
await room.connect(tokenResponse.url, tokenResponse.token);
console.log('Connected to room:', room.name);
4.2) Enable Microphone
// Request microphone access and enable
await room.localParticipant.setMicrophoneEnabled(true);
console.log('Microphone enabled');
4.3) Handle Incoming Audio
// Listen for audio tracks from the agent
room.on(RoomEvent.TrackSubscribed, (track, publication, participant) => {
if (track.kind === 'audio') {
// Attach audio track to an audio element
const audioElement = track.attach();
document.body.appendChild(audioElement);
console.log('Agent audio connected');
}
});
Step 5: Complete Integration Example
5.1) Full Implementation
import { Room, RoomEvent } from 'livekit-client';
class VoiceAgent {
constructor() {
this.room = new Room();
this.setupEventHandlers();
}
setupEventHandlers() {
// Handle new audio tracks
this.room.on(RoomEvent.TrackSubscribed, (track) => {
if (track.kind === 'audio') {
const audioElement = track.attach();
audioElement.id = 'agent-audio';
document.body.appendChild(audioElement);
}
});
// Handle disconnection
this.room.on(RoomEvent.Disconnected, () => {
console.log('Disconnected from voice session');
this.cleanup();
});
// Handle connection errors
this.room.on(RoomEvent.ConnectionQualityChanged, (quality) => {
console.log('Connection quality:', quality);
});
}
async connect(token, url) {
try {
await this.room.connect(url, token);
await this.room.localParticipant.setMicrophoneEnabled(true);
console.log('Voice session started');
} catch (error) {
console.error('Connection failed:', error);
throw error;
}
}
async disconnect() {
await this.room.disconnect();
this.cleanup();
}
cleanup() {
const audioElement = document.getElementById('agent-audio');
if (audioElement) {
audioElement.remove();
}
}
}
// Usage
const agent = new VoiceAgent();
// Get token from your backend
const tokenResponse = await fetch('/api/voice-token', {
method: 'POST',
body: JSON.stringify({ agent_id: 'your_agent_id' })
}).then(r => r.json());
// Connect to voice session
await agent.connect(tokenResponse.token, tokenResponse.url);
5.2) React Integration
import { useEffect, useRef, useState } from 'react';
import { Room, RoomEvent } from 'livekit-client';
function VoiceChat({ agentId }) {
const [isConnected, setIsConnected] = useState(false);
const [isMuted, setIsMuted] = useState(false);
const roomRef = useRef(null);
const audioRef = useRef(null);
useEffect(() => {
return () => {
roomRef.current?.disconnect();
};
}, []);
const startCall = async () => {
// Get token from your backend
const response = await fetch('/api/voice-token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ agent_id: agentId })
});
const { token, url } = await response.json();
// Create and connect room
const room = new Room();
roomRef.current = room;
room.on(RoomEvent.TrackSubscribed, (track) => {
if (track.kind === 'audio') {
track.attach(audioRef.current);
}
});
await room.connect(url, token);
await room.localParticipant.setMicrophoneEnabled(true);
setIsConnected(true);
};
const endCall = async () => {
await roomRef.current?.disconnect();
setIsConnected(false);
};
const toggleMute = async () => {
const enabled = !isMuted;
await roomRef.current?.localParticipant.setMicrophoneEnabled(!enabled);
setIsMuted(enabled);
};
return (
<div>
<audio ref={audioRef} autoPlay />
{!isConnected ? (
<button onClick={startCall}>Start Voice Call</button>
) : (
<>
<button onClick={toggleMute}>
{isMuted ? 'Unmute' : 'Mute'}
</button>
<button onClick={endCall}>End Call</button>
</>
)}
</div>
);
}
Regional Endpoints
Select the endpoint closest to your users for optimal latency:
| Region | API Endpoint | Description |
|---|---|---|
| Asia-Pacific | https://aiv.toothfairyai.com | Default, Australia-based |
| United States | https://aiv.us.toothfairyai.com | US-based servers |
| Europe | https://aiv.eu.toothfairyai.com | EU-based servers |
The LiveKit WebSocket URL (wss://toothfairyai-15k1aw45.livekit.cloud) uses a global edge network and automatically routes to the nearest server.
Best Practices
Error Handling
room.on(RoomEvent.Reconnecting, () => {
console.log('Reconnecting...');
// Show reconnection UI
});
room.on(RoomEvent.Reconnected, () => {
console.log('Reconnected');
// Hide reconnection UI
});
room.on(RoomEvent.ConnectionQualityChanged, (quality, participant) => {
if (quality === 'poor') {
console.warn('Poor connection quality');
// Notify user
}
});
Microphone Permissions
async function requestMicrophonePermission() {
try {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
stream.getTracks().forEach(track => track.stop());
return true;
} catch (error) {
console.error('Microphone permission denied:', error);
return false;
}
}
// Check before connecting
const hasPermission = await requestMicrophonePermission();
if (!hasPermission) {
alert('Microphone access is required for voice calls');
return;
}
Session Management
- Use the same
chat_idto continue previous conversations - Generate new
chat_idfor fresh conversations - Store
chat_idto allow users to resume sessions
Common Issues and Solutions
| Issue | Likely Cause | Solution |
|---|---|---|
| No audio from agent | Track not attached | Ensure audio element is added to DOM |
| Microphone not working | Permission denied | Request permissions before connecting |
| Connection timeout | Network issues | Implement retry with exponential backoff |
| Token expired | Session > 1 hour | Generate new token and reconnect |
| Echo/feedback | Audio routing | Use headphones or implement echo cancellation |
Success Checklist
- ✅ API key generated from Admin → API Integration
- ✅ Backend endpoint created to generate voice tokens
- ✅ LiveKit SDK installed and configured
- ✅ Microphone permissions handled
- ✅ Audio track subscription implemented
- ✅ Error handling and reconnection logic added
- ✅ Voice call tested end-to-end
- Test on multiple browsers (Chrome, Firefox, Safari)
- Implement a connection quality indicator for users
- Add visual feedback when the agent is speaking
- Consider adding push-to-talk for noisy environments
SIP Telephony Integration
ToothFairyAI supports SIP (Session Initiation Protocol) integration for connecting voice agents to telephony systems. This enables enterprise-grade phone integration through SIP trunks.
What You Can Do
| Feature | Description |
|---|---|
| SIP Inbound | Receive phone calls from any SIP trunk provider |
| SIP Outbound | Make phone calls from agents to external numbers |
| Multi-Provider Support | Works with Twilio, Vonage, Telnyx, and other SIP providers |
| Enterprise Telephony | Integrate with existing PBX and call center systems |
Integration Options
| Method | Use Case | Connection |
|---|---|---|
| WebRTC | Web/Mobile apps | Direct LiveKit SDK |
| SIP Inbound | Receive phone calls | SIP Trunk → LiveKit |
| SIP Outbound | Make phone calls | LiveKit → SIP Trunk |
| Twilio Webhook | Twilio phone numbers | Twilio → Webhook → Agent |
SIP API Endpoints
The Voice API provides the following SIP endpoints:
| Endpoint | Method | Description |
|---|---|---|
/sip/trunk | POST | Configure a SIP trunk connection |
/sip/dispatch | POST | Manage SIP call dispatch rules |
/sip/call | POST | Initiate an outbound SIP call |
Setting Up SIP Integration
Prerequisites
- Business or Enterprise subscription
- A SIP trunk provider (Twilio, Vonage, Telnyx, etc.)
- A configured voice agent
Configuration Steps
Configure SIP Trunk:
curl -X POST https://aiv.toothfairyai.com/sip/trunk \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"workspace_id": "your_workspace_id",
"trunk_name": "My SIP Trunk",
"provider": "twilio",
"outbound_number": "+1234567890"
}'Configure Your SIP Provider:
- Set the SIP trunk to route calls to your ToothFairyAI workspace
- Configure authentication (IP allowlist or credentials)
Dispatch Rules:
- Route incoming SIP calls to specific agents
- Set fallback agents for overflow handling
Outbound Calling:
curl -X POST https://aiv.toothfairyai.com/sip/call \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"workspace_id": "your_workspace_id",
"agent_id": "your_agent_id",
"to_number": "+19876543210",
"from_number": "+1234567890"
}'
SIP vs Twilio Webhook Integration
| Aspect | SIP Integration | Twilio Webhook |
|---|---|---|
| Setup Complexity | Higher (requires SIP trunk config) | Lower (webhook URL only) |
| Provider Flexibility | Any SIP provider | Twilio only |
| Outbound Calls | Full API control | Limited |
| Enterprise Features | Full PBX integration | Basic phone support |
| Cost | Varies by provider | Twilio pricing |
SIP integration is available for Business and Enterprise plans. The feature is stable for production use. Contact support for assistance with your specific SIP provider configuration.
- Use dedicated SIP trunks for voice agent traffic
- Configure appropriate codecs (G.711, G.722, Opus)
- Set up fallback routing for high availability
- Monitor call quality metrics from your SIP provider
Twilio Phone Integration
ToothFairyAI now offers native Twilio integration with mobile phones, enabling businesses to connect their existing phone numbers directly to ToothFairyAI voice agents through a seamless self-service setup.
What You Can Do
- Connect Any Phone Number - Use any phone number available in your Twilio account
- Self-Service Configuration - Set up without technical support intervention
- Agent Assignment - Map specific phone numbers to specific voice agents for targeted handling
- 24/7 Phone Support - Transform any mobile or landline number into an intelligent AI voice assistant
Setup Process
Prerequisites:
- A Twilio account with a purchased phone number
- A ToothFairyAI voice agent configured in your workspace
- Business or Enterprise subscription
Configure Twilio Webhook:
- In Twilio Console, go to Phone Numbers > Manage > Active numbers
- Select your phone number
- Under Voice Configuration, set the webhook URL:
https://api.toothfairyai.com/webhooks/twilio/voice/{WORKSPACE_ID} - Set HTTP Method to
POST
Assign Agent to Phone Number:
- In ToothFairyAI, go to Settings > Agents
- Select your voice agent
- Scroll to Agent Channels section
- Add your Twilio phone number with the assigned agent
Test the Integration:
- Call your Twilio phone number
- The assigned voice agent will answer and handle the conversation
- Monitor the call in ToothFairyAI's chat interface
You can assign different phone numbers to different voice agents. This allows you to route calls based on the number dialed—for example, sales calls to a sales agent and support calls to a support agent.
- Configure appropriate voice instructions for phone interactions
- Set up voicemail fallback behavior
- Monitor call logs in both Twilio and ToothFairyAI
- Test call quality before going live
API Reference
For complete API documentation, see the Voice API Reference and select "TF Voice" from the API dropdown.