full_identity_verification
Full Identity Verification SDK for Android and iOS — document scanning combined with liveness detection.
Get your credentials from Blusalt
Features
SDK Modes
1. Full Identity SDK (Document Present)
The user physically scans their document. The SDK extracts document data and runs a liveness check in a single flow. Optionally filter which document types are available.
2. Document Absent — Custom Selector
No physical document is available. The user selects their document type from a list you provide, enters their ID number, and completes a liveness check.
3. Document Absent — ID Number
No physical document is available. You already know the document type and number; pass them directly and the SDK runs only the liveness check.
Liveness Types (LivenessFacialComparisonType)
| Value | Description |
|---|---|
dynamic |
User performs head movements (open mouth, shake head). Strictest — static images are detected and rejected. |
action |
User performs a prompted action. High security. |
varifocal |
Camera-based depth detection. Good balance of speed and security. |
flash |
Screen-flash method. Works well in low-light conditions. |
Usage
dependencies:
full_identity_verification: ^2.1.4
import 'package:full_identity_verification/full_identity_verification.dart';
import 'package:full_identity_verification/enums.dart';
import 'package:full_identity_verification/model.dart';
Initialize
final _plugin = BlusaltFullIdentityVerification();
1. Full Identity SDK (Document Present)
/// [documentTypeList] filters which document types appear in the SDK.
/// Pass null to show all types.
///
/// [timeoutDurationInSec] overrides the network timeout (default 120 s).
///
/// [enableEncryption] enables bi-directional payload encryption (default false).
///
/// [metadata] optional key-value pairs forwarded to the native SDK as-is.
startFullIdentitySDK() async {
final result = await _plugin.startFullIdentitySDK(
apiKey: 'your_api_key',
appName: 'your_app_name',
clientId: 'your_client_id',
isDev: true, // set false for production
reference: 'unique_reference', // optional
webhookUrl: 'https://your-webhook.com', // optional
documentTypeList: [
DocumentType.bvn,
DocumentType.nin,
DocumentType.passport,
DocumentType.driverLicense,
DocumentType.pvc,
],
timeoutDurationInSec: 120,
enableEncryption: false,
livenessFacialComparisonType: LivenessFacialComparisonType.dynamic,
showLivenessResult: false,
metadata: {'userId': 'user_001', 'sessionId': 'abc-456'}, // optional
);
if (result?.blusaltFullIdentityProcess == BlusaltFullIdentityProcess.completed) {
final doc = result!.fullIdentitySuccessData?.extractedDocumentData;
final live = result.fullIdentitySuccessData?.livenessSuccessData;
debugPrint('Name: ${doc?.firstName} ${doc?.lastName}');
debugPrint('Liveness passed: ${live?.isPassProcedureValidation}');
} else {
debugPrint('Error ${result?.code}: ${result?.message}');
}
}
2. Document Absent — Custom Selector
/// [documentTypeList] defines which document types the user can pick from.
///
/// [livenessFacialComparisonType] controls the liveness method used.
///
/// [thresholdConfig] controls face-comparison strictness.
/// Pass null to use the SDK server default.
///
/// [metadata] optional key-value pairs forwarded to the native SDK as-is.
startDocAbsentWithCustomSelector() async {
final result = await _plugin.startDocAbsentWithCustomSelector(
apiKey: 'your_api_key',
appName: 'your_app_name',
clientId: 'your_client_id',
isDev: true,
documentTypeList: [DocumentType.bvn, DocumentType.nin],
reference: 'unique_reference',
webhookUrl: 'https://your-webhook.com',
livenessFacialComparisonType: LivenessFacialComparisonType.dynamic,
showLivenessResult: false,
thresholdConfig: ThresholdConfig(
localThreshold: 85.0,
priority: ThresholdPriority.serverWithLocalFallback,
),
timeoutDurationInSec: 120,
enableEncryption: false,
metadata: {'userId': 'user_001', 'sessionId': 'abc-456'}, // optional
);
if (result?.blusaltFullIdentityProcess == BlusaltFullIdentityProcess.completed) {
// handle success
} else {
debugPrint('Error ${result?.code}: ${result?.message}');
}
}
3. Document Absent — ID Number
/// [documentType] specifies the document type to verify against.
///
/// [documentNumber] is the ID number (e.g. BVN, NIN, passport number).
///
/// [startProcessOnGettingToFirstScreen] skips the "Continue" button on the
/// SDK's first screen and starts the process automatically.
///
/// [metadata] optional key-value pairs forwarded to the native SDK as-is.
startDocAbsentWithIdNumber() async {
final result = await _plugin.startDocAbsentWithIdNumber(
apiKey: 'your_api_key',
appName: 'your_app_name',
clientId: 'your_client_id',
isDev: true,
documentType: DocumentType.bvn,
documentNumber: '12345678901',
reference: 'unique_reference',
webhookUrl: 'https://your-webhook.com',
startProcessOnGettingToFirstScreen: false,
livenessFacialComparisonType: LivenessFacialComparisonType.dynamic,
showLivenessResult: false,
thresholdConfig: ThresholdConfig(
localThreshold: 85.0,
priority: ThresholdPriority.serverWithLocalFallback,
),
timeoutDurationInSec: 120,
enableEncryption: false,
metadata: {'userId': 'user_001', 'sessionId': 'abc-456'}, // optional
);
if (result?.blusaltFullIdentityProcess == BlusaltFullIdentityProcess.completed) {
// handle success
} else {
debugPrint('Error ${result?.code}: ${result?.message}');
}
}
Models
ThresholdConfig
Controls face-comparison strictness for doc-absent flows.
| Property | Type | Required | Description |
|---|---|---|---|
localThreshold |
double |
No | Threshold value 0–100. Higher = stricter. |
priority |
ThresholdPriority |
No | How the threshold is applied (default: serverWithLocalFallback) |
ThresholdConfig(
localThreshold: 85.0,
priority: ThresholdPriority.serverWithLocalFallback,
)
Pass null to use the SDK's server-side default.
BlusaltFullIdentityResultResponse
| Field | Type | Description |
|---|---|---|
blusaltFullIdentityProcess |
BlusaltFullIdentityProcess |
completed or notImplemented |
fullIdentitySuccessData |
FullIdentitySuccessData? |
Document + liveness data on success |
reference |
String? |
The reference you passed in |
code |
String? |
Error code on failure |
message |
String? |
Error message on failure |
exception |
PlatformException? |
Raw platform exception if available |
FullIdentitySuccessData
| Field | Type | Description |
|---|---|---|
extractedDocumentData |
dynamic |
Parsed document fields (see below) |
livenessSuccessData |
dynamic |
Liveness check results (see below) |
Common document fields: firstName, lastName, middleName, dateOfBirth, gender, documentNumber, documentType, imageByte
Common liveness fields: isPassProcedureValidation, isPassFaceComparison, isPassFaceGenuiness, livenessImage, originalLivenessImage, originalImage
Enums
DocumentType
| Value | Description |
|---|---|
bvn |
Bank Verification Number |
nin |
National Identification Number |
passport |
International Passport |
driverLicense |
Driver's License |
pvc |
Permanent Voter's Card |
LivenessFacialComparisonType
| Value | Description |
|---|---|
dynamic |
Head-movement liveness (recommended) |
action |
Prompted-action liveness |
varifocal |
Camera depth detection |
flash |
Screen-flash detection |
ThresholdPriority
| Value | Description |
|---|---|
serverWithLocalFallback |
Use server threshold; fall back to localThreshold if unavailable (default) |
localOnly |
Always use localThreshold |
serverOnly |
Always use the server threshold; ignores localThreshold |
BlusaltFullIdentityProcess
| Value | Description |
|---|---|
completed |
Verification completed successfully |
notImplemented |
Platform not supported or an error occurred |
Parameters Reference
Common (all three methods)
| Parameter | Type | Required | Description |
|---|---|---|---|
apiKey |
String |
Yes | Your Blusalt API key |
appName |
String |
Yes | Your application name |
clientId |
String |
Yes | Your Blusalt client ID |
isDev |
bool |
Yes | true for sandbox, false for production |
reference |
String? |
No | Your unique reference for this transaction |
webhookUrl |
String? |
No | URL to receive webhook notifications |
timeoutDurationInSec |
int? |
No | Network timeout in seconds (default: 120) |
enableEncryption |
bool |
No | Enable bi-directional payload encryption (default: false) - Encryption must be enabled on API key to use |
thresholdConfig |
ThresholdConfig? |
No | Face-comparison threshold config |
livenessFacialComparisonType |
LivenessFacialComparisonType |
No | Liveness method (default: dynamic) |
showLivenessResult |
bool |
No | Show the SDK's built-in result screen (default: true) |
metadata |
Map<String, dynamic>? |
No | Optional key-value pairs forwarded to the native SDK as-is |
Full Identity SDK only
| Parameter | Type | Required | Description |
|---|---|---|---|
documentTypeList |
List<DocumentType>? |
No | Filter available document types; null = all types |
Doc Absent — Custom Selector only
| Parameter | Type | Required | Description |
|---|---|---|---|
documentTypeList |
List<DocumentType> |
Yes | Document types the user can choose from |
Doc Absent — ID Number only
| Parameter | Type | Required | Description |
|---|---|---|---|
documentType |
DocumentType |
Yes | Document type to verify against |
documentNumber |
String |
Yes | The ID number |
startProcessOnGettingToFirstScreen |
bool |
No | Auto-start on first screen (default: false) |
Example
See the example/ directory for a complete sample app demonstrating all three SDK modes with all configurable options.
import 'package:full_identity_verification/full_identity_verification.dart';
import 'package:full_identity_verification/enums.dart';
import 'package:full_identity_verification/model.dart';
final _plugin = BlusaltFullIdentityVerification();
Future<void> runVerification() async {
final result = await _plugin.startFullIdentitySDK(
apiKey: 'API_KEY',
appName: 'MyApp',
clientId: 'CLIENT_ID',
isDev: false,
documentTypeList: [DocumentType.nin, DocumentType.passport],
);
if (result?.blusaltFullIdentityProcess == BlusaltFullIdentityProcess.completed) {
final doc = result!.fullIdentitySuccessData?.extractedDocumentData;
final live = result.fullIdentitySuccessData?.livenessSuccessData;
print('${doc?.firstName} ${doc?.lastName}');
print('Liveness passed: ${live?.isPassProcedureValidation}');
} else {
print('Failed — ${result?.code}: ${result?.message}');
}
}
Installation
Android
Step 1 — GitHub credentials
Create a github.properties file in the root of your /android folder:
USERNAME_GITHUB=YourGitHubUsername
TOKEN_GITHUB=YourClassicPersonalAccessToken
Grant the token access to read packages at minimum. If you see "Unauthorized" during a Gradle sync, regenerate the token and tick all package-related boxes.
Step 2 — Project-level build.gradle
buildscript {
ext.kotlin_version = '1.9.+'
dependencies {
classpath 'com.android.tools.build:gradle:7.3.+'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
def githubPropertiesFile = rootProject.file("github.properties")
def githubProperties = new Properties()
githubProperties.load(new FileInputStream(githubPropertiesFile))
repositories {
maven {
name "GitHubPackages"
url 'https://maven.pkg.github.com/Blusalt-FS/Liveness-Only-Android-Package'
credentials {
username githubProperties['USERNAME_GITHUB']
password githubProperties['TOKEN_GITHUB']
}
}
maven {
name "GitHubPackages"
url 'https://maven.pkg.github.com/Blusalt-FS/Blusalt_Document_Verification-Android-Package'
credentials {
username githubProperties['USERNAME_GITHUB']
password githubProperties['TOKEN_GITHUB']
}
}
maven {
name "GitHubPackages"
url 'https://maven.pkg.github.com/Blusalt-FS/Full-Identity-SDK-Android-Package'
credentials {
username githubProperties['USERNAME_GITHUB']
password githubProperties['TOKEN_GITHUB']
}
}
}
}
Step 3 — Minimum SDK version
In /android/app/build.gradle:
android {
defaultConfig {
minSdkVersion 24
}
}
Step 4 — ProGuard (if minify is enabled)
In /android/app/proguard-rules.pro:
-keep public class com.megvii.**{*;}
-keep class net.blusalt.liveness_native.** { *; }
-keep class net.blusalt.identityverify.** { *; }
-keep class net.blusalt.fullidentity.** { *; }
Enable it in /android/app/build.gradle:
android {
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
iOS
Minimum deployment target: iOS 14.0
cd ios && pod install
Add camera permission to ios/Runner/Info.plist:
<key>NSCameraUsageDescription</key>
<string>Required for identity document scanning and liveness detection</string>
📱 Note on iOS
-
Physical device only — iOS Simulators are not supported.
-
Ensure
FullIdentityFramework.xcframeworkis linked In your Xcode workspace, navigate toPods > FullIdentityFramework.xcframeworkand confirm it is linked to your app's build target. -
Add required system frameworks If you encounter build errors related to
MediaPlayerorWebKit:- Open Xcode → Targets → Runner → General → Frameworks, Libraries, and Embedded Content
- Add:
AVFoundation.framework,CoreMedia.framework,CoreMotion.framework,MediaPlayer.framework,SystemConfiguration.framework,WebKit.framework
Error Handling
The SDK never throws — it always returns a BlusaltFullIdentityResultResponse. Check the blusaltFullIdentityProcess field:
final result = await _plugin.startFullIdentitySDK(...);
if (result == null) {
// unexpected null — treat as failure
return;
}
switch (result.blusaltFullIdentityProcess) {
case BlusaltFullIdentityProcess.completed:
// success — read result.fullIdentitySuccessData
break;
case BlusaltFullIdentityProcess.notImplemented:
// failure — read result.code and result.message
debugPrint('Code: ${result.code}');
debugPrint('Message: ${result.message}');
break;
}
Support
For issues or questions contact Blusalt support or raise an issue in this repository.