React Native Voicebot UI Kit

View Release Notes →

enx-voice-bot-react-native lets a React Native app place an outbound call to an EnableX Dialogs voice bot and hold a live, two-way voice conversation with it. Unlike the Chatbot UI Kit, there is no chat-bubble style prebuilt screen to attach — a voice call has no message list to render. Instead, the package gives you a single call-control client (EnxVoiceClient) that handles the REST session, the real-time Pipecat WebSocket audio stream, microphone capture, and speaker playback for you, and reports call/mute/speaking state back through a listener so your app can drive a minimal call screen (connect button, mute toggle, speaking indicators) with whatever look you want.

React Native Voicebot UI Kit v1.0.0  ·  Released August 10, 2026

First release of the React Native Voicebot UI Kit, published on npm. See Release Notes for what's included.

  View on NPM

This is the first Voicebot UI Kit platform published on this site — Android, iOS, and Flutter follow the same call-control-client model. Use this page's structure as the reference when those platforms ship.

Setup

Requirements

RequirementVersion
React18.0.0 or later
React Native0.71.0 or later (validated with the bundled ExampleApp on 0.73.0)
iOS deployment target15.0 or later
AndroidStandard React Native 0.71+ Android toolchain; Gradle autolinking
Node.js18 or later
XcodeRecent stable version
CocoaPodsFor installing native iOS dependencies
Virtual number & voice bot configProvided by your EnableX account / dialog setup

Installation

Add the package

npm install enx-voice-bot-react-native

Or, if the SDK is provided as a local tarball:

npm install file:./enx-voice-bot-react-native-1.0.0.tgz

Peer dependencies (must already exist in your app): react (≥18.0.0) and react-native (≥0.71.0). Installing the package automatically pulls in react-native-permissions, react-native-live-audio-stream, protobufjs, and eventemitter3 as runtime dependencies — no separate install step is needed for these.

iOS setup

  • Install native pods:
    cd ios && pod install
  • Confirm your Podfile platform target is iOS 15.0 or later — the SDK's podspec requires it.
  • Add the microphone usage description to Info.plist (see Required Permissions).

Android setup

  • No manual linking is required — React Native autolinking registers the SDK's native module.
  • Add the required permissions to AndroidManifest.xml (see Required Permissions).
  • Rebuild the app:
    npx react-native run-android

Required Permissions MANDATORY

The SDK requests microphone access at runtime before every call, but the underlying platform permission must be declared by the host app.

Android — AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
PermissionPurpose
INTERNETRequired for the REST call and WebSocket media connection.
RECORD_AUDIORequired to capture microphone audio.
MODIFY_AUDIO_SETTINGSRequired to route call audio through the speakerphone / communication device.

iOS — Info.plist

<key>NSMicrophoneUsageDescription</key>
<string>This app needs microphone access to talk to the voice bot.</string>

iOS shows this text to the user the first time the app requests microphone access; the SDK will not proceed with a call if this key is missing.

Runtime permission flow

When EnxVoiceClient.connect() is called, the SDK automatically checks the microphone permission and requests it if it has not been granted yet. If the user denies the permission, connect() fails and the listener's onError() callback receives an error message — no call is placed.

Configuration

Use EnxSdkConfig to choose which EnableX environment the SDK talks to.

ConfigurationBase URLLogging
EnxSdkConfig.qahttps://voiceagent-qa.enablex.ioEnabled (verbose)
EnxSdkConfig.productionhttps://voiceagent.enablex.ioDisabled

For a custom endpoint (for example, a private or regional deployment), build a config from an environment object:

import { EnxSdkConfig } from 'enx-voice-bot-react-native';

const config = EnxSdkConfig.fromEnvironment(
  { name: 'Staging', callApiBaseUrl: 'https://voiceagent-staging.example.com' },
  { enableLogging: true },
);

You can also override the call API base URL for a single call without changing the SDK config, by passing callApiBaseUrl to connect() — see Connect and disconnect.

Integration Steps

  1. Install the package and, on iOS, run pod install (see Installation above).
  2. Add the Android manifest permissions and the iOS Info.plist microphone usage description (see Required Permissions above).
  3. Implement EnxVoiceListener to receive call, mute, error, and speaking-state callbacks — see the Usage Example.
  4. Create an EnxVoiceClient, call .init(virtualNumber), .setEnxVoiceListener(...), and .setSdkConfig(EnxSdkConfig.qa or .production).
  5. Call connect() to start a call and disconnect() to end it; use muteAudio() and sendDtmf() as needed during the call.
  6. Switch to EnxSdkConfig.production (or fromEnvironment with enableLogging: false) before release builds to disable verbose SDK logging.
  7. Test on real Android and iOS devices — see Developer Troubleshooting if a call fails to connect.
API Reference

Module Exports

Import everything from the package root:

import {
  EnxVoiceClient,
  EnxVoiceListener,
  EnxVoiceState,
  EnxSdkConfig,
  EnxVoiceEnvironment,
} from 'enx-voice-bot-react-native';
ExportKind
EnxVoiceClientClass — main entry point for placing and controlling a voice bot call.
EnxVoiceListenerInterface (TypeScript type) — callbacks for call/audio events.
EnxVoiceStateEnum — call connection states.
EnxSdkConfigClass — SDK environment/logging configuration.
EnxVoiceEnvironmentType + constants — REST base URL definitions (QA, Production).
encodePipecatFrame / decodePipecatFrameFunctions — low-level protobuf frame helpers (advanced use only).

EnxVoiceClient class

Controls the lifecycle of a single outbound voice bot call: requesting permissions, calling the REST API, opening the Pipecat WebSocket, and streaming audio.

Constructor

new EnxVoiceClient()

Creates a new client with its own audio player and recorder. Each instance manages at most one call at a time.

Configuration methods

setSdkConfig(config: EnxSdkConfig): EnxVoiceClient

Sets the environment (QA/Production/custom) and logging behavior used for subsequent connect() calls. Returns this for chaining.

init(virtualNumber: string): EnxVoiceClient

Sets the virtual number (E.164 format) that will be dialed when connect() is called. Returns this for chaining.

setEnxVoiceListener(listener: EnxVoiceListener): EnxVoiceClient

Registers the listener that receives call, mute, error, and speaking-state callbacks. Returns this for chaining.

setOnDtmfKeypadChanged(callback?: (showKeypad: boolean) => void): EnxVoiceClient

Registers a callback invoked when the active call session reports whether DTMF keypad input is available. Returns this for chaining.

Call control methods

connect(args?: { callApiBaseUrl?: string }): Promise<void>

Starts a call: verifies/requests the microphone permission, initializes native audio, calls POST /v2token, and opens the WebSocket media session. If callApiBaseUrl is omitted, the URL from the configured EnxSdkConfig environment is used. Emits onStatus(connecting) then either onStatus(connected) or onStatus(failed) via the listener.

disconnect(): Promise<void>

Ends the active call: deletes the REST call session, closes the WebSocket, stops audio capture/playback, and resets internal state. Emits onStatus(disconnected) and onCallDisconnect().

muteAudio(isMute: boolean): Promise<boolean>

Mutes or unmutes the microphone. Returns true on success, false if an error occurred. Emits onMuteStateChanged() on success.

isMuted(): boolean

Returns the current microphone mute state.

sendDtmf(digits: string): Promise<boolean>

Sends DTMF digits (0–9, *, #) to the active call session via POST /v2token/{voiceId}/dtmf. Returns false if there is no active session, the input is empty, or it contains invalid characters.

Read-only properties

PropertyTypeDescription
activeVoiceIdstring | undefinedThe voice session ID returned by POST /v2token for the current call, if any.
hasCallApiBaseUrlbooleanWhether a non-empty call API base URL is currently set.
isConnectingOrConnectedbooleanTrue while a call is being established or is active.
hasNotifiedDisconnectbooleanTrue once onCallDisconnect() has been delivered for the current/last call.

EnxVoiceListener interface

Implement this interface and pass it to setEnxVoiceListener() to receive call events.

MethodCalled when…
onCallConnected()The WebSocket media session has successfully opened.
onCallDisconnect()The call has fully ended (user-initiated or by the remote side/error). Fires at most once per call.
onError(message: string)A recoverable or fatal error occurs (permission denial, REST failure, WebSocket error, etc.).
onMuteStateChanged(isMuted: boolean)The microphone mute state changes as a result of muteAudio().
onStatus(state: EnxVoiceState)The overall call state transitions (see EnxVoiceState below).
onUserSpeaking(isUserSpeaking: boolean)The bot's voice-activity detector reports the user started/stopped speaking.
onBotSpeaking(isBotSpeaking: boolean)The bot starts or stops producing spoken audio.

EnxVoiceState enum

ValueDescription
connectingPermissions are being verified and the call is being initiated.
connectedThe media WebSocket is open and audio can flow.
disconnectedNo active call. This is also the initial state before connect() is called.
failedThe call failed to establish or was terminated due to an error.

EnxSdkConfig class

Holds the REST environment and logging preference used by EnxVoiceClient.

Static members

MemberEnvironmentLogging
EnxSdkConfig.qahttps://voiceagent-qa.enablex.ioEnabled
EnxSdkConfig.productionhttps://voiceagent.enablex.ioDisabled

Static method

EnxSdkConfig.fromEnvironment(
  environment: EnxVoiceEnvironment,
  opts?: { enableLogging?: boolean }
): EnxSdkConfig

Builds a config from a custom or predefined environment. If opts.enableLogging is omitted, logging defaults to disabled for the production environment and enabled otherwise.

Instance members

MemberTypeDescription
environmentEnxVoiceEnvironmentThe REST environment this config points to.
enableLoggingbooleanWhether the SDK emits verbose debug logs.
toString()stringHuman-readable summary, e.g. "EnxSdkConfig(environment: QA, logging: true)".

EnxVoiceEnvironment type

FieldDescription
nameHuman-readable environment name (e.g. "QA", "Production").
callApiBaseUrlBase URL (no trailing slash) for the /v2token REST endpoints.

Predefined constants: EnxVoiceEnvironment.qa and EnxVoiceEnvironment.production. Supply your own object with the same shape to target a custom deployment.

REST API Reference

EnxVoiceClient calls the following EnableX Voice Agent REST endpoints internally. They are documented here for troubleshooting and for advanced integrations that need to call them directly.

Start a call

POST {callApiBaseUrl}/v2token
Content-Type: application/json

Request body:

{
  "phone": "<virtual number, E.164>"
}

On success, returns HTTP 200 with a JSON body containing voice_id (or voiceId), a WebSocket URL (wss_host / wss_url / wssUrl / pipecat_url / url), and an optional dtmf flag.

Some deployments return this payload base64-encoded inside a top-level "data" field; the SDK decodes it automatically.

Used by EnxVoiceClient.connect().

End a call

DELETE {callApiBaseUrl}/v2token/{voiceId}

Returns HTTP 200 on success. Used by EnxVoiceClient.disconnect() (only if the call was successfully started).

Send DTMF digits

POST {callApiBaseUrl}/v2token/{voiceId}/dtmf
Content-Type: application/json

Request body:

{
  "digits": "<0-9, *, #>"
}

Returns HTTP 200 on success. Used by EnxVoiceClient.sendDtmf(digits).

Media WebSocket

After POST /v2token succeeds, the SDK opens a WebSocket connection to the returned URL and exchanges binary, protobuf-framed Pipecat messages: outgoing 16 kHz mono PCM microphone audio frames, and incoming bot audio frames plus JSON control events such as bot-started-speaking, bot-stopped-speaking, user-started-speaking, and user-stopped-speaking.

Low-Level Helpers advanced

These are exported for custom integrations and testing; most applications should use EnxVoiceClient instead of calling them directly.

FunctionDescription
encodePipecatFrame(frame): Uint8ArraySerializes a Pipecat frame object (audio/text/message) to the wire protobuf format.
decodePipecatFrame(bytes): PipecatFrameParses a raw WebSocket binary message into a typed Pipecat frame (audio, text, or message).
Usage Example

Usage Example

Implement the listener

Your app implements EnxVoiceListener to receive call state, speaking state, mute state, and error events.

import {
  EnxVoiceClient,
  type EnxVoiceListener,
  EnxVoiceState,
  EnxSdkConfig,
} from 'enx-voice-bot-react-native';

class MyVoiceListener implements EnxVoiceListener {
  onCallConnected() { /* update UI: call is live */ }
  onCallDisconnect() { /* update UI: call ended */ }
  onError(message: string) { /* show an error toast */ }
  onMuteStateChanged(isMuted: boolean) { /* toggle mute icon */ }
  onStatus(state: EnxVoiceState) { /* connecting / connected / disconnected / failed */ }
  onUserSpeaking(isUserSpeaking: boolean) { /* animate mic indicator */ }
  onBotSpeaking(isBotSpeaking: boolean) { /* animate bot indicator */ }
}

Create and configure the client

const client = new EnxVoiceClient()
  .init('911100123457')            // virtual number to dial
  .setEnxVoiceListener(new MyVoiceListener())
  .setSdkConfig(EnxSdkConfig.qa);   // or EnxSdkConfig.production

Connect and disconnect

// Start a call
await client.connect();

// Optionally override the REST base URL for this call only
await client.connect({ callApiBaseUrl: 'https://voiceagent.enablex.io' });

// End the call
await client.disconnect();

Mute and unmute the microphone

await client.muteAudio(true);   // mute
await client.muteAudio(false);  // unmute
client.isMuted();               // current mute state

Send DTMF digits

If the bot session supports a DTMF keypad, the listener's onDtmfKeypadChanged callback (set via setOnDtmfKeypadChanged) is notified so your app can show a keypad. Digits are sent with:

const sent = await client.sendDtmf('123#');

sendDtmf() accepts digits 0–9, * and #, and returns false if there is no active call session or the input is invalid.

React to speaking state

While a call is active, onUserSpeaking(true/false) and onBotSpeaking(true/false) fire as the bot's voice-activity detector toggles. The SDK automatically pauses microphone upload while the bot is speaking and resumes it shortly after the bot stops, to avoid echo/overlap.

Feature Support

Feature Support (v1.0.0)

FeatureSupportedNotes
Outbound voice bot callingYesSingle REST call (POST /v2token) returns a voice session ID and a WebSocket URL.
Real-time full-duplex audioYesWebSocket streaming using Pipecat protobuf frames (16 kHz mono microphone upload, bot audio playback).
Native audio bridgeYesAndroid and iOS native modules handle low-latency microphone capture and speaker playback.
Automatic permission handlingYesMicrophone permission requested/verified automatically via react-native-permissions before a call starts.
Mute / unmuteYesmuteAudio() / isMuted() during an active call.
DTMF digit sendingYesFor calls where the bot indicates a keypad is available.
Speaking-state callbacksYesonUserSpeaking / onBotSpeaking so the host app can show speaking indicators.
Selectable environmentsYesBuilt-in QA and Production, or a fully custom environment via EnxSdkConfig.fromEnvironment().
Prebuilt call UI (screens/components)NoThe package is a headless call-control client — your app builds its own call screen from the listener callbacks.
Inbound call handlingNoThe SDK only initiates outbound calls to a virtual number.
Multiple concurrent callsNoOne active call per EnxVoiceClient instance; create a new instance (or fully disconnect) before starting another call.
Reference

Known Issues & Limitations

  • Outbound calls only — the SDK initiates calls to a virtual number; there is no inbound call handling.
  • One active call per EnxVoiceClient instance — create a new instance (or fully disconnect) before starting another call.
  • No automated test suite is published with this release (npm test is a placeholder).
  • DTMF sending requires the bot session to report keypad support; otherwise sendDtmf() returns false.
  • Verbose debug logging is enabled by default under the QA configuration; use EnxSdkConfig.production or set enableLogging: false for release builds.

Developer Troubleshooting

SymptomLikely cause / fix
connect() fails immediately with a permission errorMicrophone permission was denied, or the platform usage-description (Info.plist / manifest) is missing. Prompt the user to enable it in system settings.
“callApiBaseUrl is not set” errorNo environment was configured and no callApiBaseUrl was passed to connect(). Call setSdkConfig() or pass callApiBaseUrl explicitly.
No audio heard on AndroidConfirm MODIFY_AUDIO_SETTINGS is declared and the device is not in silent/Do-Not-Disturb mode with media muted.
sendDtmf() always returns falseThere is no active call, or the bot session did not enable DTMF for this call.
Call connects but disconnects immediatelyCheck onError() output and server-side logs; the WebSocket endpoint returned an error or closed the session.

Upgrade Notes

  • This is the first published version; there are no breaking changes to migrate from.
  • When upgrading in the future, re-run pod install in the iOS project after bumping the package version, since native iOS sources ship inside the pod.
  • Review EnxSdkConfig usage before release builds — use EnxSdkConfig.production (or fromEnvironment with enableLogging: false) to disable verbose SDK logging.