Guide for integrating the Sarvam Client SDK into React Native applications on iOS and Android. For shared concepts like configuration, proxy setup, error handling, and API reference, see the Overview.

Installation

npm install sarvam-conv-ai-sdk
import { ConversationAgent } from "sarvam-conv-ai-sdk/react-native";
For voice conversations, you need to implement your own audio interface using the AsyncAudioInterface contract. See Custom Audio Interface below.

Quick Start

Text Conversation

import {
  ConversationAgent,
  InteractionType,
} from "sarvam-conv-ai-sdk/react-native";

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.CHAT,
    input_sample_rate: 16000,
    output_sample_rate: 16000,
  },
  textCallback: async (msg) => console.log("Agent:", msg.text),
  platform: "react-native",
});

await agent.start();
await agent.waitForConnect(10);
await agent.sendText("Hello!");

Voice Conversation

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();
await agent.waitForConnect(10);

Custom Audio Interface

React Native does not have a built-in browser audio API, so you must provide your own audio interface that implements the AsyncAudioInterface contract:
import {
  AsyncAudioInterface,
  AudioData,
} from "sarvam-conv-ai-sdk/react-native";

class MyRNAudioInterface implements AsyncAudioInterface {
  /**
   * Start capturing audio from the microphone.
   * Call inputCallback with PCM audio data (16-bit, mono, at configured sample rate).
   */
  async start(
    inputCallback: (audioData: AudioData, frameCount: number) => Promise<void>,
  ): Promise<void> {
    // Initialize your audio recording library
    // Call inputCallback with Uint8Array of 16-bit PCM data
  }

  /**
   * Play audio through the speaker.
   * @param audio - Uint8Array of 16-bit PCM mono audio
   * @param sampleRate - Sample rate of the audio (e.g., 16000 or 22050)
   */
  async output(audio: Uint8Array, sampleRate?: number): Promise<void> {
    // Play the audio using your playback library
  }

  /**
   * Interrupt and stop any ongoing playback immediately.
   * Called when the user interrupts the agent.
   */
  interrupt(): void {
    // Stop all queued/playing audio
  }

  /**
   * Stop recording and clean up resources.
   */
  async stop(): Promise<void> {
    // Release microphone and audio resources
  }
}


Authentication

Since cookies aren’t available in React Native, use customHeaders to pass authentication tokens when using a proxy server:
const agent = new ConversationAgent({
  apiKey: "", // Empty - proxy handles Sarvam auth
  baseUrl: "https://api.yourapp.com/sarvam/",
  customHeaders: {
    Authorization: "Bearer eyJhbGciOiJIUzI1NiIs...",
    "X-User-Id": "user-123",
  },
  config: {
    // ... your config
  },
  platform: "react-native",
});

Best Practices

Resource Cleanup

Always stop the agent when the component unmounts:
useEffect(() => {
  return () => {
    agentRef.current?.stop().catch(console.error);
  };
}, []);

Handle App Lifecycle

Stop or pause the agent when the app goes to the background to avoid holding audio resources:
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();
}, []);

Connection Timeout

Always specify a timeout when waiting for connection:
const connected = await agent.waitForConnect(10);
if (!connected) {
  // Handle connection failure — show an error or retry
}

Troubleshooting

Microphone Permission Denied

Request microphone permissions before starting a voice conversation. For Expo projects:
import { Audio } from "expo-av";

const { granted } = await Audio.requestPermissionsAsync();
if (!granted) {
  // Show dialog asking user to enable permission in Settings
}
For bare React Native, use react-native-permissions:
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
}

Connection Timeout

  • Check internet connectivity
  • Verify your API key is valid
  • Ensure org_id, workspace_id, and app_id are correct
  • Make sure the app has a committed version

No Audio Output

  • Check device volume and silent mode switch (iOS)
  • Verify your AsyncAudioInterface.output() implementation is correctly writing to the audio output
  • Ensure the sample rate in your audio playback matches output_sample_rate

WebSocket Disconnects on Background

React Native suspends WebSocket connections when the app is backgrounded. Since reconnection is not supported (each WebSocket URL is single-use), you must stop the agent and create a new instance:
const subscription = AppState.addEventListener("change", async (nextState) => {
  if (nextState === "active" && !agentRef.current?.isConnected()) {
    await agentRef.current?.stop();
    // Create a new ConversationAgent instance and call start()
  }
});