React Native SDK
The Orbit React Native SDK (@devotel-orbit/react-native) embeds Orbit’s end-user-facing surfaces into iOS and Android apps: in-app chat (the same omnichannel conversation/inbox the web widget uses), owned in-app messages and content cards, and video rooms. It ships a headless OrbitChatClient plus a useOrbitChat hook, an OrbitInAppClient for the in-app feed, and an engine-injected OrbitVideoRoomClient for video rooms.
This is a client SDK: it authenticates only with a publishable key (dv_live_pk_…), because it ships inside a distributed app binary. It intentionally does not wrap the server-side management resources (messaging, voice, contacts, campaigns, verify) — those require a secret key and belong in your backend, via the Node SDK or the REST API. See Client / mobile SDKs (different scope, by design) on the SDK index.
Pre-publish — source-only. The React Native SDK is not yet on npm —
the install line below fails today. Until first publish, vendor the
source from the monorepo (
packages/sdk-react-native/) or build against
the same conversations API the web widget uses.Scope matrix
The division is deliberate: no surface in this package accepts a secret key, so nothing in your shipped binary can be turned into a full-account credential by extraction. Server-side resources stay server-side.
Installation
react and react-native are optional peer dependencies (>=17.0.0 / >=0.70.0) — needed only if you use the hook, and marked optional so headless-only consumers never resolve the types.
Integration order
- Construct — with the publishable key, an optional storage adapter, and an
appInfotag. Misuse fails at the constructor, not the first request. - Subscribe —
chat.on("message", …),"messages","typing","connection","error"— beforeconnect()so you never miss the initial history reload. Each returns an unsubscribe function. await chat.connect()— restores or creates the conversation, loads history, and opens the real-time stream.- Send — optimistic local echo first; the
Idempotency-Keyheader pins retries, andgenerateIdempotencyKeyis exported for your own retry loops. - Tear down —
chat.dispose()stops the stream and releases the http client; the hook does this for you on unmount.
The useOrbitChat hook
The hook subscribes a screen to an Orbit conversation: it connects on mount, tears down on unmount, and returns the live message list, connection status, typing indicator, and send methods.
Headless chat client (OrbitChatClient)
Prefer the hook for screens; use the headless client directly for non-React code paths. Pass a storage adapter (for example@react-native-async-storage/async-storage satisfies the shape unmodified) to keep the conversation across app launches.
chat.on(...) returns an unsubscribe function; call chat.disconnect() to stop the real-time stream and retain the conversation id, or chat.dispose() to release everything.
In-app messages and content cards (OrbitInAppClient)
Owned in-app surfaces — assembled server-side into a per-visitor feed, ordered client-side by the exportedassembleInAppFeed (same rules the server applies, capped at MAX_CONTENT_CARDS), rendered locally, with impression / click / dismiss reported back. Dismissals persist across launches through the storage adapter.
reportEvent takes the InAppEventType union ("impression" | "click" | "dismiss") — the compiler rejects anything else, and the server 422s what
slips through. The closed set and the per-language fix are in
InAppEventType: the in-app event enum.
Video rooms (OrbitVideoRoomClient)
Headless and engine-injected: your backend mints the room token server-side (POST /api/v1/video/rooms/:id/join) and hands the token plus server URL to the app. Wire a VideoRoomEngine over your native WebRTC stack (for example @livekit/react-native) — the package carries no hard WebRTC dependency, so you never vendor a second WebRTC build beside the one you already use. Inbound WebRTC video only.
computeGridColumns picks tile-grid columns like the web room does, resolveTileLabel guarantees a non-empty participant label, and parseCaptionPayload / mergeCaption collapse interim→final caption segments the way Zoom/Meet do. Your tile grid matches the web room exactly.
Push token onboarding (backend)
Registering the device’s APNs (iOS) or FCM (Android) token is a backend call againstPOST /api/v1/push/device-tokens, run with a secret key via the
Node SDK or plain HTTP:
Runnable examples
The package’sexamples/ directory holds self-contained end-to-end TypeScript files — each takes the publishable key (dv_test_pk_… / dv_live_pk_…) as the first argument so swap sandbox vs live by changing the key: chat-connect.ts (OrbitChatClient connect + send with OrbitApiError.status handling, and the @react-native-async-storage/async-storage adapter injected) and in-app-feed.ts (OrbitInAppClient.fetchFeed() + reportEvent impression + dismiss(surfaceId)). See packages/sdk-react-native/examples/README.md; the process.env fallback only helps when you run the sample under Node — in a real app pass the key inline.
Error taxonomy
Version notes
@devotel-orbit/react-native 0.1.1 pairs @devotel-orbit/web’s request envelope whole-file; the source pins in src/__tests__/ keep the wire contract identical to the web widget across versions. The 0.x line is pre-publish, so minor releases may adjust surface details — pin package-lock.json accordingly. The runtime targets every RN engine >=0.70 (Hermes/JSC), with an AbortSignal.timeout fallback to a manual controller.
Only the publishable key (dv_live_pk_… / dv_test_pk_…) may be embedded in the app — the client refuses a secret key at construction, because anything bundled in a distributed binary is extractable by any user. Real-time updates use header-authenticated polling, so the key never lands in a URL log.