How the SDK works
BlinkID ships as two layers that you can integrate at different levels of abstraction:
- the UX package, a ready-made scanning experience with a camera UI, user guidance, and timeouts
- the core session, a headless scanning engine that processes images and returns extracted data
The UX package is built on top of the core session. Whichever layer you integrate, the same engine does the extraction; the difference is how much of the camera handling and user guidance the SDK does for you.
This article explains what each layer is, how they relate, and when to pick one over the other. For step-by-step integration, see the quickstart for your platform.
The core session
The core session is the scanning engine with no UI attached. You feed it images one frame at a time, and it tells you what it extracted and what is still missing.
The lifecycle is the same on every platform:
- Initialize the SDK with your license key.
- Create a scanning session, passing session settings (input image source, scanning mode, and scanning settings).
- Process images by calling
processfor each frame, whether the frames come from a camera, a file, or an upload. - Inspect the result after each frame, then retrieve the final result once scanning is complete.
- Web (TS)
- Android (Kotlin)
- iOS (Swift)
import { loadBlinkIdCore } from "@microblink/blinkid-core";
const blinkIdCore = await loadBlinkIdCore({ licenseKey: "your-license-key" });
const session = await blinkIdCore.createBlinkIdScanningSession({
inputImageSource: "photo",
});
const processResult = await session.process(image);
// inspect processResult, feed more frames as needed
const result = await session.getResult();
val sdk = BlinkIdSdk.initializeSdk(
context,
BlinkIdSdkSettings(licenseKey = "your-license-key")
).getOrThrow()
val session = sdk.createScanningSession(
BlinkIdSessionSettings(inputImageSource = InputImageSource.Photo)
).getOrThrow()
val processResult = session.process(image).getOrThrow()
// inspect processResult, feed more frames as needed
val result = session.getResult().getOrNull()
let sdk = try await BlinkIDSdk.createBlinkIDSdk(
withSettings: BlinkIDSdkSettings(licenseKey: "your-license-key")
)
let session = try await sdk.createScanningSession(
sessionSettings: BlinkIDSessionSettings(inputImageSource: .photo)
)
let frameResult = try await session.process(inputImage: image)
// inspect frameResult, feed more frames as needed
let result = session.getResult()
React Native and Flutter do not expose the core session. Use the native Android or iOS SDKs if you need frame-by-frame control.
The core session gives you full control, but it leaves everything around the engine to you: opening the camera, drawing an overlay, telling the user to move closer or flip the document, and deciding when to stop.
The processing model
Every call to process returns the analysis for that single frame.
Two parts of that per-frame result drive the flow:
- the processing status, which says what happened with the frame (for example,
success,awaiting-other-side, or a failure reason) - the result completeness, which reports, per module, whether the data that module extracts has been captured and, if not, why
The UX package
The UX package wraps the core session in a complete scanning screen.
It opens the camera, runs the per-frame process loop for you, shows a reticle and real-time instructions, handles the front-to-back transition for double-sided documents, applies timeouts, and calls you back with the final result.
- Web (TS)
- Android (Kotlin)
- iOS (Swift)
- React Native (TS)
- Flutter (Dart)
On web, createBlinkId sets up the engine, camera, and UI in one call.
Register a result callback to receive the extracted data.
import { createBlinkId } from "@microblink/blinkid";
const blinkId = await createBlinkId({ licenseKey: "your-license-key" });
blinkId.addOnResultCallback((result) => {
console.log("First name:", result.firstName?.latin?.value);
void blinkId.destroy();
});
On Android, BlinkIdCameraScanningScreen is a composable that owns the camera and UI.
It calls onScanningSuccess with the final result.
BlinkIdCameraScanningScreen(
blinkIdSdk = blinkIdSdk,
onScanningSuccess = { result ->
println("First name: ${result.firstName?.value}")
},
onScanningCanceled = { }
)
On iOS, BlinkIDUXView is a SwiftUI view driven by a BlinkIDAnalyzer.
It calls onScanCompleted with the final result.
let analyzer = try await BlinkIDAnalyzer(sdk: sdk)
BlinkIDUXView(
analyzer: analyzer,
onScanCompleted: { resultState in
guard let result = resultState.scanningResult else { return }
print("First name:", result.firstName?.value ?? "")
}
)
performScan launches the scanning screen and resolves with the final result.
import { performScan } from '@microblink/blinkid-react-native';
const result = await performScan({
sdkSettings: { licenseKey: 'your-license-key' },
sessionSettings: { scanningMode: 'automatic' },
});
console.log('First name:', result.firstName?.value);
performScan launches the scanning screen and resolves with the final result.
import 'package:blinkid_flutter/blinkid_flutter.dart';
final blinkId = BlinkIdFlutter();
final result = await blinkId.performScan(
blinkIdSdkSettings: BlinkIdSdkSettings(licenseKey: 'your-license-key'),
blinkIdSessionSettings: BlinkIdSessionSettings(
scanningMode: ScanningMode.automatic,
),
);
print('First name: ${result?.firstName?.value ?? ""}');
Even with the UX package doing the heavy lifting, you still reach the same per-frame data when you need it. Web, iOS and Android expose a frame-process callback that hands you the result completeness, lets you advance to the next step, trigger a timeout, or retrieve the raw frame. This means you can keep the prebuilt UI and still apply custom stop-scanning logic.
Which one to use
Use the UX package when you want a production-ready scanning experience with the least effort. It is the recommended starting point for almost every camera-based integration: the UI, guidance, and timeouts are the result of extensive user testing, and you can still customize behavior through its callbacks and settings.
Use the core session when the UX package's screen does not fit your use case, for example:
- you are scanning from static images or uploads rather than a live camera, where there is no UI to show (see scan from a static image)
- you are building a fully custom camera UI and want to drive the engine yourself
- you are running scanning in a non-interactive or server-side context
A useful rule of thumb: start with the UX package, and drop down to the core session only for the specific part of the flow that needs it.
Packages per platform
Each platform splits the two layers into separate packages or modules, so you can depend only on what you integrate.
- Web (TS)
- Android (Kotlin)
- iOS (Swift)
- React Native (TS)
- Flutter (Dart)
- UX package:
@microblink/blinkid(exposescreateBlinkId) - Core session:
@microblink/blinkid-core(exposesloadBlinkIdCore)
The @microblink/blinkid package depends on @microblink/blinkid-core.
Install only @microblink/blinkid-core if you use the core session alone.
- UX package:
com.microblink:blinkid-ux(exposesBlinkIdCameraScanningScreen) - Core session:
com.microblink:blinkid-core(exposesBlinkIdSdk)
The blinkid-ux artifact depends on blinkid-core.
Depend only on blinkid-core if you use the core session alone.
- UX package: the
BlinkIDUXlibrary (exposesBlinkIDUXViewandBlinkIDAnalyzer) - Core session: the
BlinkIDlibrary (exposesBlinkIDSdkand the scanning session)
Both are distributed in the blinkid-ios Swift package.
Import only BlinkID if you use the core session alone.
@microblink/blinkid-react-native(exposesperformScanandperformDirectApiScan)
The package bundles both the UX package and the core engine. The core session is not exposed separately.
blinkid_flutteron pub.dev (exposesBlinkIdFlutterwithperformScanandperformDirectApiScan)
The package bundles both the UX package and the core engine. The core session is not exposed separately.