Add a voice AI agent to your app
Voxera handles WebRTC transport, streaming speech-to-text, your language model and text-to-speech as one pipeline. Build your conversation experience with SDKs for web and mobile.
No credit card. 60 free voice minutes on the Free plan.
How it works
A staged pipeline you can actually see inside
Four stages, each provider-neutral. Use the bundled OpenAI defaults, or point any stage at your own HTTP API.
WebRTC microphone audio
The caller connects over WebRTC through mediasoup. Voxera runs the SFU, the ICE negotiation and the TURN relay, so audio works from a browser, a phone or a locked-down corporate network.
Streaming transcription
Audio streams to a transcription API as the caller speaks. Partial transcripts arrive continuously rather than after the caller stops, which is what makes the response feel immediate.
Text-only model call
The transcript goes to a text model in streaming mode, with your system prompt, tools and session metadata. There is no direct audio-to-model path — every stage is inspectable, loggable and swappable.
Text-to-speech, injected live
Generated speech is converted to 48 kHz PCM frames and injected back into the WebRTC stream as the model is still producing text.
Barge-in is the ability to interrupt a voice agent mid-sentence and have it stop speaking. When the caller starts talking, Voxera halts playback and discards the partial response, so the agent never talks over the person it is meant to be listening to.
Platform
Everything around the model, already built
Manage audio transport, keys, quotas, transcripts and billing alongside your voice agents.
One protocol, six SDKs
Web, React, React Native, iOS, Android and Flutter share a single wire protocol. Configure an agent once and it behaves identically everywhere.
Tool calling
Give an agent HTTP tools and it can look up an order or book a slot mid-conversation.
Keys built for clients
Secret keys stay on your server and mint short-lived session tokens. Publishable keys are safe to ship inside an app bundle.
Usage you can trust
Sessions hold a lease and settle billable seconds in one transaction on disconnect, so a dropped call is never billed twice or lost.
Full transcripts
Every turn is persisted with timing, so you can audit, evaluate and replay conversations rather than guess at them.
Bring your own providers
Point transcription, model or speech at your own HTTP endpoint. Available on Growth and Scale.
Use cases
What people build with it
The same pipeline — a prompt, a set of HTTP tools and a voice — pointed at six different problems.
Customer support that resolves, not routes
Answer account questions on the phone or in-app, and hand off with context when it matters.
Voice ordering and reservations
Take an order over the phone, confirm it against live inventory, and write it into your system.
Scheduling and reminders
Book, move and confirm appointments against a real calendar, without a phone tree.
An in-app voice assistant
Add a talk button to an app you already ship, on every platform at once.
Hands-free field and warehouse work
Log work, look up procedures and capture readings while both hands stay busy.
Tutoring and conversation practice
Low-latency back-and-forth where interruption is the point, not an edge case.
SDKs
Six SDKs, one wire protocol
Configure the agent once. Every SDK speaks the same protocol, so behaviour does not drift between your web app and your phone app.
WebTypeScript
@voxera/sdk-webBrowser SDK over Socket.IO, WebRTC and mediasoup-client. Exposes the VoxeraClient class with a typed event map for session state, transcripts and audio.
import { VoxeraClient } from "@voxera/sdk-web";
const client = new VoxeraClient({
appKey: "vx_pk_live_...", // Publishable key; restrict its allowed origins.
serverUrl: "https://rtc.voxera-voice.com",
agentId: "your-agent-id", // Publish the agent in the dashboard first.
// Let the SDK attach and play the agent's audio itself.
autoPlayRemoteAudio: true,
});
client.on("message", (message) => {
console.log(message.role, message.fullText ?? message.content);
});
await client.connect();
client.startConversation();ReactTypeScript
@voxera/sdk-reactThe browser SDK behind a hook. useVoxera owns the client lifecycle and its subscriptions, merges streamed chunks into one message per reply, and carries "use client" for the Next.js App Router.
"use client";
import { useVoxera } from "@voxera/sdk-react";
export function VoiceButton() {
const call = useVoxera();
return (
<section aria-label="Voice assistant">
<button
type="button"
disabled={call.isBusy}
onClick={() => {
if (call.isActive) {
void call.leave();
} else {
void call.start({
appKey: "vx_pk_live_...", // Publishable key, never vx_sk_*.
serverUrl: "https://rtc.voxera-voice.com",
agentId: "your-agent-id",
});
}
}}
>
{call.isBusy ? "Connecting…" : call.isActive ? "End call" : "Talk to the agent"}
</button>
<p role="status">{call.connectionStatus}</p>
{call.error ? <p role="alert">{call.error}</p> : null}
{call.autoplayBlocked ? (
<button type="button" onClick={() => void call.resumeAudio()}>
Tap to hear the agent
</button>
) : null}
{call.messages.map((message) => (
<p key={message.id}><strong>{message.role}:</strong> {message.content}</p>
))}
</section>
);
}React NativeTypeScript
@voxera/sdk-react-nativeiOS and Android from one codebase. The useVoxera hook owns the client lifecycle, merges streamed chunks into one message per reply, and handles the Android microphone permission for you.
import { Button, Text, View } from "react-native";
import { useVoxera } from "@voxera/sdk-react-native";
export function VoiceScreen() {
const { start, leave, isActive, messages, speakingState } = useVoxera();
return (
<View>
<Text>{speakingState === "ai" ? "Agent speaking" : "Listening"}</Text>
<Button
title={isActive ? "End call" : "Start call"}
onPress={() =>
isActive
? leave()
: start({
appKey: "vx_pk_live_...",
serverUrl: "https://rtc.voxera-voice.com",
agentId: "your-agent-id",
userId: "user-123",
})
}
/>
{messages.map((message) => (
<Text key={message.id}>{message.content}</Text>
))}
</View>
);
}iOSSwift
VoxeraSDKNative Swift SDK exposing VoxeraClient and a SwiftUI-ready VoxeraViewModel, with AVAudioSession handling built in.
import SwiftUI
import VoxeraSDK
struct VoiceView: View {
// The view model takes its configuration up front — there is no
// separate configure() step.
@StateObject private var voxera = VoxeraViewModel(
config: VoxeraConfig(
appKey: publishableKey,
serverUrl: "https://rtc.voxera-voice.com",
userId: "user-123"
)
)
var body: some View {
VStack(spacing: 12) {
Text(voxera.isConnected ? "Connected" : "Idle")
ForEach(voxera.conversationMessages) { message in
Text(message.content)
}
Button("Start call") {
voxera.connect()
voxera.startConversation()
}
}
}
}AndroidKotlin
Public release in progressNative Kotlin SDK with coroutine-based session control and Flow events.
import com.voxera.sdk.ConversationMessage
import com.voxera.sdk.VoxeraClient
import com.voxera.sdk.VoxeraConfig
import com.voxera.sdk.VoxeraListener
// Configuration and the listener are passed to the constructor;
// connect() itself takes no arguments.
private val voxera = VoxeraClient(
context,
VoxeraConfig(
appKey = PUBLISHABLE_KEY,
serverUrl = "https://rtc.voxera-voice.com",
userId = "user-123",
listener = object : VoxeraListener {
override fun onMessage(message: ConversationMessage) {
binding.transcript.append(message.content)
}
},
),
)
voxera.connect()
voxera.startConversation()FlutterDart
Public release in progressDart SDK over a Pigeon-generated platform channel, sharing the same native iOS and Android transports.
import 'package:voxera_flutter/voxera_flutter.dart';
final voxera = VoxeraClient(const VoxeraConfig(
appKey: publishableKey,
serverUrl: 'https://rtc.voxera-voice.com',
userId: 'user-123',
));
// Messages arrive as typed events, not as a list.
final sub = voxera.on('message').listen((event) {
setState(() => _transcript.add(event.data['content'] as String));
});
await voxera.connect();
await voxera.startConversation();Quickstart
Build a voice agent in five steps
No infrastructure to provision and no media servers to run. The longest part is deciding what the agent should say.
1.Create your account
Sign up with an email and password. You get a workspace, and the Free plan's 60 voice minutes, without a card.
2.Create an agent
Give it a name, write what it should do in plain language, and pick a voice. Saving publishes version one — there is no separate deploy step.
3.Add toolsoptional
Define function schemas so the agent can look up an order or book a slot mid-conversation. Skip this and it can still talk.
4.Issue an app key
Publishable keys ship inside a browser or app bundle and are locked to your origins. Secret keys stay on your server. The plaintext is shown once.
5.Connect from your app
Install the SDK for your platform, pass the key and the agent id, and call connect. The prompt, voice and tools all come from the agent you published.
import { VoxeraClient } from "@voxera/sdk-web";
const client = new VoxeraClient({
appKey: "vx_pk_live_...", // publishable key, safe in a bundle
serverUrl: "https://rtc.voxera-voice.com",
agentId: "your-agent-id", // the agent you published in step 2
});
await client.connect();
client.startConversation();That is the whole client. Everything the agent knows — its prompt, its voice, its tools — lives on the published version, so changing behaviour does not mean shipping a new build.
FAQ
Common questions
What is Voxera?
How is a voice minute counted?
Can I bring my own speech and language providers?
What happens when I run out of voice minutes?
Does Voxera support interrupting the agent mid-sentence?
Which platforms have SDKs?
Hear it before you build on it
The demo is the real pipeline, not a recording. Talk to it, interrupt it, and decide from there.