Voice Setup
Enable real-time voice conversations with your companion. Powered by ElevenLabs ConvAI.
How it works
When you configure an ElevenLabs ConvAI agent on a companion, the embed widget automatically enables voice mode. Visitors can switch between text and voice in the chat UI.
- Real-time voice via WebSocket (low latency)
- Built-in turn-taking and interruption handling
- Your API keys stay server-side — embeds use short-lived signed URLs
Setup
Create an ElevenLabs ConvAI agent
Go to elevenlabs.io/app/conversational-ai and create a new agent.
Configure:
- System prompt — the same prompt as your Kyndred companion, or a voice-optimized version
- Voice — pick a voice from the ElevenLabs library
- Language — set to your target language (English, Romanian, etc.)
Copy the agent ID
In the ElevenLabs dashboard, copy the agent ID (looks like agent_xxxxxxxxxxxx).
Paste it into your companion
Go to kyndred.dev/app/companions, open your companion, scroll to the Voice section, paste the agent ID, and save.
Test it
Reload the embed. You should see a Talk button on the select screen. Click it, grant mic permission, and start talking.
Mobile WebView requirements
If you're embedding in a mobile app WebView (Flutter, React Native), the native app must grant microphone permission. Browser WebViews won't ask the user automatically.
iOS
Add to Info.plist:
<key>NSMicrophoneUsageDescription</key>
<string>This app uses your microphone for voice conversations.</string>Android
Add to AndroidManifest.xml:
<uses-permission android:name="android.permission.RECORD_AUDIO" />Plus handle onPermissionRequest in your WebView to grant the resource.
Custom integration (advanced)
If you're not using our embed widget and want to build a custom voice UI, fetch a signed URL from our API and pass it to the @11labs/client SDK.
import { Conversation } from '@11labs/client';
// 1. Get a signed URL from Kyndred
const res = await fetch(
'https://kyndred.dev/api/embed/YOUR_TOKEN/voice-token',
{ method: 'POST' }
);
const { agent_id } = await res.json();
// 2. Start ConvAI session
const conv = await Conversation.startSession({
agentId: agent_id,
connectionType: 'websocket',
onConnect: () => console.log('connected'),
onMessage: ({ message, source }) => console.log(source, message),
onDisconnect: () => console.log('disconnected'),
});
// Later: end the session
conv.endSession();See the SDK Reference for our provider-agnostic wrapper that lets you swap voice backends without changing your UI code.