Dashboard

React Native SDK

@sentriq/react-native is a thin TurboModule bridge over the existing native iOS (Swift) and Android (Kotlin) Sentriq SDKs — it never reimplements transport, installation ID, session logic, or device-signal collection; every event still reaches POST /v1/events through the exact same native client the platform-specific SDKs use. Works in both a bare React Native app and an Expo Development Build.

Requirements

  • React Native 0.76+ (the New Architecture / TurboModules — this SDK ships no legacy-bridge fallback)
  • iOS 13.0+, Android minSdk 24
  • Node 22.13+

Install

Not yet published to npm — install from a checked-out commit of this repository until a release process exists:

cd packages/react-native-sdk && npm pack
# then, in your app:
npm install /path/to/sentriq-react-native-0.1.0.tgz

Autolinking picks it up automatically — no manual native linking step. iOS additionally needs a pod install (cd ios && pod install) since this SDK ships a CocoaPods podspec, the current standard mechanism for React Native iOS native modules.

Configure

import { Sentriq } from '@sentriq/react-native';

Sentriq.configure({ publicKey: 'pub_test_xxx' });

Call this once, typically at app startup. Safe to call again later — the latest call wins, matching both native SDKs' own re-configure behavior.

Never embed a secret key in a mobile app
Anything inside a shipped app bundle is public. SentriqOptions has no secret-key field at all — the underlying native SDK rejects a sec_... value immediately with a CONFIGURATION error.

Track an event

import { Sentriq, SentriqError } from '@sentriq/react-native';

async function handleLogin(userId: string) {
  try {
    const result = await Sentriq.track('login', {
      account: { id: userId },
      screenName: 'login-screen',
    });
    // result.risk.decision: "allow" | "monitor" | "challenge" | "block"
  } catch (error) {
    const e = error as SentriqError;
    // e.code: "CONFIGURATION" | "VALIDATION" | "NETWORK" | "TIMEOUT" | "CANCELLED" | "DECODING" | "API" | "NATIVE_MODULE_UNAVAILABLE"
  }
}

Account association

Pass account: { id: ... } to track() once you know who the current user is — your own opaque reference, never a password or email. Omit it for anonymous events.

Installation ID and session — no JS-side duplicate

This SDK has no JavaScript-side installation or session identifier of its own — the underlying native SDK (Keychain-backed on iOS, SharedPreferences-backed on Android) remains the sole authority, exactly as it is for a native-only integration. Sentriq.getInstallationId() reads it read-only, for debugging.

Native architecture

Built on TurboModules/Codegen (the React Native New Architecture) — not the legacy bridge. The generated native interface is implemented by a thin Swift class (iOS) and a thin Kotlin class (Android), each doing nothing but validate/convert arguments and delegate straight into the real native SentriqClient. See docs/architecture/REACT_NATIVE_SDK.md in the repository for the full bridge design.

Error handling

Every rejecting call throws a SentriqError with a stable code string, mapped consistently from the underlying native SDK's own typed error on both platforms:

CONFIGURATION | VALIDATION | NETWORK | TIMEOUT | CANCELLED | DECODING | API | NATIVE_MODULE_UNAVAILABLE

API/VALIDATION errors additionally carry status, serverCode, requestId, and (validation only) fields.

Custom API URL (local/dev)

Sentriq.configure({
  publicKey: 'pub_test_xxx',
  apiUrl: 'http://10.0.2.2:8000', // Android emulator's alias for your host's localhost
});

Expo

@sentriq/react-native works directly in an Expo Development Build — there is no separate Expo package and no config plugin. A Development Build (produced by expo prebuild) is a real bare React Native project where standard autolinking already works; this SDK needs no Info.plist/AndroidManifest.xml edits beyond what autolinking provides automatically.

Expo Go: not supported
Expo Go only bundles Expo's own pre-compiled native modules — it has no mechanism to load this SDK's custom native code. Calling any Sentriq method inside Expo Go throws NATIVE_MODULE_UNAVAILABLE with a clear message, rather than silently doing nothing. Use a Development Build instead.
npx expo prebuild
npx expo run:android   # or: npx expo run:ios

Read the public key from Expo's own client-safe env convention:

// app.config.js / .env
EXPO_PUBLIC_SENTRIQ_PUBLIC_KEY=pub_test_xxx
Sentriq.configure({
  publicKey: process.env.EXPO_PUBLIC_SENTRIQ_PUBLIC_KEY!,
});

Any EXPO_PUBLIC_* name is inlined into the JS bundle at build time — the same public/private boundary idea as Next.js's NEXT_PUBLIC_*. Never put a secret key behind this prefix.

Expo web is explicitly unsupported — every method throws NATIVE_MODULE_UNAVAILABLE rather than silently invoking a native API that doesn't exist on web. An Expo web app wanting Sentriq should use @sentriq/browser directly.

Privacy

This bridge collects no data of its own — every signal (installation ID, session, device metadata) is owned entirely by the underlying native SDK, with the same privacy guarantees documented on the iOS and Android pages.

Runtime verification limitations

  • iOS: bridge source is written and reasoned through carefully, but build/runtime is not verified — no Xcode/macOS was available in the environment this SDK was built in.
  • Android: the JS↔native bridge, Codegen, and the native Kotlin module all genuinely build, and the bridge module's own unit tests (including a real event sent to a real API) pass. Full end-to-end JS-engine (Hermes) runtime execution on a real device/emulator was not verified — no emulator was available.

Troubleshooting

  • NATIVE_MODULE_UNAVAILABLE on every call — you're running inside Expo Go (use a Development Build) or a stale build (rebuild after installing this package).
  • Gradle can't resolve io.sentriq:sentriq-android — this SDK isn't published to Maven Central yet; add mavenLocal() to your app's android/build.gradle and run ./gradlew publishToMavenLocal from sdks/android first. With Expo, expo prebuild regenerates android/build.gradle on every run, so this needs re-adding each time until a real publish exists.