Documentation menu
React Native
Install blink-kyc-react-native, create a session token on your backend, let the
SDK run the document and liveness capture on Blink's native screens, then read the verified result from
your backend. The package is a thin TypeScript bridge over the Blink Android and iOS SDKs, so the capture
screens, the on-device checks and the upload are the same ones the native SDKs ship.
00How it works
Three parties, three calls. Your client secret lives only on your backend; the phone only ever holds a short-lived session token; and the result you act on is the one your backend reads from Blink.
Create a session
POST /api/blink/session/create with your client key and secret. Returns
sessionId and sessionToken.
Run the capture
Pass the sessionToken to BlinkKyc.present. The native screens take over,
photograph the document, run the liveness check and upload to Blink.
Read the result
GET /api/blink/session/{sessionId}/result. Act on
VERIFIED, REJECTED or REVIEW.
Never put the client secret in the app. Anything shipped in a JavaScript bundle can be extracted. The app asks your backend for a session token, and your backend asks Blink.
01Install the package
blink-kyc-react-native is served from the Blink artifact site, not the public npm
registry. Install it from its tarball URL; npm records the URL in package.json and its
checksum in package-lock.json:
npm install https://blink-pay.net/maven/npm/blink-kyc-react-native-1.0.13.tgzThe package is a TurboModule and needs the React Native new architecture, which is the default since
0.76. It is built and verified against React Native 0.87. There is no config plugin: Expo projects need a
development build (npx expo prebuild), because Expo Go cannot load native code.
import { BlinkKyc, BlinkKycError } from 'blink-kyc-react-native';02Android & iOS setup
Android
The module depends on the native Blink Android SDK, com.blink.kyc:blink-kyc-android, and adds
its repository, https://blink-pay.net/maven/releases, to every project of your Gradle build,
so a plain npx react-native run-android resolves it. If your settings.gradle
locks repositories (RepositoriesMode.FAIL_ON_PROJECT_REPOS), declare it there:
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven { url = uri("https://blink-pay.net/maven/releases") }
}
}Your app needs a minimum SDK of 24 (React Native's default). The camera, internet and NFC permissions, the capture activity and the hardware features are declared by the native SDK and merged into your manifest; nothing to add. Build with JDK 17 or 21.
iOS
The native Blink iOS SDK targets iOS 15.5, which is above React Native's own minimum, so set the platform
in your Podfile. The pod is a static framework and works with React Native's default static linkage:
use_frameworks! is not required.
platform :ios, '15.5' # instead of min_ios_version_supported
prepare_react_native_project!pod install downloads BlinkKyc.xcframework and its model bundle from the Blink
artifact site into the package (once per native SDK version; if an update leaves the build without it,
run pod install again) and pins the Swift dependencies the
binary was built with, TensorFlowLiteSwift, NFCPassportReader and lottie-ios. Add the camera
usage description to Info.plist:
<key>NSCameraUsageDescription</key>
<string>We use the camera to photograph your document and confirm your identity.</string>Passport chip reading is off in this release. The ePassport NFC step is built into the native SDKs but disabled until the on-device reader has been tested, so no NFC usage description or entitlement is needed. A free personal Apple team can sign the app; document photo plus liveness work in full.
03Backend: create a session
Expose an endpoint on your backend that your app calls when the user starts verification. It authenticates your user, calls Blink with your client credentials, and returns only the session token and id. The backend side is identical for every SDK; the Flutter guide carries Node.js and Dart examples of the endpoint, and the Session API reference has every field.
curl -sX POST https://kyc-api.blink-pay.net/api/blink/session/create \
-H 'content-type: application/json' \
-d '{
"clientKey": "bkyc_live_…",
"clientSecret": "bksec_…",
"purpose": "FULL_CAPTURE",
"userRef": "customer-8841"
}'
→ 200
{
"sessionId": "[SESSION_ID]",
"sessionToken": "bkyc_sess_…",
"expiresAt": "2026-09-15T18:42:10Z",
"purpose": "FULL_CAPTURE"
}Store sessionId against your user — you need it to read the result. Sessions are
short-lived: create one when the user taps Verify, not ahead of time. Only the token and id go
to the phone.
04App: run the capture
Ask your backend for a session, hand the token to BlinkKyc.present, and await the device-side
result. The native screens own the camera: document detection, auto-capture, on-device MRZ reading and
the action-guided liveness check all run there, and the promise settles when the customer is done.
import { BlinkKyc, BlinkKycError } from 'blink-kyc-react-native';
const BACKEND = 'https://api.yourbank.com';
export async function startVerification(userJwt: string): Promise<void> {
// 1. Your backend creates the Blink session (section 03).
const res = await fetch(`${BACKEND}/kyc/sessions`, {
method: 'POST',
headers: { authorization: `Bearer ${userJwt}` },
});
const session: { sessionId: string; sessionToken: string } = await res.json();
try {
// 2. The native SDK runs document + liveness capture and uploads to Blink.
const result = await BlinkKyc.present({
sessionToken: session.sessionToken,
issuingCountry: 'SDN', // SDN or ARE; omit to let the session decide
documentType: 'PASSPORT', // omit to let the session decide
locale: 'en', // or 'ar' for Blink's built-in Arabic screens
});
// 3. Show progress to the user, but decide nothing here —
// ask your backend, which reads the authoritative result (section 05).
console.log('device copy:', result.verdict); // 'verified' | 'rejected' | 'review'
await fetch(`${BACKEND}/kyc/sessions/${session.sessionId}/complete`, {
method: 'POST',
headers: { authorization: `Bearer ${userJwt}` },
});
} catch (e) {
if (e instanceof BlinkKycError) {
// The user cancelled or a step failed. Switch on e.code, not the message.
if (e.code === 'BLINK_CAPTURE_CANCELLED') return; // user closed the capture
console.warn(e.code, e.message);
} else {
throw e;
}
}
}Configuration
BlinkKyc.present(config) takes one object. Only the token is required; everything else
defaults to letting the session decide.
| Field | Type | Meaning |
|---|---|---|
sessionToken | string | Required. The short-lived token from your backend. Never a client key or secret. |
baseUrl | string | The Blink API base URL. Defaults to https://kyc-api.blink-pay.net. |
issuingCountry | 'SDN' | 'ARE' | The document's issuing country. Omit to let the session decide. |
documentType | BlinkDocumentType | 'PASSPORT', 'NATIONAL_ID', 'DRIVING_LICENCE', 'VISA' or 'VEHICLE_LICENCE'. Omit to let the session decide. |
locale | 'en' | 'ar' | The language of the capture screens. Defaults to the device locale. |
rejectExpiredDocuments | boolean | Stop an expired document on the device before upload. Defaults to true. |
theme | BlinkTheme | Colours, sizes, positions and images for the native screens. Omit for the built-in look. See Theme below. |
The promise resolves with { verdict, detail? }, where verdict is
'verified', 'rejected' or 'review' and detail is a
neutral one-line summary for your logs. It rejects with BlinkKycError, whose
code is the native error code (section 06).
The session's policy wins. When the capture starts, the SDK reads the
session's required steps and allowed documents from Blink. issuingCountry and
documentType are hints that fill gaps — a step the session requires cannot be switched off
in the app.
Theme
theme styles the native screens. Every field is optional and keeps the built-in look when
left out. Colours are ARGB numbers, as in the native SDKs:
const result = await BlinkKyc.present({
sessionToken,
theme: {
accent: 0xff15803d,
brandName: 'Your Bank',
logo: { resource: 'bank_logo' }, // Android drawable / iOS asset-catalogue name
scrimColor: 0xff0e1e4d, // the dimmed area outside the document frame
scrimOpacity: 0.35, // 0 clear … 1 opaque; 0.6 unless set
guideSuccessColor: 0xff22c55e,
},
});The fields are the native BlinkTheme's, listed with what each one changes in
Customisation. An image is { resource: 'name' } or
{ file: '/path/to/image.png' }. An invalid value rejects with BLINK_CONFIG.
Session QR codes and hosted links
If your dashboard or backend produces a session QR or hosted link
(https://kyc-api.blink-pay.net/h/?t=bkyc_sess_…), read the token out of it locally and pass
it as sessionToken. Parse, never open, the link: take the sessionToken,
session_token or t query parameter, or the token itself when the code holds a
bare bkyc_sess_… value, and only accept links for the API host you expect. Any QR scanner
works; the Blink sample app uses react-native-camera-kit.
05Backend: read the result
This is the only verdict to act on. The value present returns on the phone is a convenience
copy that a modified app could fake; your backend reads the real one, server to server.
curl -s https://kyc-api.blink-pay.net/api/blink/session/[SESSION_ID]/result \
-H 'X-Blink-Client-Key: bkyc_live_…' \
-H 'X-Blink-Client-Secret: bksec_…'
→ 200
{
"sessionId": "[SESSION_ID]",
"status": "COMPLETED",
"result": "VERIFIED",
"resultJson": "{\"sessionId\":\"[SESSION_ID]\",\"purpose\":\"FULL_CAPTURE\",\"result\":\"VERIFIED\",\"faceScore\":0.91,\"detail\":\"…\"}"
}Only COMPLETED carries a result; route REVIEW to a person, and re-read results
from a background job for users who close the app mid-way. The
Result API reference describes every field, and the
Documents API is how your backend downloads the stored document
scans and selfies — the SDK does not hand images back to the app.
06Errors
BlinkKyc.present rejects with a BlinkKycError. Switch on code,
never on the message text.
| Code | What to do |
|---|---|
BLINK_CAPTURE_CANCELLED | The user closed the capture. Not an error to report. |
BLINK_CAMERA_DENIED | Explain why the camera is needed and link to Settings. |
BLINK_DOCUMENT_EXPIRED | The document's expiry date has passed. The SDK told the user, sent the photograph so the session records it, and closed when they pressed the button; offer a new attempt with a new session. |
DOCUMENT_UNREADABLE · LIVENESS_FAILED · … | A capture step ran and Blink refused it; the code is Blink's own (see the Errors reference). Offer another attempt with a new session. |
BLINK_STEP_FAILED | A step was refused without a code from Blink. Treat it like the row above. |
BLINK_STEP_ALREADY_DONE | The session token was already used for that step. Create a new session. |
BLINK_RESPONSE | Blink rejected the session or a step — the message carries the API's code, such as BLINK_SESSION_INVALID or BLINK_QUOTA_EXCEEDED. Create a new session, or contact Blink if it repeats. |
BLINK_NETWORK · BLINK_TIMEOUT | Connectivity. Let the user retry. |
BLINK_INTEGRITY | The app or device failed Blink's integrity checks. Do not retry automatically. |
BLINK_CONFIG | A bad argument: an empty token, an unknown document type or country. A programming error, not a user one. |
BLINK_NO_ACTIVITY · BLINK_NO_UI | Called while the app had no foreground screen to present from (Android / iOS). Call it from a mounted screen. |
BLINK_ERROR | Anything else; the message says what. |
Blink's HTTP errors all share one shape: {"code": "…", "message": "…", "timestamp": "…"}.
The full code list is in the Errors reference.
07Go-live checklist
- The client key and secret exist only in your backend's secret store — not in the app, not in the bundle.
- A new session is created for every attempt, when the user starts it.
- Your backend stores
sessionIdper user and reads/resultbefore changing any account state. REVIEWhas a manual path, andREJECTEDtells the user what to do next.- A background job re-reads results for sessions left
IN_PROGRESSwhen users close the app. - Tested on a real Android phone and a real iPhone with a real document — simulators have no camera.
- Release builds ship the bundled JavaScript; the Metro development server is never a production dependency.
Credentials for sandbox and production are issued under a Blink KYC integration agreement. Talk to us to get set up.