> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zeroruntime.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# AI Voice Agent with React Native

> Connect a Zero Runtime voice AI agent to a React Native app so users can talk to it in real time using the Google Gemini Live API.

Zero Runtime lets you integrate an AI voice agent into your React Native app (Android/iOS) within minutes. The agent joins the same meeting room and interacts over voice using the Google Gemini Live API.

## Prerequisites

Before proceeding, ensure that your development environment meets the following requirements:

* A Zero Runtime account (create one from the [Zero Runtime Dashboard](https://app.zeroruntime.ai/)).
* Node.js and a working React Native environment (Android Studio and/or Xcode).
* Python 3.11+ installed on your device.
* A Google API key with Gemini Live API access.

<Note>
  You need a Zero Runtime account to generate a token and a Google API key for the Gemini Live API.
  Generate a token from the [Zero Runtime Dashboard](https://app.zeroruntime.ai/) and a Google API key from [Google AI Studio](https://aistudio.google.com/api-keys).
</Note>

## Project Structure

First, create an empty project using `mkdir folder_name` in your preferred location for the React Native frontend. Your final project structure should look like this:

<Tree>
  <Tree.Folder name="root" defaultOpen>
    <Tree.Folder name="android" />

    <Tree.Folder name="ios" />

    <Tree.File name="App.js" />

    <Tree.File name="constants.js" />

    <Tree.File name="index.js" />

    <Tree.File name="agent-react-native.py" />

    <Tree.File name=".env" />
  </Tree.Folder>
</Tree>

You will be working on the following files:

* `android/`: Contains the Android-specific project files.
* `ios/`: Contains the iOS-specific project files.
* `App.js`: The main React Native component, containing the UI and meeting logic.
* `constants.js`: Stores the token and meeting ID for the frontend.
* `index.js`: The entry point of the React Native application, where the SDK is registered.
* `agent-react-native.py`: The Python AI agent backend using the Google Gemini Live API.
* `.env`: Environment variables for the Python agent's API keys.

## 1. React Native Frontend

<Steps>
  <Step title="Step 1: Create the app and install the SDK" icon="download">
    Create a React Native app and install the React Native SDK:

    ```bash theme={null}
    npx react-native init videosdkAiAgentRN
    cd videosdkAiAgentRN

    # Install the SDK
    npm install "@videosdk.live/react-native-sdk"
    ```
  </Step>

  <Step title="Step 2: Configure the Android project" icon="android">
    Add the required permissions in the `AndroidManifest.xml` file.

    ```xml title="android/app/src/main/AndroidManifest.xml" theme={null}
    <manifest
      xmlns:android="http://schemas.android.com/apk/res/android"
    >
        <uses-permission android:name="android.permission.INTERNET" />
        <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
        <uses-permission
            android:name="android.permission.BLUETOOTH"
            android:maxSdkVersion="30" />
        <uses-permission
            android:name="android.permission.BLUETOOTH_ADMIN"
            android:maxSdkVersion="30" />

        <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

        <uses-permission android:name="android.permission.CAMERA" />
        <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
        <uses-permission android:name="android.permission.RECORD_AUDIO" />
        <uses-permission android:name="android.permission.WAKE_LOCK" />
    </manifest>
    ```

    Link the necessary native dependencies:

    ```java title="android/app/build.gradle" theme={null}
    dependencies {
     implementation project(':rnwebrtc')
    }
    ```

    ```gradle title="android/settings.gradle" theme={null}
    include ':rnwebrtc'
    project(':rnwebrtc').projectDir = new File(rootProject.projectDir, '../node_modules/@videosdk.live/react-native-webrtc/android')
    ```

    ```java title="MainApplication.kt" theme={null}
    import live.videosdk.rnwebrtc.WebRTCModulePackage

    class MainApplication : Application(), ReactApplication {
      override val reactNativeHost: ReactNativeHost =
          object : DefaultReactNativeHost(this) {
            override fun getPackages(): List<ReactPackage> {
              val packages = PackageList(this).packages.toMutableList()
              packages.add(WebRTCModulePackage())
              return packages
            }
            // ...
          }
    }
    ```

    ```java title="android/gradle.properties" theme={null}
    /* This one fixes a weird WebRTC runtime problem on some devices. */
    android.enableDexingArtifactTransform.desugaring=false
    ```

    Include the following line in your `proguard-rules.pro` file (optional, only if you are using Proguard):

    ```java title="android/app/proguard-rules.pro" theme={null}
    -keep class org.webrtc.** { *; }
    ```

    In your `build.gradle` file, update the minimum OS/SDK version to `23`:

    ```java title="android/build.gradle" theme={null}
    buildscript {
      ext {
          minSdkVersion = 23
      }
    }
    ```
  </Step>

  <Step title="Step 3: Configure the iOS project" icon="apple">
    <Note>
      Ensure that you are using CocoaPods version 1.10 or later. To update CocoaPods, reinstall the gem using `sudo gem install cocoapods`.
    </Note>

    Change the path of `react-native-webrtc` in your Podfile:

    ```sh title="ios/Podfile" theme={null}
    pod 'react-native-webrtc', :path => '../node_modules/@videosdk.live/react-native-webrtc'
    ```

    Update the platform field in the Podfile to iOS 12.0 or above, since `react-native-webrtc` doesn't support earlier versions: `platform :ios, '12.0'`.

    Install the pods:

    ```sh theme={null}
    pod install
    ```

    Declare the camera and microphone permissions in `Info.plist` (located at `project folder/ios/projectname/info.plist`):

    ```html title="ios/MyApp/Info.plist" theme={null}
    <key>NSCameraUsageDescription</key>
    <string>Camera permission description</string>
    <key>NSMicrophoneUsageDescription</key>
    <string>Microphone permission description</string>
    ```
  </Step>

  <Step title="Step 4: Register the service and configure credentials" icon="key">
    Register the SDK services in your root `index.js` file for the initialization service.

    ```js title="index.js" theme={null}
    import { AppRegistry } from "react-native";
    import App from "./App";
    import { name as appName } from "./app.json";
    import { register } from "@videosdk.live/react-native-sdk";

    register();

    AppRegistry.registerComponent(appName, () => App);
    ```

    Create a meeting room using the Zero Runtime API:

    ```bash theme={null}
    curl -X POST https://api.videosdk.live/v2/rooms \
      -H "Authorization: YOUR_JWT_TOKEN_HERE" \
      -H "Content-Type: application/json"
    ```

    Copy the `roomId` from the response and create a `constants.js` file to store your token and meeting ID.

    ```js title="constants.js" theme={null}
    export const token = "YOUR_ZRT_AUTH_TOKEN";
    export const meetingId = "YOUR_MEETING_ID";
    export const name = "User Name";
    ```
  </Step>

  <Step title="Step 5: Build the UI and wire up MeetingProvider" icon="window">
    ```js title="App.js" theme={null}
    import React from 'react';
    import {
      SafeAreaView,
      TouchableOpacity,
      Text,
      View,
      FlatList,
    } from 'react-native';
    import {
      MeetingProvider,
      useMeeting,
    } from '@videosdk.live/react-native-sdk';
    import { meetingId, token, name } from './constants';

    const Button = ({ onPress, buttonText, backgroundColor }) => {
      return (
        <TouchableOpacity
          onPress={onPress}
          style={{
            backgroundColor: backgroundColor,
            justifyContent: 'center',
            alignItems: 'center',
            padding: 12,
            borderRadius: 4,
          }}>
          <Text style={{ color: 'white', fontSize: 12 }}>{buttonText}</Text>
        </TouchableOpacity>
      );
    };

    function ControlsContainer({ join, leave, toggleMic }) {
      return (
        <View
          style={{
            padding: 24,
            flexDirection: 'row',
            justifyContent: 'space-between',
          }}>
          <Button
            onPress={() => {
              join();
            }}
            buttonText={'Join'}
            backgroundColor={'#1178F8'}
          />
          <Button
            onPress={() => {
              toggleMic();
            }}
            buttonText={'Toggle Mic'}
            backgroundColor={'#1178F8'}
          />
          <Button
            onPress={() => {
              leave();
            }}
            buttonText={'Leave'}
            backgroundColor={'#FF0000'}
          />
        </View>
      );
    }

    function ParticipantView({ participantDisplayName }) {
      return (
        <View
          style={{
            backgroundColor: 'grey',
            height: 300,
            justifyContent: 'center',
            alignItems: 'center',
            marginVertical: 8,
            marginHorizontal: 8,
          }}>
          <Text style={{ fontSize: 16 }}>Participant: {participantDisplayName}</Text>
        </View>
      );
    }

    function ParticipantList({ participants }) {
      return participants.length > 0 ? (
        <FlatList
          data={participants}
          renderItem={({ item }) => {
            return <ParticipantView participantDisplayName={item.displayName} />;
          }}
        />
      ) : (
        <View
          style={{
            flex: 1,
            backgroundColor: '#F6F6FF',
            justifyContent: 'center',
            alignItems: 'center',
          }}>
          <Text style={{ fontSize: 20 }}>Press Join button to enter meeting.</Text>
        </View>
      );
    }

    function MeetingView() {
      const { join, leave, toggleMic, participants, meetingId } = useMeeting({});

      const participantsList = [...participants.values()].map(participant => ({
        displayName: participant.displayName,
      }));

      return (
        <View style={{ flex: 1 }}>
          {meetingId ? (
            <Text style={{ fontSize: 18, padding: 12 }}>Meeting Id : {meetingId}</Text>
          ) : null}
          <ParticipantList participants={participantsList} />
          <ControlsContainer
            join={join}
            leave={leave}
            toggleMic={toggleMic}
          />
        </View>
      );
    }

    export default function App() {
      if (!meetingId || !token) {
        return (
          <SafeAreaView style={{ flex: 1, backgroundColor: '#F6F6FF' }}>
            <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
              <Text style={{ fontSize: 20, textAlign: 'center' }}>
                Please add a valid Meeting ID and Token in the `constants.js` file.
              </Text>
            </View>
          </SafeAreaView>
        );
      }

      return (
        <SafeAreaView style={{ flex: 1, backgroundColor: '#F6F6FF' }}>
          <MeetingProvider
            config={{
              meetingId,
              micEnabled: true,
              webcamEnabled: false,
              name,
            }}
            token={token}>
            <MeetingView />
          </MeetingProvider>
        </SafeAreaView>
      );
    }
    ```
  </Step>
</Steps>

## 2. Python AI Agent

<Steps>
  <Step title="Step 1: Setup the project" icon="download">
    Use Python 3.11 or higher, then install the Zero Runtime Agents SDK. Create and activate a virtual environment, then install `zrt` and `python-dotenv`. Every provider plugin ships with `zrt`.

    <CodeGroup>
      ```bash uv theme={null}
      uv venv --python 3.11
      source .venv/bin/activate     # macOS/Linux
      .\.venv\Scripts\activate      # Windows
      ```

      ```bash pip theme={null}
      python3.11 -m venv venv
      source venv/bin/activate      # macOS/Linux
      .\venv\Scripts\activate       # Windows
      ```
    </CodeGroup>

    <Tip>
      New to uv? See the [uv install guide](https://docs.astral.sh/uv/getting-started/installation/).
    </Tip>

    <CodeGroup>
      ```bash uv theme={null}
      uv pip install zrt python-dotenv
      ```

      ```bash pip theme={null}
      pip install zrt python-dotenv
      ```
    </CodeGroup>
  </Step>

  <Step title="Step 2: Configure environment variables" icon="key">
    Create a `.env` file in your project root to store your keys and token securely for the Python agent:

    ```shell title=".env" theme={null}
    # Google API Key for the Gemini Live API
    GOOGLE_API_KEY=your_google_api_key_here

    # Option A: pre-generated ZRT auth token
    ZRT_AUTH_TOKEN=your_zrt_auth_token_here

    # Option B: SDK auto-generates the token (omit ZRT_AUTH_TOKEN)
    # ZRT_API_KEY=your_zrt_api_key
    # ZRT_SECRET_KEY=your_zrt_secret_key
    ```
  </Step>

  <Step title="Step 3: Create the AI agent backend" icon="robot">
    Create the Python AI agent (`agent-react-native.py`) that joins the same meeting room and interacts with users through voice.

    ```python title="agent-react-native.py" theme={null}
    import os
    import zrt
    from zrt import Agent, Pipeline, Room
    from zrt.plugins import GeminiRealtime, GeminiLiveConfig
    from zrt.inference import AICousticsDenoise
    from dotenv import load_dotenv

    load_dotenv()

    AGENT_ID = "react-native-agent"

    pipeline = Pipeline(
        llm=GeminiRealtime(
            api_key=os.getenv("GOOGLE_API_KEY"),  # optional when GOOGLE_API_KEY is in .env
            config=GeminiLiveConfig(model="gemini-3.1-flash-live-preview", voice="Leda", response_modalities=["AUDIO"]),
        ),
        denoise=AICousticsDenoise(model_id="quail-vf-2.2-l-16khz"),
    )


    class MyVoiceAgent(Agent):
        def __init__(self) -> None:
            super().__init__(
                name="MyVoiceAgent",
                agent_id=AGENT_ID,
                instructions="You are a high-energy game-show host guiding the caller to guess a secret number from 1 to 100 to win 1,000,000$.",
                pipeline=pipeline,
            )

        async def on_enter(self) -> None:
            await self.session.say(
                "Welcome to the Zero Runtime AI Agent game show! I'm your host, and we're about to play for 1,000,000$. Are you ready to play?"
            )

        async def on_exit(self) -> None:
            await self.session.say("Goodbye!")


    def invoke_agent() -> None:
        # Join the same room the frontend created (its meeting ID).
        zrt.invoke(AGENT_ID, room=Room(room_id="YOUR_MEETING_ID"))


    if __name__ == "__main__":
        zrt.serve(MyVoiceAgent, on_ready=invoke_agent)
    ```
  </Step>
</Steps>

## 3. Run the Application

<Steps>
  <Step title="Step 1: Start the React Native app" icon="mobile">
    ```bash theme={null}
    npm install

    # Android
    npm run android

    # iOS (macOS only)
    cd ios && pod install && cd ..
    npm run ios
    ```
  </Step>

  <Step title="Step 2: Run the AI agent" icon="terminal">
    Open a new terminal, activate your virtual environment, and run the Python agent. It reads `GOOGLE_API_KEY` from the `.env` file.

    <CodeGroup>
      ```bash uv theme={null}
      uv run python agent-react-native.py
      ```

      ```bash pip theme={null}
      python agent-react-native.py
      ```
    </CodeGroup>
  </Step>

  <Step title="Step 3: Connect and interact" icon="microphone">
    1. **Join the meeting from the app:**
       * Tap the **Join** button in the app.
       * Allow microphone permissions when prompted.

    2. **Agent connection:**
       * Once you join, the Python agent detects your participation.
       * You should see "Participant joined" in the terminal.
       * The AI agent greets you and initiates the game.

    3. **Start playing:**
       * The agent guides you through a number guessing game (1–100).
       * Use your microphone to interact with the AI host.
  </Step>
</Steps>

## Troubleshooting

<AccordionGroup>
  <Accordion title="'Waiting for participant...' but no connection" icon="plug">
    * Ensure both the React Native app and the agent are running.
    * Check that the meeting ID matches in `constants.js` and `agent-react-native.py`.
    * Verify your Zero Runtime token is valid.
  </Accordion>

  <Accordion title="Audio not working" icon="volume-high">
    * Check device/simulator permissions for microphone access.
    * Ensure your Google API key has Gemini Live API access enabled.
  </Accordion>

  <Accordion title="Agent not responding" icon="robot">
    * Verify your `GOOGLE_API_KEY` is set in the `.env` file.
    * Check that the Gemini Live API is enabled in your Google Cloud Console.
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Quickstart Example" icon="github" href="https://github.com/ZeroRuntimeAI/zrt-python-sdk-examples">
    Complete working example with source code.
  </Card>

  <Card title="Build Your First Voice Agent" icon="microphone" href="/quickstarts/build-your-first-voice-agent">
    Learn the Cascade and Realtime pipelines in depth.
  </Card>
</CardGroup>
