> For clean Markdown of any page, append `.md` to the page URL. > For a complete documentation index, see https://docs.sarvam.ai/llms.txt. > For full documentation content in one file, see https://docs.sarvam.ai/llms-full.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.sarvam.ai/_mcp/server. # React Native For real-time voice on iOS and Android, the TypeScript SDK (`sarvam-conv-ai-sdk/react-native`) uses the same `ConversationAgent` client as [Web](/conversations/deploy/sdks/web). React Native has no built-in browser audio API, so you must pass your own `AsyncAudioInterface` for microphone capture and playback. ## Install ```bash npm install sarvam-conv-ai-sdk ``` ```typescript import { ConversationAgent } from "sarvam-conv-ai-sdk/react-native"; ``` ## Start a voice session ```typescript import { ConversationAgent, InteractionType, } from "sarvam-conv-ai-sdk/react-native"; const audioInterface = new MyRNAudioInterface(); const agent = new ConversationAgent({ apiKey: "your_api_key", config: { org_id: "your_org_id", workspace_id: "your_workspace_id", app_id: "your_app_id", user_identifier: "user123", user_identifier_type: "custom", interaction_type: InteractionType.CALL, input_sample_rate: 16000, output_sample_rate: 16000, }, audioInterface, platform: "react-native", transcriptCallback: async (msg) => { console.log(`${msg.role}: ${msg.content}`); }, }); await agent.start(); const connected = await agent.waitForConnect(10); if (!connected) { // Show an error or retry } ``` Set `platform: "react-native"` and pass an `audioInterface` that implements `AsyncAudioInterface` (see [Custom audio interface](#custom-audio-interface) below). ## InteractionConfig | Field | Required | Description | | ----------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `user_identifier_type` | Yes | `'custom'`, `'email'`, `'phone_number'`, or `'unknown'` | | `user_identifier` | Yes | The identifier value; also what you search by in [Log Analyser](/conversations/monitor/agent-analytics/log-analyser) | | `org_id` | Yes | Your organization ID | | `workspace_id` | Yes | Your workspace ID | | `app_id` | Yes | The agent to connect to | | `interaction_type` | Yes | `InteractionType.CALL` for a voice session | | `input_sample_rate` | Yes | `8000` or `16000` Hz | | `output_sample_rate` | Yes | `16000` or `22050` Hz | | `version` | No | Pins a specific committed agent version. If omitted, the SDK uses the latest committed version, and the connection fails if the agent has no committed version | | `agent_variables` | No | Seed [agent variables](/conversations/build/variables-personalization) at session start | | `initial_language_name` | No | Starting language; must be one of the agent's allowed languages | | `initial_state_name` | No | Starting state, if the agent uses [states](/conversations/build/states-conversation-flow) | | `initial_bot_message` | No | First message from the agent | ## Callbacks Pass any of these to `ConversationAgent` to react to what happens during the call: | Callback | Fires with | Use it for | | -------------------- | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------ | | `transcriptCallback` | transcript message (`role`: `Role.USER` or `Role.BOT`, `content`) | A live transcript of both sides of the call | | `audioCallback` | audio chunk | Raw agent audio, if you're handling playback yourself | | `audioLevelCallback` | output level | Real-time level for a speaking indicator | | `eventCallback` | session event | Session events such as connect, barge-in, and end | | `stateCallback` | `AgentState` | Drive UI from `IDLE`, `CONNECTING`, `CONNECTED`, `LISTENING`, `SPEAKING`, or `ERROR` | | `startCallback` | — | Session start | | `endCallback` | — | Session end and cleanup | ## Custom audio interface Implement `AsyncAudioInterface` and feed 16-bit PCM mono at the configured sample rate: ```typescript import { AsyncAudioInterface, AudioData, } from "sarvam-conv-ai-sdk/react-native"; class MyRNAudioInterface implements AsyncAudioInterface { async start( inputCallback: (audioData: AudioData, frameCount: number) => Promise, ): Promise { // Initialize your audio recording library // Call inputCallback with a Uint8Array of 16-bit PCM data } async output(audio: Uint8Array, sampleRate?: number): Promise { // Play 16-bit PCM mono through the speaker } interrupt(): void { // Stop all queued and playing audio (user barge-in) } async stop(): Promise { // Release microphone and audio resources } } ``` Recommended libraries: * [react-native-audio-api](https://github.com/software-mansion/react-native-audio-api) — low-level audio API * [expo-av](https://docs.expo.dev/versions/latest/sdk/av/) — Expo projects * [react-native-live-audio-stream](https://github.com/niceDev0908/react-native-live-audio-stream) — real-time streaming ## Session methods | Method | Description | | -------------------------------------- | ----------------------------------------------------------------------------------------------------------- | | `await agent.start()` | Fetch a signed WebSocket URL and connect | | `await agent.waitForConnect(timeout?)` | Wait until connected; `timeout` is in seconds (returns `false` on timeout) | | `await agent.sendAudio(data)` | Send a raw PCM audio chunk | | `agent.isConnected()` | Current connection status | | `agent.getInteractionId()` | The current interaction (call) ID, once connected | | `agent.getState()` | Current `AgentState` | | `agent.mute()` / `agent.unmute()` | Mute or unmute the microphone without disconnecting. While muted, the SDK sends silence so VAD stays stable | | `agent.isMuted()` | Current mute status | | `await agent.waitForDisconnect()` | Wait until the session ends | | `await agent.stop()` | Close the connection and clean up | > **Note** > > Call `await agent.stop()` on unmount so the WebSocket and audio interface are cleaned up. Reconnection is not supported: each WebSocket URL is single-use. If the connection drops, call `stop()` and create a new `ConversationAgent`. Stop or recreate the agent when the app backgrounds. React Native suspends WebSockets in the background, and you cannot reuse the old URL: ```typescript import { useEffect, useRef } from "react"; import { AppState, AppStateStatus } from "react-native"; const appState = useRef(AppState.currentState); useEffect(() => { const subscription = AppState.addEventListener( "change", (nextState: AppStateStatus) => { if (appState.current.match(/active/) && nextState === "background") { agentRef.current?.stop(); } appState.current = nextState; }, ); return () => { subscription.remove(); agentRef.current?.stop().catch(console.error); }; }, []); ``` ## Troubleshooting **Microphone permission denied.** Request the microphone before starting a voice session. Expo: ```typescript import { Audio } from "expo-av"; const { granted } = await Audio.requestPermissionsAsync(); if (!granted) { // Ask the user to enable the permission in Settings } ``` Bare React Native, with [react-native-permissions](https://github.com/zoontek/react-native-permissions): ```typescript import { request, PERMISSIONS, RESULTS } from "react-native-permissions"; import { Platform } from "react-native"; const result = await request( Platform.OS === "ios" ? PERMISSIONS.IOS.MICROPHONE : PERMISSIONS.ANDROID.RECORD_AUDIO, ); if (result !== RESULTS.GRANTED) { // Handle denied permission } ``` **No audio output.** Check device volume and the iOS silent switch. Confirm `output()` writes to the speaker at `output_sample_rate`. **Connection timeout.** Check connectivity, API key, `org_id` / `workspace_id` / `app_id`, and that the agent has a committed version. > **Warning** > > Never embed your API key in the app binary. Point `baseUrl` at a backend you control, leave `apiKey` empty, and pass your app auth in `customHeaders` (cookies are not available in React Native): ```typescript const agent = new ConversationAgent({ apiKey: "", baseUrl: "https://api.yourapp.com/sarvam/", customHeaders: { Authorization: "Bearer eyJhbGciOiJIUzI1NiIs...", "X-User-Id": "user-123", }, platform: "react-native", audioInterface: new MyRNAudioInterface(), config: { // ... }, }); ```