Skip to main content

React Native SDK

The Kora IDV React Native SDK is a thin JS wrapper over the native iOS and Android SDKs. All camera, ML, and verification UI runs natively for performance and access to platform capabilities (CameraX, AVFoundation, ML Kit, NFC). Your JS layer just calls a single bridge method and gets back a typed result.

Requirements

  • React Native 0.72+ (Old or New Architecture)
  • iOS 14.0+ deployment target
  • Android minSdk 24+
  • Bare React Native CLI or Expo with prebuild (expo prebuild). Expo Go is not supported — the SDK uses native modules.

Installation

npm install @koraidv/react-native
# or
yarn add @koraidv/react-native

iOS

cd ios && pod install

Add the camera permission to ios/<YourApp>/Info.plist:

<key>NSCameraUsageDescription</key>
<string>We need your camera to verify your identity.</string>

If you also enable NFC passport reading, add:

<key>NFCReaderUsageDescription</key>
<string>We use NFC to verify the chip in your passport.</string>
<key>com.apple.developer.nfc.readersession.iso7816.select-identifiers</key>
<array>
<string>A0000002471001</string>
</array>

Android

The package autolinks. After installing, rebuild:

npx react-native run-android

Add to android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-feature android:name="android.hardware.camera" android:required="true" />

The SDK requests camera permission at runtime if not already granted.

Expo

If you use Expo with custom dev clients (not Expo Go):

npx expo install @koraidv/react-native
npx expo prebuild
npx expo run:ios
npx expo run:android

The package ships an Expo config plugin that wires the iOS Info.plist keys and Android manifest entries automatically.

Quick start (React)

The recommended path is the KoraIDVProvider + useKoraIDV hook combination. Wrap the part of your app that needs verification:

import { KoraIDVProvider } from '@koraidv/react-native';

export default function App() {
return (
<KoraIDVProvider
apiKey="kora_sandbox_xxxxx"
tenantId="your-tenant-uuid"
config={{ environment: 'sandbox' }}
>
<RootNavigator />
</KoraIDVProvider>
);
}

Then trigger verification from any screen:

import { useKoraIDV } from '@koraidv/react-native';

function VerifyScreen() {
const {
startVerification,
verification,
error,
isLoading,
isCancelled,
reset,
} = useKoraIDV();

const onPress = () => {
startVerification(
`user-${userId}`, // your own external identifier
'standard', // tier
);
};

if (isLoading) return <Text>Verifying…</Text>;

if (verification) {
return (
<View>
<Text>Status: {verification.status}</Text>
<Text>Risk score: {verification.riskScore}</Text>
</View>
);
}

if (error) {
return (
<View>
<Text>Error: {error.code}</Text>
<Button title="Retry" onPress={reset} />
</View>
);
}

if (isCancelled) {
return (
<View>
<Text>You cancelled. Try again?</Text>
<Button title="Restart" onPress={reset} />
</View>
);
}

return <Button title="Verify identity" onPress={onPress} />;
}

Headless component

For a one-shot flow with callbacks, use <VerificationFlow> — it auto-starts on mount and renders nothing in JS:

import { KoraIDVProvider, VerificationFlow } from '@koraidv/react-native';

function VerifyOnce() {
return (
<KoraIDVProvider apiKey="" tenantId="">
<VerificationFlow
externalId={`user-${userId}`}
tier="standard"
onComplete={(v) => console.log('done', v.status, v.riskScore)}
onError={(e) => console.error(e.code, e.message)}
onCancel={() => console.log('user cancelled')}
/>
</KoraIDVProvider>
);
}

Imperative API

For non-React contexts (sagas, redux thunks, native code paths), use the singleton:

import { KoraIDV } from '@koraidv/react-native';

KoraIDV.configure({
apiKey: 'kora_sandbox_xxxxx',
tenantId: 'your-tenant-uuid',
environment: 'sandbox',
});

const result = await KoraIDV.startVerification(`user-${userId}`, 'standard');
if (result.type === 'success') {
console.log(result.verification.status);
} else {
console.log('cancelled');
}

Configuration

type KoraIDVConfiguration = {
apiKey: string; // kora_sandbox_… or kora_live_…
tenantId: string; // your tenant UUID
environment?: 'sandbox' | 'production'; // auto-detected from key prefix
baseUrl?: string; // override (on-prem only)
livenessMode?: 'active' | 'passive';
theme?: Theme;
timeout?: number; // network timeout in ms (default 120000)
debugLogging?: boolean;
};

Pass anything beyond apiKey and tenantId via the config prop on the provider:

<KoraIDVProvider
apiKey=""
tenantId=""
config={{
environment: 'production',
livenessMode: 'active',
theme: { primaryColor: '#0D9488', backgroundColor: '#FFFFFF' },
debugLogging: __DEV__,
}}
>
<App />
</KoraIDVProvider>

Verification tiers

TierIncludes
basicDocument OCR + basic authenticity
standard+ Face match + active liveness
enhanced+ Anti-spoof + risk signals + compliance screening (sanctions / PEP / adverse media)

Pick the tier that matches the regulatory requirement for the user action.

Result shape

type Verification = {
id: string;
externalId: string;
tenantId: string;
tier: string;
status:
| 'pending'
| 'document_required'
| 'selfie_required'
| 'liveness_required'
| 'processing'
| 'approved'
| 'rejected'
| 'review_required'
| 'expired';
documentVerification: DocumentVerification | null;
faceVerification: FaceVerification | null;
livenessVerification: LivenessVerification | null;
riskSignals: RiskSignal[] | null;
riskScore: number | null; // 0–100 overall
createdAt: string;
updatedAt: string;
completedAt: string | null;
};

Per-feature scores live inside the nested objects: documentVerification.documentAuthenticity, faceVerification.matchScore, livenessVerification.score, etc. The single top-level riskScore is the fused 0–100 number you'd typically display.

The complete VerificationFlowResult is a tagged union:

type VerificationFlowResult =
| { type: 'success'; verification: Verification }
| { type: 'cancelled' };

Errors (network failures, denied permissions, expired sessions) are thrown as KoraError from startVerification / resumeVerification.

Resuming a verification

If a user closes your app mid-flow, you'll get a verificationId from your webhook (or from a previous run's verification.id). Resume with:

const { resumeVerification } = useKoraIDV();
await resumeVerification(existingVerificationId);

Error handling

import { KoraError, KoraErrorCode } from '@koraidv/react-native';

try {
await KoraIDV.startVerification(externalId, 'standard');
} catch (err) {
if (err instanceof KoraError) {
switch (err.code) {
case KoraErrorCode.NETWORK_ERROR:
showRetry(err.message);
break;
case KoraErrorCode.CAMERA_ACCESS_DENIED:
promptCameraPermission();
break;
case KoraErrorCode.SESSION_EXPIRED:
// Start a fresh verification
break;
case KoraErrorCode.NOT_CONFIGURED:
// KoraIDV.configure was never called
break;
}
}
}

ProGuard / R8 rules (Android)

If R8 is enabled in your release build, add to android/app/proguard-rules.pro:

-keep class com.koraidv.sdk.** { *; }
-keepclassmembers class com.koraidv.sdk.** { *; }

Server-side webhook

The mobile flow returns the verification result directly, but you should also configure a webhook endpoint to receive the authoritative verification.completed event server-side (mobile clients can't be trusted to report failures). See Webhooks.

Wallet (W3C Verifiable Credentials)

The SDK also ships a wallet module for storing and presenting credentials with selective disclosure. Import from the same package:

import { KoraWallet, DisclosureProfiles } from '@koraidv/react-native';

Wallet documentation: see the KoraWallet API reference.

Common issues

IssueFix
The package '@koraidv/react-native' doesn't seem to be linkedRun pod install (iOS) and rebuild (npx react-native run-android / run-ios). Don't use Expo Go.
Black camera preview on AndroidConfirm <uses-permission android:name="android.permission.CAMERA" /> is in your manifest and runtime permission was granted.
NOT_CONFIGURED errorKoraIDV.configure() (or wrapping in KoraIDVProvider) must complete before any startVerification call.
Sandbox API key returning 401Verify the key is kora_sandbox_… and you didn't override environment to 'production'.
iOS build fails with "Module 'KoraIDV' was not built for...":Clean the iOS derived data: cd ios && rm -rf build Pods Podfile.lock && pod install.

Next steps