Voice Conversations
Start a real-time audio conversation between your user and a Stellar agent. The SDK handles microphone capture, audio playback, and the WebSocket connection.
import { createStellarClient } from "@stellar-ai/agent-sdk";
const client = createStellarClient();
const conversation = await client.startConversation({
auth: {
strategy: "publicToken",
token: "<your-public-access-token>",
},
agentId: "<your-agent-id>",
});
Each client supports one active voice conversation at a time. To start a new one, end the current conversation with conversation.end() first, or you'll get a CONVERSATION_ALREADY_ACTIVE error.
In browser or WebView environments, request microphone access before starting:
await navigator.mediaDevices.getUserMedia({ audio: true });
If you're embedding the SDK inside a hybrid app or native shell (Capacitor, Cordova/Ionic, MAUI Blazor Hybrid, Modyo Native App Shell, Tauri, Electron, custom Android WebView or iOS WKWebView, etc.), see Hybrid apps and WebViews for how to grant the WebView mic access from the native side.
Events​
Subscribe to events using .on(). All event methods return the conversation instance for chaining.
conversation
.on("stateChanged", ({ state }) => {
console.log("State:", state);
})
.on("error", ({ error, code }) => {
console.error("Error:", code, error);
})
.on("started", ({ clientId }) => {
console.log("Started with client ID:", clientId);
})
.on("handover", ({ handoverType, phoneNumber, queueId, announcement }) => {
console.log("Handover:", handoverType);
})
.on("actionExecuted", ({ actionName, requestBody, responseBody }) => {
console.log("Action:", actionName);
});
| Event | Payload | Description |
|---|---|---|
stateChanged | { state } | Connection state changed (connecting, connected, disconnected, error) |
error | { error, code } | An error occurred |
started | { clientId, conversationId? } | Conversation started successfully |
handover | { handoverType, phoneNumber?, queueId?, announcement? } | Agent initiated a handover |
actionExecuted | { actionName, requestBody?, responseBody? } | Agent executed a tool or action |
Handover event​
The handover event fires when the agent initiates a transfer. It uses HandoverType.PHONE for phone transfers and HandoverType.QUEUE for queue-based routing:
import { HandoverType } from "@stellar-ai/agent-sdk";
conversation.on("handover", ({ handoverType, phoneNumber, queueId, announcement }) => {
if (handoverType === HandoverType.PHONE) {
// Transfer the call to phoneNumber
} else if (handoverType === HandoverType.QUEUE) {
// Route to the queue identified by queueId
}
});
Phone handovers include phoneNumber, queue handovers include queueId, and both may include an announcement.
Action executed event​
The actionExecuted event is the main way to handle Studio Client action executions in your app during a voice conversation. When the agent chooses a client_action, the SDK emits this event with the Studio action name and the arguments the agent supplied. Your app can listen for it and map it to local behavior like navigation, opening native UI, or updating client-side state while the conversation continues.
The same event also fires for other action types. In those cases, requestBody and responseBody let you inspect what happened after the action ran.
conversation.on("actionExecuted", ({ actionName, requestBody }) => {
const args = requestBody ? JSON.parse(requestBody) : {};
if (actionName === "navigate_to_screen") {
navigateToScreen(args.screen_name);
}
});
Methods​
end​
End the conversation and close the connection. After calling end, discard the instance.
await conversation.end();
mute and unmute​
Control the microphone. When muted, the SDK stops capturing audio but keeps the WebSocket connection open.
conversation.mute();
console.log(conversation.isMuted); // true
conversation.unmute();
console.log(conversation.isMuted); // false
isMuted​
A boolean property indicating whether the microphone is currently muted.
state​
The current connection state: connecting, connected, disconnected, or error.
console.log(conversation.state); // "connected"
on, off, and once​
on(event, handler)— subscribe to an event. Returns the instance for chaining.off(event, handler)— unsubscribe a handler.once(event, handler)— subscribe for a single occurrence, then auto-unsubscribe.
Next steps​
- Text chat — messaging-based conversations with streaming responses
- TypeScript SDK — installation, authentication, and shared configuration