Skip to content

About

Brings Deepgram's capabilities to React Native applications, with a focus on performance and ease of use.

Topics

Resources

Code of conduct

Contributing

Stars

6 stars

Watchers

0 watching

Forks

Latest commit

 

History

129 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

react-native-deepgram

npm version License: MIT

The complete client for Deepgram's voice AI platform on React Native and Expo — real-time transcription, conversational voice agents, text-to-speech, and text intelligence in a single dependency, with hardware echo cancellation and background-audio support wired up for you.

yarn add react-native-deepgram

At a glance

🗣️ Voice Agent Full-duplex agents over Deepgram's WebSocket. Hardware AEC on iOS (VPIO) and Android (VOICE_COMMUNICATION + MODE_IN_COMMUNICATION). Function calling, prompt updates, latency telemetry.
🎙️ Speech-to-Text Live PCM streaming over WebSocket on STT v1 or Flux v2 (flux-general-en). File transcription with summarisation, topics, intents, entities. Optionally record the mic to a WAV file while you stream.
🔊 Text-to-Speech One-shot HTTP synthesis or low-latency WebSocket streaming for Aura and Flux TTS voices, with Flush / Clear / Close controls and Flux barge-in (interrupt()).
🧠 Text Intelligence Summaries, topics, intents, sentiment over text or URLs.
🛠️ Management API Typed wrapper for projects, models, keys, usage, balances, members, invitations, scopes, purchases, and temporary tokens.
⚙️ Expo plugin One-line install — handles permissions, background-audio modes, and Android foreground service automatically.
🧰 Resilient by default Audio interruptions, route changes, headphone unplugging, mediaserverd resets, audio-focus loss, and queue overflow are all handled — plus opt-in WebSocket auto-reconnect with exponential backoff.
🆕 Modern iOS APIs AVAudioApplication.requestRecordPermission and AllowBluetoothHFP on iOS 17+, with safe fallbacks.

Table of contents

  1. Quick start (5 minutes)
  2. Requirements
  3. Installation
  4. Migrating from 2.x
  5. Core concepts
  6. Audio session, AEC & background
  7. Voice Agent
  8. Speech-to-Text
  9. Text-to-Speech
  10. Text Intelligence
  11. Management API
  12. Expo config plugin reference
  13. Common recipes
  14. Troubleshooting
  15. Example app
  16. Roadmap
  17. Contributing
  18. License

Quick start (5 minutes)

1. Install

yarn add react-native-deepgram

2. Configure the Expo plugin

// app.config.js (or app.json under "plugins")
module.exports = {
  expo: {
    plugins: [
      [
        'react-native-deepgram',
        {
          microphonePermission:
            'Allow $(PRODUCT_NAME) to access your microphone.',
          backgroundAudio: true, // default — keeps audio alive in the background; set false to skip the Android foreground service + iOS background mode
        },
      ],
    ],
  },
};
npx expo prebuild
npx expo run:ios   # or expo run:android

Bare React Native? Skip the plugin, run cd ios && pod install. The package autolinks via react-native.config.js.

3. Configure the SDK once at startup

// App.tsx
import { configure } from 'react-native-deepgram';

configure({ apiKey: process.env.EXPO_PUBLIC_DEEPGRAM_API_KEY! });

Never ship raw API keys. Use EXPO_PUBLIC_* env vars, secrets storage, or a backend proxy that mints scoped keys.

4. Stream microphone audio to Deepgram (live STT)

import { useDeepgramSpeechToText } from 'react-native-deepgram';
import { Button, Text, View } from 'react-native';

export function LiveCaption() {
  const {
    startListening,
    stopListening,
    state,         // { status: 'idle' | 'listening' | 'error' | ... }
    transcript,    // accumulated final transcript
    interimTranscript, // current partial
  } = useDeepgramSpeechToText({
    trackState: true,
    trackTranscript: true,
    live: { punctuate: true, interimResults: true },
  });

  return (
    <View>
      <Text>{transcript} {interimTranscript}</Text>
      <Button title="Start" onPress={() => startListening()} />
      <Button title="Stop" onPress={stopListening} />
      {state?.error && <Text style={{ color: 'red' }}>{String(state.error)}</Text>}
    </View>
  );
}

5. Talk to a Voice Agent (full-duplex with AEC)

import { useDeepgramVoiceAgent } from 'react-native-deepgram';

const agent = useDeepgramVoiceAgent({
  trackState: true,
  trackConversation: true,
  defaultSettings: {
    audio: {
      input:  { encoding: 'linear16', sample_rate: 16_000 }, // mic capture rate
      output: { encoding: 'linear16', sample_rate: 24_000, container: 'none' },
    },
    agent: {
      greeting: 'Hi! What can I help you with?',
      listen: { provider: { type: 'deepgram', model: 'nova-3', language: 'en' } },
      think:  { provider: { type: 'open_ai', model: 'gpt-4o' }, prompt: 'You are a helpful assistant.' },
      speak:  { provider: { type: 'deepgram', version: 'v2', model: 'flux-kit-en' } },
    },
  },
});

await agent.connect();          // requests mic, opens WS, starts streaming
agent.injectUserMessage('Hi'); // optional: prompt the agent with text
agent.disconnect();             // tears everything down

That's a working, low-latency conversational agent with hardware echo cancellation. Open the example app to try the full surface area.


Requirements

Minimum
Node 18
React 19.0
React Native 0.80 (New Architecture)
iOS 15.1 (React Native's minimum)
Android API 24 (Android 7.0)
Xcode 16.1 (React Native 0.80's minimum)

The native module is a TurboModule and requires the New Architecture (the default since React Native 0.76). The legacy architecture is no longer supported as of 3.0.


Installation

yarn add react-native-deepgram
# or
npm install react-native-deepgram

Bare React Native

cd ios && pod install

The package is autolinked through react-native.config.js — no manual MainApplication edits required.

Add android.permission.RECORD_AUDIO to your AndroidManifest.xml and NSMicrophoneUsageDescription to your Info.plist if you use any microphone feature. If you also want audio to keep running in the background, see Background audio for the opt-in foreground-service entries.

Expo (managed or bare)

Add the plugin to your config:

// app.config.js
module.exports = {
  expo: {
    plugins: [
      [
        'react-native-deepgram',
        {
          microphonePermission:
            'Allow $(PRODUCT_NAME) to access your microphone.',
          // backgroundAudio: true, // default — see plugin docs below
        },
      ],
    ],
  },
};

Then prebuild and run:

npx expo prebuild
npx expo run:ios   # or expo run:android

Already running an existing development build? Re-run expo prebuild (and pod install on iOS) so the new permissions, background modes, and Android foreground service are picked up.


Migrating from 2.x

3.0 drops the legacy architecture and the APIs that were deprecated in 2.x. After upgrading, rebuild the native app (npx expo prebuild --clean or pod install) — a JS-only reload is not enough.

Platform

  • React Native 0.80+, React 19 and the New Architecture are required.
  • The package is ESM-only; deep imports from react-native-deepgram/lib/commonjs no longer exist.
  • If you listened to native device events directly (DeepgramAudioPCM / AudioChunk, DeepgramAudioLevel, DeepgramRouteChange, DeepgramInterruption) or called NativeModules.Deepgram / MicPermission, use the typed Deepgram export instead: Deepgram.onAudioPCM, onAudioLevel, onAudioRouteChange, onAudioInterruption and Deepgram.requestRecordPermission(). addAudioRouteChangeListener and addInterruptionListener are unchanged.
  • Deepgram.stopPlayer() now returns a promise.

Speech-to-Text

2.x 3.0
diarize: true diarizeModel: 'latest'

Text-to-Speech

2.x 3.0
options: { model, encoding, sampleRate, speed, mipOptOut } options: { http: { … }, stream: { … } } (set per transport)
options: { bitRate, container, tag, callback, callbackMethod } options: { http: { … } }
options.format / http.format http.container
sendMessage({ type: 'Text', text }) sendMessage({ type: 'Speak', text }) or sendText(text)
DeepgramTextToSpeechEncoding DeepgramTextToSpeechHttpEncoding / DeepgramTextToSpeechStreamEncoding

synthesize() only plays raw linear16 output (the default) and respects autoPlayAudio; compressed formats are returned without playback.

Voice Agent

2.x 3.0
agent.language agent.listen.provider.language (Deepgram voices take the language from the model name)
audio.input.sample_rate above 16000 Capped at 16000, the microphone capture rate
injectAgentMessage(message, 'queue') Unchanged, or injectAgentMessage(message, { behavior, dynamic })

See the changelog for the full list, including the new Flux, Configure and Voice Agent APIs.


Core concepts

A few ideas you only have to learn once.

1. Configure once, use anywhere

import { configure } from 'react-native-deepgram';
configure({ apiKey: process.env.EXPO_PUBLIC_DEEPGRAM_API_KEY! });

configure is global — call it at the entry point (e.g. App.tsx). Every hook reads from this config. The Management API additionally needs a key with management scopes.

Never ship raw API keys. Use EXPO_PUBLIC_* env vars, secrets storage, or a backend proxy that mints scoped keys.

Custom endpoints (regional, Dedicated & self-hosted)

Point the SDK at a non-default Deepgram deployment by passing base URLs to configure. Every override is optional and falls back to the public Deepgram endpoints.

configure({
  apiKey: process.env.EXPO_PUBLIC_DEEPGRAM_API_KEY!,
  baseUrl: 'https://api.beta.deepgram.com/v1', // REST (must include the version segment)
  baseWss: 'wss://api.beta.deepgram.com/v1',   // live STT + streaming TTS sockets
  agentUrl: 'wss://agent.deepgram.com/v1/agent/converse', // Voice Agent socket
});
Option Applies to Default
baseUrl REST calls (file STT, TTS, Text Intelligence, Management) https://api.deepgram.com/v1
baseWss Live STT + streaming TTS sockets wss://api.deepgram.com/v1
agentUrl Voice Agent socket wss://agent.deepgram.com/v1/agent/converse

baseUrl/baseWss must include the version segment (/v1). Flux v2 endpoints are derived automatically by swapping the trailing /v1 for /v2. See Deepgram's custom endpoints reference.

Ephemeral / scoped tokens (keep your API key off-device)

Instead of shipping a long-lived apiKey, you can hand configure a getToken provider that returns a short-lived Deepgram token minted by your backend (which proxies Deepgram's /v1/auth/grant). The SDK caches the token, refreshes it before it expires, and de-duplicates concurrent refreshes — so a burst of requests never triggers multiple grants.

configure({
  // No raw apiKey on the device. `getToken` takes precedence when provided.
  getToken: async () => {
    const res = await fetch('https://your-backend.example.com/deepgram-token');
    const { access_token, expires_in } = await res.json();
    return { token: access_token, expiresInSeconds: expires_in };
  },
});
Field Type Description
token string The short-lived token. Sent as Authorization: Bearer <token>.
expiresInSeconds number (optional) TTL hint used to refresh ahead of expiry. Defaults to 30.
  • When both getToken and apiKey are set, getToken wins; the API key is the fallback.
  • The token only needs to be valid at connection time — live STT / Voice Agent sockets keep running after it expires.
  • Exception: the Management API (useDeepgramManagement) cannot authenticate with temporary tokens, so it always uses the configured apiKey.

2. Hooks return reactive state — opt in

Every hook supports track* flags that turn on a reactive return value. Without them, the hook stays event-driven (callbacks only) so it never causes unnecessary re-renders.

const { transcript, state } = useDeepgramSpeechToText({
  trackTranscript: true,  // exposes `transcript` + `interimTranscript`
  trackState: true,       // exposes `state.status`, `state.error`
});
Hook Tracking flags
useDeepgramVoiceAgent trackState, trackConversation, trackAgentStatus
useDeepgramSpeechToText trackState, trackTranscript
useDeepgramTextToSpeech trackState
useDeepgramTextIntelligence trackState

3. Audio session is automatic

You don't manage AVAudioSession, AudioManager, or focus. The native module activates the right session category, switches to VoiceChat / MODE_IN_COMMUNICATION when echo cancellation is enabled, requests/abandons audio focus, starts/stops a foreground service on Android, and rebuilds after mediaserverd resets on iOS.

For tuning details, see Audio session, AEC & background.

4. Hooks at a glance

Hook Purpose Endpoint
useDeepgramVoiceAgent Conversational agents (full-duplex) wss://agent.deepgram.com/v1/agent/converse
useDeepgramSpeechToText Live STT + file transcription wss://api.deepgram.com/v1/listen (v1) / /v2/listen (Flux)
useDeepgramTextToSpeech TTS via REST or streaming https://api.deepgram.com/v1/speak & wss://api.deepgram.com/v1/speak
useDeepgramTextIntelligence Summaries, topics, intents, sentiment https://api.deepgram.com/v1/read
useDeepgramManagement Projects / keys / usage REST API https://api.deepgram.com/v1

5. Typed errors

Every error the hooks surface — mic-permission denials, native start/stop failures, fetch and WebSocket errors — is normalized to a DeepgramError with a typed code, so you can branch on a stable enum instead of string-matching messages. The original failure is preserved on cause.

import { DeepgramError, useDeepgramSpeechToText } from 'react-native-deepgram';

useDeepgramSpeechToText({
  onError: (err) => {
    if (err instanceof DeepgramError && err.code === 'permission_denied') {
      promptForMicAccess();
    }
  },
});

The code is one of the native reject codes (or 'unknown' when it can't be mapped):

permission_denied · init_failed · start_error · stop_error · audio_start_error · audio_stop_error · stop_player_error · invalid_data · playback_error


Audio session, AEC & background

This section covers what the native module does for you so you can debug or tune behaviour with confidence.

Hardware echo cancellation (full-duplex)

When you build a Voice Agent (or any duplex flow where Deepgram is speaking and the mic is open), set enableVoiceProcessing: true:

import { Deepgram } from 'react-native-deepgram';

await Deepgram.startRecording({ enableVoiceProcessing: true });

useDeepgramVoiceAgent opts into this automatically. What this gives you:

Platform Behaviour
iOS Capture is routed through AVAudioEngine.inputNode with setVoiceProcessingEnabled:YES on both input and output, and the audio session uses AVAudioSessionModeVoiceChat. This engages Apple's Voice-Processing I/O Audio Unit — the only way to get hardware AEC on iOS.
Android Capture uses MediaRecorder.AudioSource.VOICE_COMMUNICATION and the system is set to MODE_IN_COMMUNICATION so the platform's hardware AEC engages with the active playback signal as its reference. The previous mode is restored on stopRecording.

For pure speech-to-text usage you generally don't want this — leave the option off (the default). STT-only paths use VOICE_RECOGNITION on Android and a pure AudioQueue capture on iOS so Deepgram receives the rawest possible signal.

Background audio

Background audio is opt-in: the library itself ships with no foreground-service permissions, so apps that only use foreground audio (or the pure-REST Text Intelligence / Management APIs) never have to justify foreground-service types in the Google Play Console.

Leave backgroundAudio: true on the Expo plugin (it's the default) to keep playback and capture alive when the user leaves the app:

  • iOS — UIBackgroundModes: ["audio"] is added to your Info.plist.
  • Android — the FOREGROUND_SERVICE, FOREGROUND_SERVICE_MICROPHONE, and FOREGROUND_SERVICE_MEDIA_PLAYBACK permissions are added to your manifest, and the bundled DeepgramAudioService is given foregroundServiceType="microphone|mediaPlayback". The service is started/stopped automatically whenever recording or playback is active.

On bare React Native, add the equivalent entries to your AndroidManifest.xml yourself:

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />

<application>
  <!-- Merges the foreground-service type onto the library's service -->
  <service
    android:name="com.deepgram.DeepgramAudioService"
    android:foregroundServiceType="microphone|mediaPlayback" />
</application>

(If your app is playback-only — TTS but no mic in the background — you can omit FOREGROUND_SERVICE_MICROPHONE and use android:foregroundServiceType="mediaPlayback"; the service degrades gracefully.)

Things to know:

  • Play Console declaration. Apps targeting Android 14+ that declare foreground-service types must describe each type's use (and link a demo video) under App content → Foreground service permissions in the Play Console. For this package: TYPE_MICROPHONE → “Background Audio Access” (live transcription / voice agent continues while backgrounded), TYPE_MEDIA_PLAYBACK → “Media Playback” (TTS/agent audio continues while backgrounded). If you can't justify them, set backgroundAudio: false (or omit the manifest entries) — then no declaration is needed.

  • App Store privacy. The pod ships a PrivacyInfo.xcprivacy privacy manifest declaring audio-data collection (App Functionality, not linked to identity, no tracking) and its required-reason API usage — Xcode aggregates it into your app's privacy report automatically. Remember to also declare your own use of the captured audio (e.g. Deepgram processing/retention) in App Store Connect.

  • Customizing the notification. By default the Android keep-alive notification uses your app's name and launcher icon, and tapping it opens the app. Brand it via the Expo plugin's androidNotification option, or on bare React Native with <meta-data> entries in your <application> element:

    <application>
      <meta-data android:name="com.deepgram.notification.TITLE" android:value="Transcribing…" />
      <meta-data android:name="com.deepgram.notification.TEXT" android:value="Live captions are running" />
      <meta-data android:name="com.deepgram.notification.CHANNEL_NAME" android:value="Live audio" />
      <meta-data android:name="com.deepgram.notification.ICON" android:value="ic_stat_audio" />
    </application>

    Each entry is optional. Values can also reference string resources for localization (android:resource="@string/..." instead of android:value); ICON names a drawable/mipmap resource and safely falls back to the launcher icon if missing.

  • Graceful degradation. If the permissions or the service's foregroundServiceType (it must include mediaPlayback) aren't declared, everything still works while the app is in the foreground; the native module simply skips the keep-alive service instead of crashing. Other libraries declaring FOREGROUND_SERVICE don't opt you in.

Built-in resilience

The native modules handle the awkward edge cases for you:

  • Audio interruptions — phone calls, Siri, system alerts pause + resume cleanly on iOS.
  • Route changes — unplugging headphones or disconnecting Bluetooth pauses playback (instead of suddenly blasting through the speaker), matching Apple's AVAudioSessionRouteChangeReasonOldDeviceUnavailable guidance and Android's ACTION_AUDIO_BECOMING_NOISY broadcast.
  • Audio focus loss (Android) — transient losses pause playback; permanent losses tear down recording + playback so you don't fight other apps for the mic.
  • mediaserverd resets (iOS) — if iOS restarts its audio service mid-session, the audio engine is rebuilt from scratch on the next call.
  • Bluetooth on iOS 17+ — uses AllowBluetoothHFP when available, with a deprecation-safe fallback for older toolchains.
  • iOS 17+ permission API — Deepgram.requestRecordPermission() calls AVAudioApplication.requestRecordPermissionWithCompletionHandler when available, falling back to AVAudioSession on older OS versions.
  • Bounded playback queue (Android) — if JS feeds audio faster than the device can play it, the oldest chunks are dropped at ~1.5 MB so you never OOM mid-call.

Interruption events

All of the pausing/resuming above happens automatically — but your UI can observe it. Subscribe to interruption events to show “paused — on a call” states or to restart a session after Android tears it down on permanent focus loss:

import { addInterruptionListener } from 'react-native-deepgram';

const sub = addInterruptionListener((e) => {
  if (e.type === 'began') {
    // capture/playback paused by the system
    // e.reason: 'phoneCall' | 'focusLoss' | 'routeChange' | 'unknown'
  } else if (e.type === 'ended') {
    // interruption over; e.shouldResume === true means audio already resumed
  } else {
    // e.type === 'stopped' — Android permanent focus loss tore the session down;
    // call startListening()/connect() again if you want to continue
  }
});
// later: sub.remove();

The same events are available as an onInterruption callback on useDeepgramSpeechToText, useDeepgramVoiceAgent, and useDeepgramTextToSpeech. While a live STT or agent session is active the hooks also react on your behalf: they keep the Deepgram socket warm with KeepAlive frames for as long as the interruption holds the microphone (otherwise Deepgram closes the idle socket after ~10 s with NET-0001), resume normal streaming when it ends, and end the session gracefully (onEnd/onClose, no error) when Android tears it down on permanent focus loss.

Audio output routing

Steer playback between the loudspeaker, the earpiece and a connected Bluetooth headset, and observe route changes made outside your app (Control Center, plugging in headphones, …):

import {
  setAudioRoute,
  getAudioRoute,
  addAudioRouteChangeListener,
} from 'react-native-deepgram';

await setAudioRoute('speaker'); // 'speaker' | 'earpiece' | 'bluetooth' | 'auto'

const active = await getAudioRoute(); // 'speaker' | 'earpiece' | 'bluetooth' | 'wired'

const sub = addAudioRouteChangeListener((route) => {
  console.log('now playing through', route);
});
// later: sub.remove();

Things to know:

  • Best-effort semantics. The OS always has the final say: a wired headset wins over everything, and bluetooth only engages once a headset is actually connected (the request is remembered and applied when one appears). Invalid route names reject with invalid_data; OS-level failures reject with playback_error.
  • Call any time. You can set a route before or during a session. Set it before connect()/startListening() and it's applied when the audio session spins up.
  • Bluetooth is opt-in during full-duplex. In echo-cancelled (enableVoiceProcessing) sessions the module doesn't auto-route to Bluetooth — HFP's loopback defeats hardware AEC — so the agent stays on the built-in mic/speaker until you explicitly request 'bluetooth'.
  • External switches are adopted, not fought. If the user reroutes audio from Control Center or the system UI, the module accepts the new route (and tells you via the listener) instead of forcing your last request back.
  • Android permissions — MODIFY_AUDIO_SETTINGS and BLUETOOTH (API ≤ 30) are merged in from the library manifest automatically.

Voice Agent (useDeepgramVoiceAgent)

useDeepgramVoiceAgent connects to wss://agent.deepgram.com/v1/agent/converse, captures microphone audio, and optionally auto-plays the agent's streamed responses. It wraps the full Voice Agent messaging surface so you can react to conversation updates, function calls, warnings, and raw PCM audio.

🔊 Hardware AEC enabled by default. The hook calls startRecording({ enableVoiceProcessing: true }), so iOS routes capture through Apple's VPIO Audio Unit and Android switches to VOICE_COMMUNICATION + MODE_IN_COMMUNICATION. The result: the agent never hears itself speak, even on speakerphone. (Note: VPIO is not available in the iOS Simulator — test on a physical device.)

Quick start

const {
  connect,
  disconnect,
  state, // { connectionState, error, warning }
  agentStatus, // { thinking, latency }
  conversation, // Array<{ role, content }>
  injectUserMessage,
  sendFunctionCallResponse,
  updatePrompt,
} = useDeepgramVoiceAgent({
  trackState: true, // Enable reactive state tracking
  trackConversation: true, // Enable conversation history tracking
  trackAgentStatus: true, // Enable agent status tracking
  autoPlayAudio: true, // Automatically play agent audio
  defaultSettings: {
    audio: {
      input: { encoding: 'linear16', sample_rate: 16_000 }, // mic capture rate
      output: { encoding: 'linear16', sample_rate: 24_000, container: 'none' },
    },
    agent: {
      greeting: 'Hello! How can I help you today?',
      listen: {
        provider: {
          type: 'deepgram',
          model: 'nova-3',
          language: 'en',
          smart_format: true,
        },
      },
      think: {
        provider: { type: 'open_ai', model: 'gpt-4o', temperature: 0.7 },
        prompt: 'You are a helpful voice concierge.',
      },
      speak: {
        // Flux TTS (v2); use version 'v1' with an aura-* voice for Aura.
        provider: { type: 'deepgram', version: 'v2', model: 'flux-kit-en' },
      },
    },
    tags: ['demo'],
  },
  onConversationText: (msg) => {
    console.log(`${msg.role}: ${msg.content}`);
  },
  onAgentThinking: (msg) => console.log('thinking:', msg.content),
  onAgentAudioDone: () => console.log('Agent finished speaking'),
  onServerError: (err) => console.error('Agent error', err.description),
});

const begin = async () => {
  try {
    await connect();
  } catch (err) {
    console.error('Failed to start agent', err);
  }
};

const askQuestion = () => {
  injectUserMessage("What's the weather like?");
};

const provideTooling = () => {
  sendFunctionCallResponse({
    id: 'func_12345',
    name: 'get_weather',
    content: JSON.stringify({ temperature: 72, condition: 'sunny' }),
    client_side: true,
  });
};

const rePrompt = () => {
  updatePrompt('You are now a helpful travel assistant.');
};

return (
  <>
    <Text>Status: {state.connectionState}</Text>
    <Button title="Start agent" onPress={begin} />
    <Button title="Ask" onPress={askQuestion} />
    <Button title="Send tool output" onPress={provideTooling} />
    <Button title="Update prompt" onPress={rePrompt} />
    <Button title="Stop" onPress={disconnect} />
  </>
);

💬 The hook requests mic permissions, streams PCM to Deepgram, and surfaces the agent's replies as text so nothing plays back into the microphone.

API reference (Voice Agent)

Signature

const {
  // Connection
  connect, disconnect, isConnected,
  // Mic control
  mute, unmute, isMuted,
  // Messaging
  sendMessage, sendSettings, sendMedia, sendKeepAlive,
  injectUserMessage, injectAgentMessage, forceEndTurn, updatePrompt,
  updateListen, updateThink, updateSpeak, updateThinkLive,
  // Function calls
  sendFunctionCallResponse,
  // Reactive state (opt-in via track* flags)
  state, conversation, agentStatus, clearConversation,
} = useDeepgramVoiceAgent(props);

Hook props

Configuration

Prop Type Default Description
endpoint string wss://agent.deepgram.com/v1/agent/converse WebSocket endpoint used for the agent conversation.
defaultSettings DeepgramVoiceAgentSettings – Base Settings payload sent on connect; merge per-call overrides via connect(override).
autoStartMicrophone boolean true Automatically requests mic access and starts streaming PCM.
autoPlayAudio boolean true Plays received audio using the native player.
bargeIn boolean false Flush the agent's queued playback when the server reports UserStartedSpeaking, so the agent audibly stops when the user talks over it. Only flushes while agent audio is actually playing and the mic is not muted; the player stays usable for the next agent turn. Requires autoPlayAudio. Barge-in depends on hardware echo cancellation — test on a physical device (simulators have no VPIO, so the agent's own audio can trigger the VAD).
downsampleFactor number heuristic Manually override the downsample ratio applied to captured audio.
reconnect DeepgramReconnectOptions { enabled: false } Auto-reconnect config for the agent socket. The stored Settings payload is re-sent on every successful reconnect.

Reactive tracking flags (opt-in — extra renders only when set)

Prop Default Enables
trackState false state return — connection status, error, warning.
trackConversation false conversation return — running history of { role, content }.
trackAgentStatus false agentStatus return — thinking flag and latency metrics.
trackStats false stats return — session telemetry, updated at most once per second.

Callbacks

Lifecycle — connect/disconnect/error
Callback Signature Fired when
onBeforeConnect () => void connect is called, before mic prompt or socket open.
onConnect () => void Socket opens and the initial settings payload is delivered.
onClose (event?: any) => void Socket closes (manual disconnect or remote).
onError (error: unknown) => void Unexpected client-side error (mic, playback, socket send).
onReconnecting (attempt: number) => void A reconnect attempt begins (1-based attempt number). Requires reconnect.enabled.
onReconnected () => void The socket reconnects and the stored settings are re-sent.
onBargeIn () => void A bargeIn flush actually happened (agent audio was cut short by the user speaking).
onServerError (message: DeepgramVoiceAgentErrorMessage) => void API reports a structured error (description + code).
onWarning (message: DeepgramVoiceAgentWarningMessage) => void Non-fatal warning (e.g. degraded audio quality).
Conversation — text, audio, agent state
Callback Signature Fired when
onWelcome (message: DeepgramVoiceAgentWelcomeMessage) => void Agent returns the initial Welcome envelope.
onSettingsApplied (message: DeepgramVoiceAgentSettingsAppliedMessage) => void Settings are acknowledged by the agent.
onConversationText (message: DeepgramVoiceAgentConversationTextMessage) => void Transcript update (role + content) arrives.
onUserStartedSpeaking (message: DeepgramVoiceAgentUserStartedSpeakingMessage) => void Server-side VAD detects the user speaking.
onAgentThinking (message: DeepgramVoiceAgentAgentThinkingMessage) => void Agent reports internal reasoning state.
onAgentStartedSpeaking (message: DeepgramVoiceAgentAgentStartedSpeakingMessage) => void Response playback session begins (latency metrics included).
onAgentAudioDone (message: DeepgramVoiceAgentAgentAudioDoneMessage) => void Agent finishes emitting audio for a turn.
onAudio (audioData: ArrayBuffer) => void Raw binary audio chunk received from the socket.
onAudioConfig (message: DeepgramVoiceAgentAudioConfigMessage) => void The server announces the output audio format (the native player is restarted at that rate).
onPromptUpdated (message: DeepgramVoiceAgentPromptUpdatedMessage) => void Active prompt is updated (e.g. after updatePrompt).
onSpeakUpdated (message: DeepgramVoiceAgentSpeakUpdatedMessage) => void Active speak configuration changes (server-driven).
onListenUpdated (message: DeepgramVoiceAgentListenUpdatedMessage) => void Listen configuration is updated (e.g. after updateListen).
onThinkUpdated (message: DeepgramVoiceAgentThinkUpdatedMessage) => void Think configuration is updated (e.g. after updateThink).
onThinkLiveUpdated (message: DeepgramVoiceAgentThinkLiveUpdatedMessage) => void think_live behaviours are updated (after updateThinkLive).
onLatencyReport (message: DeepgramVoiceAgentLatencyReportMessage) => void Per-turn latency breakdown (stt_latency, ttt_*_latency, tts_latency, total_latency).
onFrustrationDetected (message: DeepgramVoiceAgentFrustrationDetectedMessage) => void think_live detected a frustrated user.
onCustomFromThinkProvider (message: DeepgramVoiceAgentCustomFromThinkProviderMessage) => void A custom think provider forwarded a payload.
onHistory (message: DeepgramVoiceAgentHistoryMessage) => void Conversation history (text turn or function calls) is replayed by the server.
onInjectionRefused (message: DeepgramVoiceAgentInjectionRefusedMessage) => void Inject request rejected (typically while the agent is speaking).
onMessage (message: DeepgramVoiceAgentServerMessage) => void Catch-all for every JSON message from the API.
Function calls — client-side tools
Callback Signature Fired when
onFunctionCallRequest (message: DeepgramVoiceAgentFunctionCallRequestMessage) => void Agent asks the client to execute a tool marked client_side: true. Respond with sendFunctionCallResponse.
onFunctionCallResponse (message: DeepgramVoiceAgentReceiveFunctionCallResponseMessage) => void Server shares the outcome of a non-client-side function call.
onFunctionCallCancelled (message: DeepgramVoiceAgentFunctionCallCancelledMessage) => void Pending calls were cancelled (e.g. the user kept talking); drop their results.

Returned methods

Connection

Method Signature Description
connect (settings?: DeepgramVoiceAgentSettings) => Promise<void> Opens the socket, optionally merges settings, starts mic streaming.
disconnect () => void Tears down the socket, stops recording, removes listeners.
isConnected () => boolean Returns true when the socket is open.
mute () => void Stops forwarding mic audio while keeping the socket alive with periodic KeepAlive frames.
unmute () => void Resumes forwarding microphone audio.
getStats () => SessionStats Snapshot of the session telemetry (always available, never re-renders).

Messaging

Method Signature Description
sendMessage (message: DeepgramVoiceAgentClientMessage) => boolean Send a pre-built client envelope (custom message types).
sendSettings (settings: DeepgramVoiceAgentSettings) => boolean Update settings mid-session.
sendMedia (chunk: ArrayBuffer | Uint8Array | number[]) => boolean Stream additional PCM (e.g. pre-recorded buffer).
sendKeepAlive () => boolean Emit a KeepAlive ping.
injectUserMessage (content: string) => boolean Inject a user-side text turn.
injectAgentMessage (message: string, options?: DeepgramVoiceAgentInjectBehavior | { behavior?; dynamic? }) => boolean Make the agent speak. behavior: 'default' (refused mid-turn), 'queue' or 'interrupt'; dynamic: true lets the LLM phrase the reply from message.
forceEndTurn () => boolean End the user's turn now (push-to-talk; pairs with Flux eot_threshold: 1.0).
updatePrompt (prompt: string) => boolean Replace the active system prompt.
updateListen (listen: DeepgramVoiceAgentListenConfig) => boolean Swap the speech-recognition (listen) provider mid-session.
updateThink (think: DeepgramVoiceAgentThinkConfig) => boolean Swap the LLM (think) provider mid-session.
updateSpeak (speak: DeepgramVoiceAgentSpeakConfig | DeepgramVoiceAgentSpeakConfig[]) => boolean Swap the text-to-speech (speak) provider mid-session.
updateThinkLive (thinkLive: Record<string, unknown>) => boolean Update the think_live behaviours mid-session.

Function calls

Method Signature Description
sendFunctionCallResponse (response: Omit<DeepgramVoiceAgentFunctionCallResponseMessage, 'type'>) => boolean Return tool results for a client-side function call.

Reactive state

Each value is undefined unless its corresponding track* flag is true.

Return Type Requires
state { connectionState: 'idle' | 'connecting' | 'connected' | 'disconnected'; error: string | null; warning: string | null } trackState: true
isMuted boolean trackState: true
conversation Array<{ role: string; content: string }> trackConversation: true
agentStatus { thinking: string | null; latency: { total?: number; tts?: number; ttt?: number } | null } trackAgentStatus: true
clearConversation () => void trackConversation: true
stats SessionStats (bytesSent, bytesReceived, framesDropped, reconnects, connectedAtMs, firstResultMs) trackStats: true

Settings payload (DeepgramVoiceAgentSettings)

Expand settings fields
Field Type Purpose
tags string[] Labels applied to the session for analytics/routing.
flags.history boolean Enable prior history playback to the agent.
audio.input DeepgramVoiceAgentAudioConfig Configure encoding/sample rate for microphone audio.
audio.output DeepgramVoiceAgentAudioConfig Choose output encoding/sample rate/bitrate for agent speech.
agent DeepgramVoiceAgentAgentConfig | string Inline configuration, or the UUID of a reusable agent configuration stored in your Deepgram project.
agent.context.messages DeepgramVoiceAgentContextMessage[] Seed the conversation with prior turns or system notes.
agent.listen.provider DeepgramVoiceAgentListenProvider Speech recognition: version 'v1' (Nova: language, keyterms, smart_format) or 'v2' (Flux: eot_threshold, eager_eot_threshold, eot_timeout_ms, language_hints, keyterms).
agent.think.provider DeepgramVoiceAgentThinkProvider LLM selection (type, model, temperature, reasoning_mode, version, etc.).
agent.think.functions DeepgramVoiceAgentFunctionConfig[] Tooling exposed to the agent (name, parameters, optional endpoint, defer_until_eot).
agent.think.prompt string System prompt presented to the thinking provider.
agent.think_live Record<string, unknown> Conversational behaviours (backchannels, presence checks, frustration detection).
agent.speak DeepgramVoiceAgentSpeakConfig | DeepgramVoiceAgentSpeakConfig[] Text-to-speech provider (Deepgram version 'v2' + flux-* model with speed / expressivity, or 'v1' + aura-*); an array lists fallbacks. Defaults to flux-kit-en.
agent.greeting string Optional greeting played once settings are applied.
mip_opt_out boolean Opt the session out of the Model Improvement Program.

Speech-to-Text (useDeepgramSpeechToText)

The speech hook streams microphone audio over WebSocket and also handles prerecorded transcription. It defaults to STT v1 and auto-boots into Flux v2 when apiVersion: 'v2' is supplied (model defaults to flux-general-en).

💭 Flux v2 envelope handled correctly. The hook decodes Deepgram's TurnInfo events and treats event === 'EndOfTurn' as the final flag, so you don't have to reason about the new envelope yourself. Connected, ConfigureSuccess / ConfigureFailure, and Error messages are surfaced via callbacks.

Live streaming quick start

const {
  startListening,
  stopListening,
  state, // { status, error }
  transcript, // "Hello world..."
} = useDeepgramSpeechToText({
  trackState: true,
  trackTranscript: true,
  onTranscript: console.log,
  live: {
    apiVersion: 'v2',
    model: 'flux-general-en',
    eotThreshold: 0.55,
  },
});

<Text>Transcript: {transcript}</Text>
<Button
  title="Start"
  onPress={() => startListening({ keyterm: ['Deepgram'] })}
/>
<Button title="Stop" onPress={stopListening} />

💡 When you opt into apiVersion: 'v2' the hook automatically selects flux-general-en if you do not provide a model.

File transcription quick start

import * as DocumentPicker from 'expo-document-picker';

const { transcribeFile } = useDeepgramSpeechToText({
  onTranscribeSuccess: (text) => console.log(text),
  prerecorded: {
    punctuate: true,
    summarize: 'v2',
  },
});

const pickFile = async () => {
  const res = await DocumentPicker.getDocumentAsync({ type: 'audio/*' });
  if (res.canceled) return;
  const { uri, name, mimeType } = res.assets[0]!;
  await transcribeFile({ uri, name, type: mimeType }, { topics: true, intents: true });
};

API reference (Speech-to-Text)

Signature

const {
  // Live streaming
  startListening, stopListening, pause, resume,
  // File transcription
  transcribeFile,
  // Reactive state (opt-in)
  state, transcript, interimTranscript, isPaused, audioLevel, recordingUri,
} = useDeepgramSpeechToText(props);

Hook props

Configuration

Prop Type Description
live DeepgramLiveListenOptions Default options merged into every live stream.
prerecorded DeepgramPrerecordedOptions Default options merged into every file transcription.
reconnect DeepgramReconnectOptions Auto-reconnect config for the live socket. Disabled unless reconnect.enabled is true.
metering { enabled?: boolean; intervalMs?: number } Microphone audio-level (VU meter) events. Disabled by default; set enabled: true to emit a normalized RMS level (~10 Hz, tune with intervalMs).
recordToFile { enabled?: boolean; path?: string; format?: 'wav' } Persist the captured mic audio to a WAV file while it streams. Disabled by default. Omit path to let the native module pick an app-specific location.

Live streaming callbacks

Callback Signature Fired when
onBeforeStart () => void Before requesting mic permissions or starting a stream.
onStart () => void The WebSocket opens.
onTranscript (transcript: string, event?: DeepgramTranscriptEvent) => void Every transcript update (partial and final).
onTurnInfo (message: DeepgramFluxTurnInfo) => void Every Flux (v2) TurnInfo event, including StartOfTurn, EagerEndOfTurn and TurnResumed.
onMessage (message: DeepgramListenServerMessage) => void Every JSON message from the live socket (Results, UtteranceEnd, ConfigureSuccess, ConfigureFailure, Warning, ...).
onError (error: unknown) => void A streaming error occurs.
onAudioLevel (level: number) => void A new mic audio level (0..1 normalized RMS) is available. Requires metering.enabled.
onRecordingComplete (uri: string) => void A recordToFile session finished; receives the file:// URI of the saved WAV. Requires recordToFile.enabled.
onReconnecting (attempt: number) => void A reconnect attempt begins (1-based attempt number). Requires reconnect.enabled.
onReconnected () => void The live socket successfully reconnects.
onEnd () => void The socket closes (manual stop, or after reconnect attempts are exhausted).

File transcription callbacks

Callback Signature Fired when
onBeforeTranscribe () => void Before posting a prerecorded request.
onTranscribeSuccess (transcript: string) => void The final transcript arrives.
onTranscribeError (error: unknown) => void The prerecorded request fails.

Reactive tracking flags (opt-in)

Prop Default Enables
trackState false state return — status + last error.
trackTranscript false transcript and interimTranscript returns.
trackStats false stats return — session telemetry, updated at most once per second.

Returned methods

Method Signature Description
startListening (options?: DeepgramLiveListenOptions) => Promise<void> Requests mic access, starts recording, streams audio to Deepgram.
stopListening () => void Stops recording and closes the active WebSocket.
pause () => void Pauses mic streaming while keeping the socket alive via KeepAlive. On v1 a Finalize is sent first to flush buffered audio.
resume () => void Resumes mic streaming after a pause.
configure (options: DeepgramLiveConfigureOptions) => boolean Updates keyterms and numerals (Nova-3 and Flux) or Flux turn thresholds and languageHints mid-stream. The server replies with ConfigureSuccess / ConfigureFailure via onMessage.
forceEndTurn () => boolean Flux only: ends the current turn now (e.g. push-to-talk release).
getStats () => SessionStats Snapshot of the session telemetry (always available, never re-renders).
transcribeFile (file: DeepgramPrerecordedSource, options?: DeepgramPrerecordedOptions) => Promise<void> Uploads a file/URI/URL and resolves via the success/error callbacks.

Reactive state

Return Type Requires
state { status: 'idle' | 'loading' | 'listening' | 'transcribing' | 'error'; error: Error | null } trackState: true
isPaused boolean trackState: true
audioLevel number (0..1 normalized RMS) trackState: true + metering.enabled
recordingUri string (last saved file:// WAV) trackState: true + recordToFile.enabled
transcript string trackTranscript: true
interimTranscript string trackTranscript: true
stats SessionStats (bytesSent, bytesReceived, framesDropped, reconnects, connectedAtMs, firstResultMs) trackStats: true

Live transcription options (DeepgramLiveListenOptions)

Model & API version
Option Type Default Purpose
apiVersion 'v1' | 'v2' 'v1' Selects the realtime API generation ('v2' unlocks Flux streaming).
model DeepgramLiveListenModel 'nova-3' (v1) / 'flux-general-en' (v2) Streaming model. Flux also supports 'flux-general-multi' for multilingual turns.
version string – Specific model version.
language string Auto BCP-47 hint (e.g. en-US), or 'multi' for Nova-3 multilingual.
languageHint string | string[] – Bias language detection for Nova-3 language: 'multi' and flux-general-multi.
Audio input
Option Type Default Purpose
encoding DeepgramLiveListenEncoding 'linear16' Codec supplied to Deepgram.
sampleRate number 16000 PCM sample rate.
channels number – Channel count.
multichannel boolean false Transcribe each channel independently.
Formatting & content
Option Type Default Purpose
punctuate boolean false Auto-insert punctuation/capitalisation.
smartFormat boolean false Apply Deepgram smart formatting.
numerals boolean false Convert spoken numbers to digits.
measurements boolean false Convert measurements to abbreviations.
dictation boolean false Dictation features.
fillerWords boolean false Include "um"/"uh".
profanityFilter boolean false Remove profanity.
detectEntities boolean false Extract entities (names, places, etc.) into the results.
redact DeepgramLiveListenRedaction | DeepgramLiveListenRedaction[] – Remove PCI/PII.
replace string | string[] – Replace specific terms.
search string | string[] – Return timestamps for search terms.
keywords string | string[] – Boost or suppress keywords on legacy/non-Nova-3 models.
keyterm string | string[] – Bias Nova-3 and Flux with key terms.
Endpointing & VAD
Option Type Default Purpose
endpointing number | boolean – Endpoint detection (false to disable).
interimResults boolean false Emit interim (non-final) transcripts.
utteranceEndMs number – Delay before emitting utterance-end event.
vadEvents boolean false Emit voice activity detection events.
diarizeModel DeepgramLiveListenDiarizeModel – Diarisation model ('latest' / 'v1'). Separates speakers.
Flux v2 (only when apiVersion: 'v2')

Flux also accepts keyterm, numerals, profanityFilter, redact ('numbers' / 'aggressive_numbers'), mipOptOut and tag. Other v1 options are dropped from the URL.

Option Type Purpose
eotThreshold number Confidence required to finalise a turn (0.5–1.0; 1.0 disables automatic end-of-turn).
eagerEotThreshold number Confidence required to emit an eager turn.
eotTimeoutMs number Silence timeout before closing a turn.
Webhooks & metadata
Option Type Default Purpose
callback string – Webhook URL invoked when the stream finishes.
callbackMethod 'POST' | 'GET' | 'PUT' | 'DELETE' 'POST' HTTP verb for callback.
extra Record<string, string | number | boolean> – Custom metadata returned with the response.
tag string – Label the request for reporting.
mipOptOut boolean false Opt out of the Model Improvement Program.

Prerecorded transcription options (DeepgramPrerecordedOptions)

Model & language
Option Type Default Purpose
model DeepgramPrerecordedModel API default Transcription model.
version DeepgramPrerecordedVersion 'latest' Specific model version.
language string Auto BCP-47 language hint, or 'multi' for Nova-3 multilingual.
languageHint string | string[] – Bias detection with Nova-3 language: 'multi'.
detectLanguage boolean | string | string[] false Auto-detect language or restrict detection.
Audio input
Option Type Default Purpose
encoding DeepgramPrerecordedEncoding – Codec of the uploaded audio.
multichannel boolean false Transcribe each channel independently.
Formatting & content
Option Type Default Purpose
punctuate boolean false Auto-insert punctuation.
smartFormat boolean false Smart formatting.
numerals boolean false Convert spoken numbers to digits.
measurements boolean false Convert measurements to abbreviations.
paragraphs boolean false Split transcript into paragraphs.
dictation boolean false Dictation formatting.
fillerWords boolean false Include filler words.
profanityFilter boolean false Remove profanity.
redact DeepgramPrerecordedRedaction | DeepgramPrerecordedRedaction[] – Remove sensitive content.
replace string | string[] – Replace specific terms.
search string | string[] – Timestamps for search terms.
keywords string | string[] – Boost or suppress keywords on legacy/non-Nova-3 models.
keyterm string | string[] – Bias Nova-3 and Flux with key terms.
Diarisation & utterances
Option Type Default Purpose
diarizeModel DeepgramPrerecordedDiarizeModel – Diarisation model ('latest' / 'v1' / 'v2'). Separates speakers.
utterances boolean false Utterance-level timestamps.
uttSplit number – Pause duration (s) used to split utterances.
detectEntities boolean false Extract entities (names, places, etc.).
Intelligence (summarise / topics / intents / sentiment)
Option Type Default Purpose
summarize DeepgramPrerecordedSummarize false AI summary (true, 'v1', 'v2').
topics boolean false Detect topics.
customTopic string | string[] – Additional topics to monitor.
customTopicMode DeepgramPrerecordedCustomMode 'extended' 'extended' (additive) or 'strict' (exact).
intents boolean false Detect intents.
customIntent string | string[] – Custom intents.
customIntentMode DeepgramPrerecordedCustomMode 'extended' 'extended' or 'strict'.
sentiment boolean false Sentiment analysis.
Webhooks & metadata
Option Type Default Purpose
callback string – Webhook URL.
callbackMethod DeepgramPrerecordedCallbackMethod 'POST' HTTP verb for callback.
extra DeepgramPrerecordedExtra – Metadata returned with the response.
tag string | string[] – Label the request.
mipOptOut boolean false Opt out of the Model Improvement Program.

Text-to-Speech (useDeepgramTextToSpeech)

Generate audio via a single HTTP call or stream interactive responses over WebSocket. The hook exposes granular configuration for both request paths.

Aura vs Flux TTS

The hook picks the endpoint from the model name:

Aura (aura-*) Flux TTS (flux-*)
Endpoint /v1/speak /v2/speak
Turn model Text buffer; Flush emits audio Flush ends the turn; per-turn SpeechStarted / SpeechMetadata
Barge-in clearStream() sends Clear interrupt() stops playback, cancels the turn and reports text_spoken / text_remaining
Mid-stream changes – configureStream({ speed })
Extra options – expressivity (-2..2, beta), speed 0.5–1.5

Both stream raw linear16 / mulaw / alaw and accept Token or Bearer auth. Aura allows up to 20 Flush messages per 60 seconds. Flux closes a session after 60 s without client messages; React Native's WebSocket cannot send ping frames, so start a new stream after long pauses.

HTTP synthesis quick start

const { synthesize } = useDeepgramTextToSpeech({
  options: {
    http: {
      model: 'aura-2-asteria-en',
      encoding: 'mp3',
      bitRate: 48000,
      container: 'none',
    },
  },
  onSynthesizeSuccess: (buffer) => {
    console.log('Received bytes', buffer.byteLength);
  },
});

await synthesize('Hello from Deepgram!');

Streaming quick start

const {
  startStreaming,
  sendText,
  flushStream,
  clearStream,
  closeStreamGracefully,
  stopStreaming,
  state, // { status, error }
} = useDeepgramTextToSpeech({
  trackState: true,
  autoPlayAudio: true, // Automatically play received audio
  options: {
    stream: {
      model: 'aura-2-asteria-en',
      encoding: 'linear16',
      sampleRate: 24000,
      autoFlush: false,
    },
  },
  onAudioChunk: (chunk) => console.log('Audio chunk', chunk.byteLength),
  onStreamMetadata: (meta) => console.log(meta.model_name),
});

await startStreaming('Booting stream…');
sendText('Queue another sentence', { sequenceId: 1 });
flushStream();
closeStreamGracefully();

Flux TTS streaming with barge-in

const { startStreaming, sendText, flushStream, interrupt } =
  useDeepgramTextToSpeech({
    options: {
      stream: { model: 'flux-haley-en', expressivity: 1, autoFlush: false },
    },
    onSpeechMetadata: (turn) => console.log(turn.billable_character_count),
    onSpeechInterrupted: ({ text_spoken }) => trimLlmContext(text_spoken),
  });

await startStreaming('Sure, ');
for await (const token of llmTokens) sendText(token); // sent verbatim
flushStream(); // ends the turn

// User talks over the agent:
interrupt({ playbackOffsetMs: playedMs });

API reference

Signature

const {
  // HTTP synthesis
  synthesize, synthesizeToBytes,
  // Streaming
  startStreaming, sendText, sendMessage,
  flushStream, clearStream, interrupt, configureStream,
  closeStreamGracefully, stopStreaming,
  // Reactive state (opt-in)
  state,
} = useDeepgramTextToSpeech(props);

Hook props

Configuration

Prop Type Default Description
options UseDeepgramTextToSpeechOptions – Defaults merged into HTTP and streaming requests.
autoPlayAudio boolean true Auto-play received audio with the native player.
trackState boolean false Enable state return.

HTTP synthesis callbacks

Callback Signature Fired when
onBeforeSynthesize () => void Before dispatching the HTTP request.
onSynthesizeSuccess (audio: ArrayBuffer) => void The HTTP request succeeds.
onSynthesizeError (error: unknown) => void The HTTP request fails.

Streaming callbacks

Callback Signature Fired when
onBeforeStream () => void Before opening the WebSocket.
onStreamStart () => void Socket is open and ready.
onAudioChunk (chunk: ArrayBuffer) => void Each PCM chunk arrives.
onStreamMetadata (metadata: DeepgramTextToSpeechStreamMetadataMessage) => void Aura stream metadata arrives.
onStreamFlushed (event: DeepgramTextToSpeechStreamFlushedMessage) => void Deepgram confirms a flush.
onStreamCleared (event: DeepgramTextToSpeechStreamClearedMessage) => void Deepgram confirms an Aura clear.
onStreamWarning (warning: DeepgramTextToSpeechStreamWarningMessage) => void Deepgram warns about the stream.
onSpeechStarted (event: DeepgramTextToSpeechSpeechStartedMessage) => void Flux: a turn's audio starts.
onSpeechMetadata (event: DeepgramTextToSpeechSpeechMetadataMessage) => void Flux: a turn finished (billing and timing).
onSpeechInterrupted (event: DeepgramTextToSpeechSpeechInterruptedMessage) => void Flux: answer to interrupt().
onStreamMessage (message: DeepgramTextToSpeechStreamResponseMessage) => void Every JSON message (e.g. Flux Connected, ConfigureSuccess, SessionMetadata).
onStreamError (error: unknown) => void The WebSocket errors.
onStreamEnd () => void The stream closes (gracefully or otherwise).

Returned methods

HTTP

Method Signature Description
synthesize (text: string) => Promise<ArrayBuffer> Single-shot REST call; resolves with the full audio buffer and, for raw linear16 output with autoPlayAudio, plays it.
synthesizeToBytes (text: string, options?: DeepgramTextToSpeechHttpOptions) => Promise<{ data: ArrayBuffer; mimeType: string }> Like synthesize but does not play the audio — returns the raw bytes + MIME type for saving/caching. Results are cached in-memory (LRU) keyed by text + format, so repeated identical prompts skip the network. Pass options to override the format per call.

Streaming

Method Signature Description
startStreaming (text: string) => Promise<void> Open the WebSocket and queue the first message.
sendText (text: string, options?: { flush?: boolean; sequenceId?: number }) => boolean Queue additional text; can suppress auto-flush or set an Aura sequence id. Flux text is sent verbatim, Aura text is trimmed.
sendMessage (message: DeepgramTextToSpeechStreamInputMessage) => boolean Send a raw control message (Speak, Flush, Clear, Close, Interrupt, Configure).
flushStream () => boolean Aura: emit all buffered audio now. Flux: end the current turn.
clearStream () => boolean Aura: clear buffered text/audio. Flux: same as interrupt().
interrupt (options?: { playbackOffsetMs?: number }) => boolean Flux: stop playback, cancel the turn and drop in-flight audio.
configureStream (options: { speed?: number }) => boolean Flux: change the speed mid-stream.
closeStreamGracefully () => boolean Drain outstanding audio, then close.
stopStreaming () => void Force-close and release resources.

Reactive state

Return Type Requires
state DeepgramTextToSpeechState trackState: true

Configuration (UseDeepgramTextToSpeechOptions)

UseDeepgramTextToSpeechOptions has one section per transport plus shared query parameters.

Global options
Option Type Applies to Purpose
queryParams Record<string, string | number | boolean> Both Shared query string parameters appended to all requests.
http DeepgramTextToSpeechHttpOptions HTTP HTTP synthesis configuration.
stream DeepgramTextToSpeechStreamOptions Streaming Streaming configuration.
`options.http` (REST synthesis)
Option Type Purpose
model DeepgramTextToSpeechModel Aura or Flux voice (flux-* uses /v2/speak).
encoding DeepgramTextToSpeechHttpEncoding Output audio codec.
sampleRate DeepgramTextToSpeechSampleRate Output sample rate in Hz.
container DeepgramTextToSpeechContainer Wrap audio in a container ('none', 'wav', 'ogg').
bitRate DeepgramTextToSpeechBitRate Bit rate for compressed formats (e.g. MP3).
speed number Speed multiplier (Aura-2 0.7–1.5, Flux 0.5–1.5, default 1).
expressivity number Flux only (beta): -2 (calm) to 2 (animated).
tag string | string[] Label the request for usage reporting.
callback string Webhook URL invoked after synthesis completes.
callbackMethod DeepgramTextToSpeechCallbackMethod HTTP verb used for the callback.
mipOptOut boolean Opt out of the Model Improvement Program.
queryParams Record<string, string | number | boolean> Extra query parameters appended to the request.
`options.stream` (WebSocket streaming)
Option Type Purpose
model DeepgramTextToSpeechModel Aura or Flux voice (flux-* uses /v2/speak).
encoding DeepgramTextToSpeechStreamEncoding Output PCM encoding for streamed chunks.
sampleRate DeepgramTextToSpeechSampleRate Output sample rate in Hz.
speed number Speed multiplier (Aura-2 0.7–1.5, Flux 0.5–1.5, default 1).
expressivity number Flux only (beta): -2 (calm) to 2 (animated).
tag string | string[] Flux only: label the session for usage reporting.
mipOptOut boolean Opt out of the Model Improvement Program.
queryParams Record<string, string | number | boolean> Extra query parameters appended to the streaming URL.
autoFlush boolean Flush after each sendText call (defaults to true). On Flux a flush ends the turn.

Text Intelligence (useDeepgramTextIntelligence)

Run summarisation, topic detection, intent detection, sentiment analysis, and more over plain text or URLs.

const { analyze, state } = useDeepgramTextIntelligence({
  trackState: true,
  onAnalyzeSuccess: (response) => console.log(response.results.summary?.text),
  options: {
    summarize: true,
    topics: true,
    intents: true,
    language: 'en-US',
  },
});

await analyze({ text: 'Deepgram makes voice data useful.' });

API reference

Signature

const {
  analyze, // (input: { text?: string; url?: string }) => Promise<void>
  state,   // requires trackState: true
} = useDeepgramTextIntelligence(props);

Hook props

Prop Type Description
options UseDeepgramTextIntelligenceOptions Defaults merged into every analyze call.
onBeforeAnalyze () => void Before the request is dispatched.
onAnalyzeSuccess (response: DeepgramTextIntelligenceResponse) => void Result arrives.
onAnalyzeError (error: Error) => void Request fails.
trackState boolean Enable state return (default false).

Options (UseDeepgramTextIntelligenceOptions)

Analysis
Option Type Purpose
summarize boolean Run summarisation on the input.
topics boolean Detect topics.
customTopic string | string[] Additional topics to monitor.
customTopicMode 'extended' | 'strict' Additive (extended) or exact (strict).
intents boolean Detect intents.
customIntent string | string[] Custom intents.
customIntentMode 'extended' | 'strict' Additive or exact match.
sentiment boolean Sentiment analysis.
language DeepgramTextIntelligenceLanguage BCP-47 hint (default 'en').
tag string | string[] Label the request for usage reporting.
Webhooks
Option Type Purpose
callback string Webhook URL invoked after processing.
callbackMethod 'POST' | 'PUT' | (string & {}) HTTP method for the callback.

Management API (useDeepgramManagement)

Receive a fully typed REST client for the Deepgram Management API. No props are required.

const dg = useDeepgramManagement();

const projects = await dg.projects.list();
console.log('Projects:', projects.map((p) => p.name));

Snapshot of available groups

Most methods also accept an optional trailing query?: Record<string, any> argument for additional server-side filters/pagination.

Group Representative methods
models list(includeOutdated?), get(modelId)
projects list(), get(id), delete(id), patch(id, body), listModels(id)
keys list(projectId), create(projectId, body), get(projectId, keyId), delete(...)
usage listRequests(projectId), getRequest(projectId, requestId), getBreakdown(projectId)
balances list(projectId), get(projectId, balanceId)
auth grant(body?) — mint a short-lived access token ({ ttl_seconds? })

(Plus helpers for members, scopes, invitations, and purchases.)

Query examples

const dg = useDeepgramManagement();

// Include outdated models + extra query filters
const models = await dg.models.list(true, { limit: 50 });

// Project list pagination/filtering
const page1 = await dg.projects.list({ page: 1, per_page: 25 });

// Usage request windows
const requests = await dg.usage.listRequests('project_id', {
  page: 1,
  per_page: 100,
  from: '2026-03-01T00:00:00Z',
  to: '2026-03-17T23:59:59Z',
});

// Keys/members scoping
const keys = await dg.keys.list('project_id', { page: 1, per_page: 20 });
const members = await dg.members.list('project_id', { page: 1 });

query is passed through as URL query parameters, so you can supply endpoint-specific filters/pagination supported by Deepgram.


Expo config plugin reference

The package ships with an Expo config plugin (exported from app.plugin.js). It mutates AndroidManifest.xml and Info.plist at prebuild time so you don't have to.

What it does

Platform Effect
Android Adds android.permission.RECORD_AUDIO if missing.
Android When backgroundAudio !== false, adds the FOREGROUND_SERVICE, FOREGROUND_SERVICE_MICROPHONE, and FOREGROUND_SERVICE_MEDIA_PLAYBACK permissions and sets `android:foregroundServiceType="microphone
Android When androidNotification is provided, writes com.deepgram.notification.* <meta-data> entries that customize the foreground-service notification (title, text, channel name, icon).
iOS Sets NSMicrophoneUsageDescription from the microphonePermission option (with a sensible fallback).
iOS When backgroundAudio !== false, adds audio to UIBackgroundModes.

Options

// app.config.js
module.exports = {
  expo: {
    plugins: [
      [
        'react-native-deepgram',
        {
          microphonePermission:
            'Allow $(PRODUCT_NAME) to capture audio for real-time transcription.',
          backgroundAudio: true, // default — set false to disable background audio + foreground service
          androidNotification: {
            title: 'Transcribing…', // default: your app's name
            text: 'Live captions are running',
            channelName: 'Live audio',
            icon: 'ic_stat_audio', // drawable/mipmap resource name; default: launcher icon
          },
        },
      ],
    ],
  },
};
Option Type Default Effect
microphonePermission string Generic English fallback Sets NSMicrophoneUsageDescription. Always provide your own copy in production.
backgroundAudio boolean true When false, omits the iOS background-audio mode, the Android foreground-service permissions, and the service's foregroundServiceType. Choose this if your app must never run audio in the background — it also spares you the Play Console foreground-service declaration.
androidNotification object — Brands the Android keep-alive notification. title (default: app name), text, channelName (default: title), and icon (drawable/mipmap resource name, default: launcher icon) are each optional. See Background audio for the bare-RN <meta-data> equivalents.

🧭 Bare React Native without Expo? You can still load the plugin in your own prebuild pipeline: require('react-native-deepgram/app.plugin.js').


Common recipes

Copy-paste patterns for tasks that come up often.

Push-to-talk live transcription

import { useDeepgramSpeechToText } from 'react-native-deepgram';
import { Pressable, Text } from 'react-native';

export function PushToTalk() {
  const { startListening, stopListening, transcript } =
    useDeepgramSpeechToText({
      trackTranscript: true,
      live: { punctuate: true, interimResults: true },
    });

  return (
    <Pressable
      onPressIn={() => startListening()}
      onPressOut={() => stopListening()}
    >
      <Text>{transcript || 'Hold to talk'}</Text>
    </Pressable>
  );
}

Flux v2 streaming with end-of-turn handling

const stt = useDeepgramSpeechToText({
  live: {
    apiVersion: 'v2',
    model: 'flux-general-en',
    eotThreshold: 0.6,
    eotTimeoutMs: 800,
  },
  onTranscript: (text, event) => {
    if (event?.isFinal) {
      // a `TurnInfo` with `event === 'EndOfTurn'` arrived
      submitToBackend(text);
    } else {
      renderInterim(text);
    }
  },
  onTurnInfo: (turn) => {
    if (turn.event === 'StartOfTurn') stopAgentSpeech(); // barge-in
  },
});

// Push-to-talk release: close the turn without waiting for the model.
stt.forceEndTurn();

// Tighten turn detection mid-stream without reconnecting.
stt.configure({ eotThreshold: 0.8, keyterms: ['Deepgram'] });

Pause and resume a live stream

const { startListening, pause, resume, isPaused } = useDeepgramSpeechToText({
  trackState: true,
  live: { punctuate: true, interimResults: true },
});

// Stop forwarding mic audio without dropping the socket. Deepgram is kept warm
// with periodic KeepAlive frames, so resume() continues the same session.
<Button
  title={isPaused ? 'Resume' : 'Pause'}
  onPress={() => (isPaused ? resume() : pause())}
/>;

Auto-reconnect a dropped live stream

const stt = useDeepgramSpeechToText({
  trackState: true,
  reconnect: {
    enabled: true,
    maxRetries: 5, // give up after 5 attempts
    initialDelayMs: 500, // 0.5s, doubling each retry
    maxDelayMs: 10_000, // capped at 10s (+ jitter)
  },
  onReconnecting: (attempt) => console.log(`reconnecting… (#${attempt})`),
  onReconnected: () => console.log('back online'),
});

The Voice Agent hook accepts the same reconnect options and re-sends its Settings payload automatically on every successful reconnect.

Point at a regional or self-hosted endpoint

import { configure } from 'react-native-deepgram';

configure({
  apiKey: process.env.EXPO_PUBLIC_DEEPGRAM_API_KEY!,
  baseUrl: 'https://api.beta.deepgram.com/v1', // REST (include the /v1 segment)
  baseWss: 'wss://api.beta.deepgram.com/v1', // live STT + streaming TTS
});

Flux v2 URLs are derived automatically by swapping the trailing /v1 for /v2.

Transcribe a picked file with summaries + topics

import * as DocumentPicker from 'expo-document-picker';

const { transcribeFile } = useDeepgramSpeechToText({
  prerecorded: { punctuate: true, smartFormat: true },
  onTranscribeSuccess: (transcript) => console.log(transcript),
});

const pick = async () => {
  const res = await DocumentPicker.getDocumentAsync({ type: 'audio/*' });
  if (res.canceled) return;
  const { uri, name, mimeType } = res.assets[0]!;
  await transcribeFile(
    { uri, name, type: mimeType },
    { summarize: 'v2', topics: true, intents: true }
  );
};

Generate subtitles from a transcription

toSRT / toWebVTT turn a raw pre-recorded /listen response into a subtitle document — zero dependencies, pure functions. Request utterances: true for the best cue boundaries (the helpers fall back to word-level chunking otherwise), and pass speakerLabels: true alongside diarize_model=latest to prefix cues with Speaker N:.

import { toSRT, toWebVTT } from 'react-native-deepgram';
import type { DeepgramPrerecordedResponse } from 'react-native-deepgram';

const res = await fetch(
  'https://api.deepgram.com/v1/listen?model=nova-3&smart_format=true&utterances=true&diarize_model=latest',
  {
    method: 'POST',
    headers: {
      'Authorization': `Token ${DEEPGRAM_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ url: 'https://dpgr.am/spacewalk.wav' }),
  }
);
const response: DeepgramPrerecordedResponse = await res.json();

const srt = toSRT(response, { lineLength: 32, lineCount: 2 });
const vtt = toWebVTT(response, { speakerLabels: true });

Split a transcript by speaker

toSpeakerSegments folds the word-level diarization data of a response requested with diarize_model into per-speaker segments (it returns [] when diarization wasn't requested). Pairs naturally with the caption helpers' speakerLabels option above.

import { toSpeakerSegments } from 'react-native-deepgram';

const segments = toSpeakerSegments(response); // same response as above
segments.forEach(({ speaker, text, start, end, confidence }) => {
  console.log(`[${start}s–${end}s] Speaker ${speaker} (${confidence}): ${text}`);
});

Record the mic to a file while transcribing

Persist the captured audio to a WAV file and get the live transcript at the same time. The file:// URI arrives via onRecordingComplete (and, with trackState, as recordingUri) once stopListening() finishes writing.

import { useDeepgramSpeechToText } from 'react-native-deepgram';

const { startListening, stopListening, recordingUri } =
  useDeepgramSpeechToText({
    trackState: true,
    recordToFile: { enabled: true }, // omit `path` to use an app-specific file
    onTranscript: (text) => console.log(text),
    onRecordingComplete: (uri) => {
      console.log('saved recording at', uri); // file:///.../deepgram-….wav
    },
  });

// start, talk, then:
stopListening(); // onRecordingComplete fires with the finalized WAV

Want a specific destination? Pass a path (with or without a file:// prefix) — e.g. new File(Paths.document, 'take.wav').uri from expo-file-system. Only uncompressed wav is supported today.

Voice Agent with a client-side function

const agent = useDeepgramVoiceAgent({
  trackState: true,
  defaultSettings: {
    agent: {
      think: {
        provider: { type: 'open_ai', model: 'gpt-4o' },
        prompt: 'You are a helpful weather assistant.',
        functions: [
          {
            name: 'get_weather',
            description: 'Look up live weather for a city.',
            parameters: {
              type: 'object',
              properties: { city: { type: 'string' } },
              required: ['city'],
            },
            client_side: true, // executed by the app, not the server
          },
        ],
      },
      // ...listen + speak providers
    },
  },
  onFunctionCallRequest: async ({ functions }) => {
    for (const fn of functions) {
      if (!fn.client_side || fn.name !== 'get_weather' || !fn.id) continue;
      const args = JSON.parse(fn.arguments ?? '{}');
      const data = await fetchWeather(args.city);
      agent.sendFunctionCallResponse({
        id: fn.id,
        name: fn.name,
        content: JSON.stringify(data),
      });
    }
  },
});

Streaming TTS with manual flushing

const { startStreaming, sendText, flushStream, closeStreamGracefully } =
  useDeepgramTextToSpeech({
    autoPlayAudio: true,
    options: {
      stream: {
        model: 'aura-2-asteria-en',
        encoding: 'linear16',
        sampleRate: 24000,
        autoFlush: false, // we'll flush on punctuation boundaries instead
      },
    },
  });

await startStreaming('Streaming up.');
sendText(' Sentence one.');
sendText(' Sentence two.');
flushStream();         // emit audio for the buffered text immediately
closeStreamGracefully(); // finish remaining audio then close

One-shot HTTP TTS

synthesize resolves with the audio buffer. With the default raw linear16 output (container: 'none') and autoPlayAudio on, it also plays it through the native player at the requested sample rate. Compressed formats (MP3, Opus, AAC, FLAC) and WAV containers are returned without playback; play those with your own audio player.

const { synthesize } = useDeepgramTextToSpeech({
  options: {
    http: {
      model: 'aura-2-asteria-en',
      encoding: 'linear16',
      sampleRate: 24000,
    },
  },
});

const buffer = await synthesize('Hello from Deepgram.');
// `buffer` is raw 24 kHz PCM16 — also played out loud.

Save TTS to a file without playing it

Use synthesizeToBytes when you only want the bytes (to cache, upload, or write to disk). Unlike synthesize, it does not play the audio, and identical prompts are served from an in-memory LRU cache:

import { useDeepgramTextToSpeech } from 'react-native-deepgram';
import { File, Paths } from 'expo-file-system';

const { synthesizeToBytes } = useDeepgramTextToSpeech({
  options: {
    http: {
      model: 'aura-2-asteria-en',
      encoding: 'mp3',
      bitRate: 48000,
      container: 'none',
    },
  },
});

const { data, mimeType } = await synthesizeToBytes('Hello from Deepgram.');
const file = new File(Paths.document, 'greeting.mp3');
file.write(new Uint8Array(data));
// `file.uri` now points to a saved MP3 file; `mimeType` is "audio/mpeg".
// (Need base64 instead, e.g. for an upload? Use `arrayBufferToBase64(data)`.)

Run text intelligence on a transcript

analyze resolves when the request finishes; the response arrives through onAnalyzeSuccess:

const { analyze } = useDeepgramTextIntelligence({
  options: { summarize: true, topics: true, sentiment: true },
  onAnalyzeSuccess: ({ results }) => {
    console.log(results.summary?.text, results.topics, results.sentiments?.average);
  },
});

await analyze({ text: longTranscript });

Use the Management API to rotate keys

const dg = useDeepgramManagement();

const newKey = await dg.keys.create('project_id', {
  comment: 'mobile app',
  scopes: ['usage:write'],
  time_to_live_in_seconds: 60 * 60, // short-lived token
});
console.log('temp token =', newKey.key);

Handle errors by typed code

import { useDeepgramVoiceAgent, DeepgramError } from 'react-native-deepgram';

useDeepgramVoiceAgent({
  onError: (err) => {
    const code = err instanceof DeepgramError ? err.code : 'unknown';
    switch (code) {
      case 'permission_denied':
        return promptForMicAccess();
      case 'playback_error':
        return toast('Audio output failed — check the route and volume.');
      default:
        return console.error('Deepgram error', code, err);
    }
  },
});

DeepgramError.code is a DeepgramErrorCode | 'unknown', and the underlying failure is always kept on DeepgramError.cause.


Troubleshooting

"I hear myself echo back through the agent's mic"

You're not using hardware echo cancellation. If you're going through useDeepgramVoiceAgent, this should already be on — make sure you're testing on a physical device: the iOS Simulator has no VPIO and the Android emulator exposes no AcousticEchoCanceler, so the mic picks up the agent through your computer's speakers and barge-in cuts it off. Use headphones when testing on a simulator/emulator. If you're calling Deepgram.startRecording yourself, pass { enableVoiceProcessing: true }.

"Recording stops when the app goes to the background"

Make sure backgroundAudio is left at its default (true) on the Expo plugin and that you've re-prebuilt + re-installed after enabling it. On Android, check that the foreground-service notification is showing while audio is active — if it isn't, the manifest entries haven't been picked up: the service needs the FOREGROUND_SERVICE* permissions and android:foregroundServiceType including mediaPlayback on DeepgramAudioService (see Background audio); without them the service is skipped.

"Permission denied" on startListening / connect

The microphone permission was never granted. Either:

  • Trigger the permission prompt manually (Deepgram.requestRecordPermission() on iOS, PermissionsAndroid.request(RECORD_AUDIO) on Android), or
  • Make sure you're not on a secondary entitlement profile that strips NSMicrophoneUsageDescription.

If you previously denied the prompt, the OS will silently refuse subsequent requests until the user re-enables the permission in Settings.

Flux v2 returns no transcripts

Flux only accepts a small set of query parameters. The hook builds the right query map for you when you set apiVersion: 'v2', but if you bypass it, ensure you're sending only Flux-supported keys (model, encoding, sample_rate, eager_eot_threshold, eot_threshold, eot_timeout_ms, keyterm, language_hint, numerals, profanity_filter, redact, mip_opt_out, measurements, tag).

"Sound disappears when I unplug headphones"

That's intentional. The native module pauses playback on OldDeviceUnavailable (iOS) and ACTION_AUDIO_BECOMING_NOISY (Android) so the user isn't surprised by audio blasting through the speaker. Resume playback explicitly if your UX requires it.

Native code changes aren't taking effect

Native edits require a rebuild. With the example app:

yarn example:prebuild  # also runs pod install on iOS
yarn example:ios       # or example:android

If things look stale, yarn example:clean wipes the generated native folders and caches.

"react-native-deepgram: the native module is not linked"

The native module didn't link. In a bare RN project, run cd ios && pod install and rebuild, and make sure the New Architecture is enabled. In Expo, you must use a development build (the package can't run in Expo Go because it has native code).

Android builds fail with "minSdk too low"

Bump android/build.gradle minSdkVersion to 24 (Android 7.0). Some required AudioRecord.Builder APIs aren't available below that.


Example app

The repository includes an Expo-managed playground under example/ that wires up every hook in this package. It is configured for a tight inner-dev loop: edits to the library's TypeScript sources are hot-reloaded into the running app — no rebuild required.

react-native-deepgram example app showing the Voice Agent, Speech-to-Text, Text-to-Speech, Text Intelligence, and Management screens

Screen Shows
Voice Agent Nova or Flux listening, Flux/Aura voices (switchable live), barge-in, end-of-turn, injected user/agent messages, per-turn latency report, audio routing, telemetry
Speech-to-Text Nova-3, Nova-3 multilingual or Flux; Flux turn events and End turn; live keyterm configure(); pause/resume, metering, record-to-file, file transcription, SRT/WebVTT captions and speaker segments
Text-to-Speech HTTP synthesis, save to file, Aura and Flux streaming with Flux interrupt, mid-stream speed and expressivity
Text Intelligence / Management Summaries, topics, intents, sentiment; the Management API

1. Install workspace dependencies

git clone https://github.com/itsRares/react-native-deepgram
cd react-native-deepgram
yarn install

2. Configure your Deepgram key

cp example/.env.example example/.env
# then edit example/.env and paste your key

You can generate API keys from the Deepgram Console. For management endpoints, ensure the key carries the right scopes.

3. First run (compiles native code)

yarn example:ios       # iOS simulator (or `yarn example:ios --device` for a phone)
yarn example:android   # Android emulator / connected device

These run expo run:<platform> under the hood, which generates the native projects (if needed), installs CocoaPods, and launches Metro plus the app.

4. Subsequent runs (JS-only changes)

Once the app is installed on the device/simulator you can iterate on the library's TypeScript without rebuilding native code:

yarn start             # just starts Metro; the app on the device picks up changes

Edit anything in src/**/*.ts (or example/src/**) and the app reloads via Fast Refresh. Metro resolves react-native-deepgram to src/ through the react-native-deepgram-source export condition.

Want to test the published JS build instead? Run yarn build once, then start with yarn example:start:built (sets DEEPGRAM_USE_BUILT=1 so Metro resolves to lib/module).

5. When to rebuild natively

You only need to recompile when you change one of:

  • ios/**/*.{m,mm,h,swift} or any podspec
  • android/**/*.{kt,java,xml,gradle}
  • app.plugin.js / plugin/src/** (Expo config plugin — run yarn build:plugin first)
  • example/app.json or any other prebuild input

Rebuild flow:

yarn example:prebuild  # regenerates ios/ + android/ from app.json (and installs pods)
yarn example:ios       # or yarn example:android

If the app gets into a weird state, yarn example:clean wipes the generated native folders and caches so the next example:ios|android rebuilds from scratch.

Available example scripts

Script Purpose
yarn start Start Metro bundler (Fast Refresh)
yarn example:ios / yarn example:android Build + run on simulator/device
yarn example:prebuild Regenerate native projects from app.json
yarn example:start:built Run Metro pointed at lib/module instead of src/
yarn example:clean Wipe generated native folders + caches
yarn example <cmd> Run any script/binary in the example workspace (e.g. yarn example expo install)

Roadmap

  • ✅ Speech-to-Text (WebSocket + REST)
  • ✅ Speech-to-Text v2 / Flux streaming support
  • ✅ Text-to-Speech (HTTP synthesis + WebSocket streaming)
  • ✅ Flux TTS (/v2/speak) with interrupt and mid-stream configure
  • ✅ New Architecture TurboModule with typed native events
  • ✅ Text Intelligence (summaries, topics, sentiment, intents)
  • ✅ Management API wrapper
  • ✅ Hardware echo cancellation (iOS VPIO + Android VOICE_COMMUNICATION)
  • ✅ Background-audio support (iOS UIBackgroundModes + Android foreground service)
  • ✅ iOS 17+ permission API (AVAudioApplication) and Bluetooth option (AllowBluetoothHFP)
  • ✅ Microphone audio-level / metering events
  • ✅ Record the microphone to a WAV file while streaming
  • ✅ Audio output route control (speaker / earpiece / Bluetooth) with route-change events
  • ✅ Typed error codes (DeepgramError / DeepgramErrorCode)
  • 🚧 Detox E2E tests for the example app

Contributing

Issues and PRs are welcome—see CONTRIBUTING.md.


License

MIT

About

Brings Deepgram's capabilities to React Native applications, with a focus on performance and ease of use.

Topics

Resources

Code of conduct

Contributing

Stars

6 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages