Converse Logo
Developers

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

The Converse Voice SDK handles the complete audio pipeline internally: session creation, WebRTC transport, microphone capture, speaker output, and real-time state events. You write UI code — we handle infrastructure.

Available SDKs

Web (React/JS)

stable

@converse/sdk

npm i @converse/sdk

Python (Server)

stable

converse-ai

pip install converse-ai

React Native

source

@converse/react-native

packages/voice-react-native

iOS (Swift)

source

ConverseSDK

packages/voice-ios

Android (Kotlin)

source

io.converse:converse-sdk

packages/voice-android

Flutter (Dart)

source

converse_sdk

packages/voice-flutter

Quick Start (React)

Add voice to any React app in under 10 lines:

bash
npm install @converse/sdk
tsx
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.

tsx
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 to
tokenProvider?() => PromiseAlternative: fetch token from your backend
baseUrl?stringOverride API base URL
onStateChange?(state) => voidCalled when voiceState changes
onConnect?(session) => voidCalled when session is established
onDisconnect?() => voidCalled when call ends
onTranscript?(entry) => voidCalled on each new transcript entry
onError?(err) => voidCalled on error

Return Values

voiceStateVoiceStateCall lifecycle state
agentStateAgentStateAgent conversation state (drives visualizers)
sessionVoiceSession | null{ callId, connected } when active
transcriptTranscriptEntry[]Running transcript array
connect()Promise<void>Start a voice call
disconnect()voidEnd the call
errorstring | nullError message if voiceState is error

Quick Start (React Native)

bash
npm install @converse/react-native
tsx
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:

swift
// Package.swift
dependencies: [
    .package(url: "https://github.com/converse-ai/voice-ios.git", from: "0.1.0"),
]
swift
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)

kotlin
// build.gradle.kts
dependencies {
    implementation("io.converse:converse-sdk:0.1.0")
}
kotlin
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)

yaml
# pubspec.yaml
dependencies:
  converse_sdk: ^0.1.0
dart
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:

typescript
// 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)

python
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"])

API Key Security

Server-side SDKs run with your full API key. Never bundle them into client-side code. For browser apps, use the token endpoint pattern above.