Voice SDK
Add AI voice conversations to any app — web, mobile, or server-side. One package, zero third-party dependencies visible to developers.
How it works
Available SDKs
Web (React/JS)
stable@converse/sdk
npm i @converse/sdkPython (Server)
stableconverse-ai
pip install converse-aiReact Native
source@converse/react-native
packages/voice-react-nativeiOS (Swift)
sourceConverseSDK
packages/voice-iosAndroid (Kotlin)
sourceio.converse:converse-sdk
packages/voice-androidFlutter (Dart)
sourceconverse_sdk
packages/voice-flutterQuick Start (React)
Add voice to any React app in under 10 lines:
npm install @converse/sdk
import { useConverseVoice, ConverseOrb } from '@converse/sdk/components';
function TalkToAI() {
const { voiceState, agentState, connect, disconnect } = useConverseVoice({
publicKey: 'pk_live_...',
agentId: 'your-agent-id',
});
return (
<ConverseOrb
state={agentState}
size="lg"
color="#7c3aed"
onClick={voiceState === 'idle' ? connect : disconnect}
/>
);
}
// That's it. Mic, speaker, status — all handled internally.How It Works
// Your code:
1. connect() called
// SDK internally:
2. POST /v2/calls/web → gets session token
3. WebRTC connection to voice.converse.axllabs.in
4. Microphone enabled, speaker output attached
5. AI agent joins automatically
// You receive:
6. agentState updates: connecting → listening → speaking → ...
7. transcript array updates in real-time
State Machine
Two state values drive your entire UI:
voiceState (call lifecycle)
idleNot connected. Ready to start.connectingSession being established.activeCall is live. Audio flowing.endedCall ended gracefully.errorConnection failed. Check error string.agentState (conversation state)
idleNo active conversation.connectingAgent is joining the call.listeningAgent is listening to you speak.thinkingAgent is processing your request.speakingAgent is speaking to you.Visualizers
Six pre-built animated visualizers, all driven by agentState. Swap freely — same props, different visual style.
import {
ConverseOrb, // Glowing orb with ripples — great for call buttons
ConverseBar, // Vertical bars — the classic voice indicator
ConverseWave, // SVG sine wave — horizontal layouts
ConverseAura, // Blob morph + conic gradient — premium feel
ConverseRadial, // Polar bar ring — canvas-based, 60fps
ConverseGrid, // Pulsing dot grid — subtle ambient indicator
} from '@converse/sdk/components';
// All take the same props:
<ConverseOrb state={agentState} size="lg" color="#7c3aed" onClick={toggle} />useConverseVoice Options
publicKeystringPublishable key (pk_live_... or pk_test_...)agentIdstringAgent to connect voice totokenProvider?() => PromiseAlternative: fetch token from your backendbaseUrl?stringOverride API base URLonStateChange?(state) => voidCalled when voiceState changesonConnect?(session) => voidCalled when session is establishedonDisconnect?() => voidCalled when call endsonTranscript?(entry) => voidCalled on each new transcript entryonError?(err) => voidCalled on errorReturn Values
voiceStateVoiceStateCall lifecycle stateagentStateAgentStateAgent conversation state (drives visualizers)sessionVoiceSession | null{ callId, connected } when activetranscriptTranscriptEntry[]Running transcript arrayconnect()Promise<void>Start a voice calldisconnect()voidEnd the callerrorstring | nullError message if voiceState is errorQuick Start (React Native)
npm install @converse/react-native
import { useConverseVoice, ConverseVoiceView } from '@converse/react-native';
function VoiceScreen() {
const { voiceState, agentState, connect, disconnect } = useConverseVoice({
publicKey: 'pk_live_...',
agentId: 'your-agent-id',
});
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<ConverseVoiceView agentState={agentState} size={120} color="#6366F1" />
<Button
title={voiceState === 'active' ? 'End Call' : 'Start Call'}
onPress={voiceState === 'active' ? disconnect : connect}
/>
</View>
);
}Quick Start (iOS Swift)
Add the Swift package dependency:
// Package.swift
dependencies: [
.package(url: "https://github.com/converse-ai/voice-ios.git", from: "0.1.0"),
]import ConverseSDK
import SwiftUI
struct VoiceView: View {
@StateObject private var voice = ConverseVoice(
config: ConverseVoiceConfig(publicKey: "pk_live_...")
)
var body: some View {
VStack(spacing: 24) {
ConverseVoiceView(voice: voice, size: 120, color: .indigo)
ConverseVoiceButton(voice: voice, agentId: "your-agent-id")
}
}
}Quick Start (Android Kotlin)
// build.gradle.kts
dependencies {
implementation("io.converse:converse-sdk:0.1.0")
}import io.converse.sdk.ConverseVoice
import io.converse.sdk.ConverseVoiceView
class VoiceActivity : ComponentActivity() {
private val voice = ConverseVoice(publicKey = "pk_live_...")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
ConverseVoiceView(agentState = voice.agentState)
Button(onClick = {
if (voice.voiceState == VoiceState.ACTIVE) voice.disconnect()
else voice.connect("your-agent-id")
}) {
Text(if (voice.voiceState == VoiceState.ACTIVE) "End" else "Start")
}
}
}
}
}Quick Start (Flutter)
# pubspec.yaml dependencies: converse_sdk: ^0.1.0
import 'package:converse_sdk/converse_sdk.dart';
class VoiceScreen extends StatefulWidget {
@override
_VoiceScreenState createState() => _VoiceScreenState();
}
class _VoiceScreenState extends State<VoiceScreen> {
final controller = ConverseVoiceController(publicKey: 'pk_live_...');
@override
Widget build(BuildContext context) {
return StreamBuilder<AgentState>(
stream: controller.agentStateStream,
builder: (context, snapshot) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ConverseVoiceWidget(controller: controller, size: 120),
ElevatedButton(
onPressed: () => controller.voiceState == VoiceState.active
? controller.disconnect()
: controller.connect('your-agent-id'),
child: Text(controller.voiceState == VoiceState.active ? 'End' : 'Start'),
),
],
);
},
);
}
}Production Setup (Token Endpoint)
Never expose API keys in browser code. Create a server-side endpoint:
// app/api/converse-session/route.ts (Next.js)
import { ConverseClient } from '@converse/sdk';
const converse = new ConverseClient({
apiKey: process.env.CONVERSE_API_KEY!, // Server-only
});
export async function POST(req: Request) {
const { agentId } = await req.json();
// Validate agentId belongs to your org
const session = await converse.calls.createWebCall(agentId);
return Response.json({ call_id: session.call_id });
}Python SDK (Server-Side Calls)
from converse import ConverseClient
client = ConverseClient(api_key="sk_live_...")
# Outbound voice call
call = client.calls.create(
agent_id="agent_xxx",
to="+919876543210",
metadata={"task": "Confirm appointment"},
)
print(f"Call: {call['id']} Status: {call['status']}")
# List recent calls
calls = client.calls.list(status="completed", limit="10")
for c in calls["data"]:
print(c["id"], c["duration_ms"])