Using callbacks
BlinkID emits data for different events while it scans identity documents. You can grab that data using one of the following callback functions:
addOnDismountCallback()addOnDocumentFilteredCallback()addOnErrorCallback()addOnFrameProcessCallback()addOnResultCallback()addOnUiStateChangedCallback()
All of these callbacks are available from the general
@microblink/blinkid package,
as well as from these individual components:
@microblink/blinkid-ux-manager, @microblink/camera-manager.
How to get results of a scan?
To get scan results, use addOnResultCallback().
import { createBlinkId } from "@microblink/blinkid";
const blinkid = await createBlinkId({
// ...initialize, configure, scan
// after scanning, show results in the console
blinkid.addOnResultCallback((result) => {
console.log("Result:", result);
When a scanning session is completed, addOnResultCallback() will pass the result to
a function you provide as a parameter. In the example above, we're logging the result
to the console.
How to get results of an uncompleted scan?
You can also grab the results of an uncompleted scan by calling the getResult
method directly. For this, you need to use the
blinkid-core package.
import { loadBlinkIdCore } from "@microblink/blinkid-core";
const blinkIdCore = await loadBlinkIdCore({
// ...initialize, configure
// Create a scanning session:
const session = await blinkIdCore.createBlinkIdScanningSession();
// Process a frame. In this example we're processing an ImageData object, but you
// might, for example, add frames from a video stream instead.
const frameResult = await session.process(new ImageData(1920, 1080));
// Show scan results as they become available. The scan doesn't have to be complete.
const result = await session.getResult();
console.log("result", result);
How to filter specific documents?
To filter out (or in) specific document types, use
addDocumentClassFilter()
together with the
addOnDocumentFilteredCallback()
import { createBlinkId } from "@microblink/blinkid";
const blinkid = await createBlinkId({
// ...initialize, configure, scan
blinkid.addDocumentClassFilter((documentClassInfo) => {
return documentClassInfo.type === "id";
});
blinkid.addOnDocumentFilteredCallback(() => {
console.log("The document is not an ID card.");
});
In the example above, we are filtering in only ID cards. First we add a document
class filter that says "return true if the presented document is an ID card". Then,
when a scan occurs, we check if we received a return of true, and if so, we can
proceed; otherwise, we log to the console the notice that the document isn't an ID
card.
We could also filter documents out, i.e. specifically disallow certain kinds of documents. Let's say we don't want to allow driver licenses:
import { createBlinkId } from "@microblink/blinkid";
const blinkid = await createBlinkId({
// ...initialize, configure, scan
blinkid.addDocumentClassFilter((documentClassInfo) => {
return documentClassInfo.type !== "dl";
});
blinkid.addOnDocumentFilteredCallback(() => {
console.log("The document is a driver license, and driver licenses aren't supported.");
});
Here we are saying "return true if the presented document isn't a driver
license". But when we present a driver license for scanning, the filter doesn't
return a true value, and a filtering callback is activated.
You can also combine multiple document features, such as only allowing certain types of documents from certain countries, or disallowing specific regions. See this example app, in which every document except a US passport is not allowed.
The following is a list of documentClassInfo fields that you can use (and combine,
and chain) for filtering:
countrycountryNameisoAlpha2CountryCodeisoAlpha3CountryCodeisoNumericCountryCoderegiontype
See the reference for information about subtypes and allowed values.
How to get frame processing results for an uncompleted scan?
To get frame processing results for an uncompleted (ongoing) scan, use
addOnFrameProcessCallback().
import { createBlinkId } from "@microblink/blinkid";
const blinkid = await createBlinkId({
// ...initialize, configure, scan
blinkid.blinkIdUxManager.addOnFrameProcessCallback((frameResult) => {
console.log('Frame processed:', frameResult);
});
In the example above, whenever a there's a frame change, the console logs the entire result (or the absence of it):
Click to open
{
"inputImageAnalysisResult": {
"processingStatus": "detection-failed",
"missingMandatoryFields": [],
"extractedFields": [],
"invalidCharacterFields": [],
"extraPresentFields": [],
"imageExtractionFailures": [],
"scanningSide": "first",
"documentDetectionStatus": "failed",
"documentClassInfo": {
"countryName": "",
"isoNumericCountryCode": "",
"isoAlpha2CountryCode": "",
"isoAlpha3CountryCode": ""
},
"blurDetectionStatus": "not-available",
"glareDetectionStatus": "not-available",
"documentColorStatus": "not-available",
"documentMoireStatus": "not-available",
"faceDetectionStatus": "not-available",
"mrzDetectionStatus": "not-available",
"barcodeDetectionStatus": "not-available",
"realIDDetectionStatus": "not-available",
"documentLightingStatus": "not-available",
"documentHandOcclusionStatus": "not-available",
"documentOrientation": "not-available",
"documentRotation": "not-available"
},
"resultCompleteness": {
"scanningStatus": "scanning-side-in-progress",
"vizExtracted": false,
"mrzExtracted": false,
"barcodeExtracted": false,
"documentImageExtracted": false,
"faceImageExtracted": false,
"signatureImageExtracted": false
},
"arrayBuffer": {}
}
Note that performance-wise, it's not a good idea to append the output of
addOnFrameProcessCallback() to a data structure without any filtering because, as
the name implies, it generates data on every frame, so you might quickly exhaust your
RAM.
If you're looking for a specific field in the scan result, and you don't want to rely on one of the builtin callbacks from the intro, you should wrap your saving logic in a conditional statement that checks if the field you're interested in is present, for example:
import { createBlinkId } from "@microblink/blinkid";
const blinkid = await createBlinkId({
// ...initialize, configure, scan
let capturedData = null;
let hasBeenCaptured = false;
const TARGET_FIELD = "some value you're interested in";
const savedFrameData = blinkid.blinkIdUxManager.addOnFrameProcessCallback((frameResult) => {
if (!hasBeenCaptured) {
// we're using an non-existent path in this example
const nestedValue = frameProcessResult.inputImageAnalysisResult.some.field.that.interests.you
if (nestedValue === TARGET_VALUE) {
console.log("Success!");
capturedData = frameProcessResult;
hasBeenCaptured = true;
}
}
}
How to catch error messages for failing scans?
To catch general error messages, use
addOnErrorCallback().
import { createBlinkId } from "@microblink/blinkid";
const blinkid = await createBlinkId({
// ...initialize, configure, scan
blinkid.addOnErrorCallback((error) => {
if (error === "timeout") {
console.log("Timeout!")
} else if (error === "unknown") {
console.log("Unknown!);
}
Possible callback values are timeout and unknown.
This callback can be used in combination with the addOnFrameProcessedCallback(),
for example by saving a subset of its output and then reading that output in case the
error callback is activated, and deleting the output in case no error is detected.
This way you get detailed diagnostic information for each processed frame, but only
if an error occurred.
In this example, we just continuously overwrite the lastCapturedFrame object until
such time that there's an error, and then we present the "diagnostic" frame data in
case of an error:
import { createBlinkId } from "@microblink/blinkid";
const blinkid = await createBlinkId({
// ...initialize, configure, scan
let lastCapturedFrame = null;
const frameData = blinkid.blinkIdUxManager.addOnFrameProcessCallback(
(frameResult) => {
lastCapturedFrame = frameResult;
}
);
blinkid.addOnErrorCallback((error) => {
console.log(`An error occurred: ${error}.`);
console.log(`This is the last captured frame:`, lastCapturedFrame);
});
How to respond to UI and camera changes?
There are numerous "cues" that BlinkID provides to the user with regards to how to present a document. Mostly these are things like "move the document closer" or "make sure that the document is fully readable": instructions for the user to change positioning or lighting. This also includes instructions to flip the document, if it's a two-sided kind. All of these instructions happen by themselves and require no intervention or manual integration, but you may want to add your own logic that responds to such changes.
In that case, you can use
addOnUiStateChangedCallback():
import { createBlinkId } from "@microblink/blinkid";
const blinkid = await createBlinkId({
// ...initialize, configure, scan
blinkid.blinkIdUxManager.addOnUiStateChangedCallback((uiState) => {
if (uiState.key === "DOCUMENT_FRAMING_CAMERA_TOO_CLOSE") {
console.log("The document is too close to the camera.");
}
});
The example above is redundant: the message logged to the console is presented to the user anyway, but instead of logging the message, you might perform some custom logic.
The same goes for all other key values for the UI state. The full list is available
in the API
reference.
A special case of UI changes is when the camera UI component is removed from the
page. This happens after a scan is completed, and you might want to add special logic
for each camera dismount. In that case, you can use
addOnDismountCallback()[/sdk/web/api-ref/blinkid/type-aliases/CameraManagerComponent/#addondismountcallback):
import { createBlinkId } from "@microblink/blinkid";
const blinkid = await createBlinkId({
// ...initialize, configure, scan
blinkid.addOnResultCallback((result) => {
console.log("Result caught");
void blinkid.destroy();
});
blinkid.cameraUi.addOnDismountCallback(() => {
console.log("Camera UI dismounted");
});
In the example above, after we call the destroy() method, the camera UI component
gets dismounted, and the dismount callback is activated. Again, we're simply logging
a dismount message to the console, but if there's any custom logic to implement for
the dismount, this is the place.