React Native SDK
The native drop-in flow for React Native apps on Android and iOS.
Status: release candidate, not yet on npm. The package and its native SDKs (
ai.sekyro:*0.1.0-rc.2 on Maven Central,SekyroKit0.1.0-rc.2 onsekyro-ios-dist) are published together; until then, Ibtikar shares a packed tarball (npm pack) for pilots.
@sekyro/react-native runs the Sekyro AI native drop-in (consent, document capture, NFC chip, liveness, phone or
e-mail OTP, registry) full screen from a React Native app and returns a display-only result. It is a thin
TurboModule over the binary Android and iOS SDKs: every capture, network and UI line lives in the native SDKs, so a
React Native app gets exactly the same flow as a native one.
- React Native 0.76 or later, New Architecture only (the default since 0.76). No Expo Go (it cannot load native
modules); Expo apps use a development build or
expo prebuildwith the config plugin below. - Android minSdk 24, iOS 15.1.
- The client token comes from your server (
@sekyro/nodesessions.create). API keys never ship in an app. - Results are for display. Decide on your server from webhooks or
sessions.get(see quickstart §4).
1. Install
Release candidate
The React Native SDK is a release candidate. Chip reading (NFC) and email codes in the drop-in flow are staging-only for now: see Availability.
npm install @sekyro/react-nativeAndroid
The native SDK comes from Maven Central (ai.sekyro:sekyro-bom + ai.sekyro:sekyro-ui, version pinned by the
npm package), so mavenCentral() must be in your repositories (it is in every React Native template). Set
minSdkVersion to 24 or more. Nothing else: the SDK's manifest merges the CAMERA and NFC permissions, the
optional camera/NFC features and its SekyroActivity. Your MainActivity must be a ReactActivity (any
ComponentActivity works).
Add one packaging block to android/app/build.gradle (the chip reader's Bouncy Castle jars and jspecify ship
duplicate META-INF files; without it mergeDebugJavaResource fails with "2 files found with path
'META-INF/LICENSE.md'"):
android {
packaging {
resources {
excludes += ["META-INF/versions/9/OSGI-INF/MANIFEST.MF"]
pickFirsts += ["META-INF/LICENSE.md", "META-INF/NOTICE.md", "META-INF/LICENSE.txt", "META-INF/NOTICE.txt"]
}
}
}iOS
SekyroKit is not on CocoaPods trunk. Declare where it comes from once in your Podfile, after
use_native_modules!:
require Pod::Executable.execute_command('node', ['-p',
'require.resolve("@sekyro/react-native/package.json", {paths: [process.argv[1]]})', __dir__]
).strip.sub(/package\.json$/, 'scripts/sekyro_pods.rb')
target 'YourApp' do
config = use_native_modules!
sekyro_kit_pod! # SekyroKit from github.com/IBTIKAR-Technologies/sekyro-ios-dist at the pinned version
# ...
endThen cd ios && pod install. sekyro_kit_pod! installs the signed SekyroKit.xcframework named by the dist
repo's podspec (its download is checksum-verified). Static or dynamic frameworks (use_frameworks!) both work.
Info.plist (camera always; NFC only if you read document chips):
| Key | Value |
|---|---|
NSCameraUsageDescription | e.g. "The camera is used to capture your face and your identity document for verification." |
NFCReaderUsageDescription | e.g. "NFC is used to read the chip of your passport or identity card." |
com.apple.developer.nfc.readersession.iso7816.select-identifiers | A0000002471001, A0000002472001 |
Capabilities (Xcode → Signing & Capabilities): App Attest (entitlement
com.apple.developer.devicecheck.appattest-environment = production for TestFlight / App Store) and, for chip
reading, Near Field Communication Tag Reading (com.apple.developer.nfc.readersession.formats = TAG).
Expo (development builds / prebuild)
{ "expo": { "plugins": [["@sekyro/react-native", { "nfc": true, "appAttest": "production" }]] } }Options: nfc (default true), appAttest ("production" default, "development", or false),
cameraPermission and nfcPermission (your usage strings). The plugin writes the Info.plist keys, the NFC AIDs, the
entitlements and the sekyro_kit_pod! Podfile line (idempotent), and fails the prebuild when Android minSdkVersion
(expo-build-properties) is below 24. Unknown options are an error.
2. Register the app (Sekyro dashboard → Apps → your app → Platforms)
Device attestation (Google Play Integrity / Apple App Attest) is described in
native-attestation.md. For a React Native app register the same identifiers as a native
app: the Android applicationId with its signing-certificate SHA-256 digests (Play app signing key, upload key,
debug key for internal builds) and the iOS bundle id with your Team ID.
3. Start a verification
import { startSekyro, SekyroError } from '@sekyro/react-native';
async function verify(clientToken: string) {
try {
const result = await startSekyro({
clientToken, // from your server, one per session
locale: 'fr', // 'ar' | 'fr' | 'en'; default: the session's / device language
theme: { colors: { brand: '#0B6E4F' }, radius: 12 },
onEvent: (e) => analytics.track('kyc_step', e), // no personal data in events
});
switch (result.status) {
case 'finished': // result.sessionId, result.displayStatus: 'approved' | 'review_pending' | 'rejected'
break;
case 'cancelled': // the person closed the flow
break;
case 'failed': // result.code from the Sekyro error catalog (e.g. 'session_expired', 'session_token_invalid')
break;
}
} catch (e) {
if (e instanceof SekyroError) {
// Programmer errors only: 'invalid_config' | 'flow_in_progress' | 'no_activity' | 'unsupported_platform'
}
}
}Or declaratively — <SekyroFlow> presents the flow when it mounts and renders nothing. Mount it once per session
(remount with a new clientToken to restart):
import { SekyroFlow } from '@sekyro/react-native';
{token ? <SekyroFlow clientToken={token} locale="ar" onResult={(r) => setResult(r)} /> : null}- What the person sees follows your app's eKYC profile, in three sections: contact (phone/email code, or one
of them by choice), identity (national eID, NNI, document photo or chip, then an optional summary to confirm),
live photo. See the flow model. The identity summary stays
on the device screen: it is never in
onEvent, the result or webhooks. - One flow at a time. A second
startSekyrowhile one runs rejectsflow_in_progress. apiOriginis only for Ibtikar staging tests (https://api-staging.sekyro.ai); leave it unset in production.- An expired session resolves
{ status: 'failed', code: 'session_expired' }; create a new session. unsupported_platform: the native module is not linked (Expo Go, web, a missingpod install, or an app with the New Architecture turned off).
4. Events
onEvent receives { type: 'step', step, retryReason? } when the screen changes, and { type: 'waiting_for_network' }
while the SDK waits for connectivity. Steps: consent, method, phone_otp, email_otp, registry, document,
nfc, liveness, processing, result, update_required. retryReason is a catalog code (for example
liveness_retry, chip_read_failed) when the person is asked to try again. Events never contain personal data, the
token or a decision; use them for analytics and your own progress UI only.
5. Theming and copy
theme field | Android | iOS |
|---|---|---|
colors.brand, brandFg, bg, surface, fg, fgMuted, danger, success, warning (#RRGGBB) | yes | yes |
colors.border | ignored in 0.1 | yes |
radius (0–32, dp / pt) | yes | yes |
fontFamily (a font bundled in your app) | ignored in 0.1 | yes |
darkMode ('system' | 'light' | 'dark') | yes | yes |
The runtime theme wins over the branding set in the dashboard, which wins over Sekyro's defaults.
strings overrides copy by catalog key, per locale. It needs locale (only the active locale's map is sent):
startSekyro({ clientToken, locale: 'fr', strings: { fr: { 'consent.title': 'Vérification du client' } } });Named placeholders such as {channel} are filled in your text. Unknown keys are ignored.
6. Test it
The sample app sekyro-mobile/examples/react-native-sample (in the SDK distribution) takes a hosted link
(https://verify-staging.sekyro.ai/fr/abc-defg-hjk or https://verify.sekyro.ai/...), starts the flow and lists the
events and the display result. Create the session from your server or the dashboard and paste its link.