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
API Reference
For complete API documentation, see the Voice API Reference and select "TF Voice" from the API dropdown.