Scan a card from a static image
For file upload or gallery flows, use the lower-level scanning session directly, without the camera UI. This lets you feed images into the scanning engine one at a time.
Initialize and create a session
Create a session with the input image source set to photo.
This tells the engine to expect static images rather than a video stream.
On Web, load the core engine with loadBlinkCardCore, then create the session.
On Android, initialize the SDK, then create the session.
On iOS, create the SDK, then create the session.
On Flutter and React Native, there is no session to manage. You prepare the SDK and session settings, then pass them to the DirectAPI scanning method along with the images, and the wrapper sets the input image source for you.
- Web (JS)
- Android (Kotlin)
- iOS (Swift)
- Flutter (Dart)
- React Native (TS)
import { loadBlinkCardCore } from "@microblink/blinkcard-core";
const blinkCardCore = await loadBlinkCardCore({
licenseKey: "your-license-key",
});
const session = await blinkCardCore.createScanningSession({
inputImageSource: "photo",
});
val sdk = BlinkCardSdk.initializeSdk(
context,
BlinkCardSdkSettings(licenseKey = "your-license-key")
).getOrThrow()
val session = sdk.createScanningSession(
BlinkCardSessionSettings(inputImageSource = InputImageSource.Photo)
)
let sdk = try await BlinkCardSdk.createBlinkCardSdk(
withSettings: BlinkCardSdkSettings(licenseKey: "your-license-key")
)
let session = try await sdk.createScanningSession(
sessionSettings: BlinkCardSessionSettings(inputImageSource: .photo)
)
import 'package:blinkcard_flutter/blinkcard_flutter.dart';
final blinkCardPlugin = BlinkCardFlutter();
final sdkSettings = BlinkCardSdkSettings(licenseKey: 'your-license-key');
final sessionSettings = BlinkCardSessionSettings();
import {
perfromDirectApiScan,
BlinkCardSdkSettings,
BlinkCardSessionSettings,
} from "@microblink/blinkcard-react-native";
const sdkSettings = new BlinkCardSdkSettings({ licenseKey: "your-license-key" });
const sessionSettings = new BlinkCardSessionSettings();
The React Native package exports the DirectAPI method as perfromDirectApiScan, with that spelling.
For installation, license setup, and hosting the WASM resources, see the quickstart.
Load an image for scanning
Each platform wraps a decoded image in the format the scanning engine expects.
On Web, the engine accepts ImageData.
Use an offscreen canvas to decode a File or Blob into the required format.
On Android, build an InputImage from an ARGB_8888 Bitmap loaded from the gallery or a file.
On iOS, build an InputImage from a UIImage loaded from the photo library or a file.
On Flutter and React Native, DirectAPI accepts Base64-encoded image strings.
- Web (JS)
- Android (Kotlin)
- iOS (Swift)
- Flutter (Dart)
- React Native (TS)
async function fileToImageData(file: File): Promise<ImageData> {
const img = new Image();
const url = URL.createObjectURL(file);
img.src = url;
try {
await new Promise<void>((resolve) => {
img.onload = () => resolve();
});
const canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext("2d");
if (!ctx) throw new Error("Could not get canvas context");
ctx.drawImage(img, 0, 0);
return ctx.getImageData(0, 0, canvas.width, canvas.height);
} finally {
URL.revokeObjectURL(url);
}
}
val inputImage = InputImage.createFromBitmap(bitmap)
The bitmap must be in ARGB_8888 configuration.
let inputImage = InputImage(uiImage: image)
Read the image files from disk or the gallery and encode them:
import 'dart:convert';
import 'dart:io';
final firstImageBase64 = base64Encode(await File(firstImagePath).readAsBytes());
final secondImageBase64 = base64Encode(await File(secondImagePath).readAsBytes());
Read the image files from disk or the gallery and encode them, for example with react-native-image-picker:
import { launchImageLibrary } from "react-native-image-picker";
const picked = await launchImageLibrary({ mediaType: "photo", includeBase64: true });
const firstImageBase64 = picked.assets?.[0]?.base64;
if (!firstImageBase64) throw new Error("No image selected");
Process images and get the result
Pass each image to the session and check the processing status after each call.
A status of awaiting-other-side means the engine needs the card's other side before it can return a final result.
When the card is fully scanned, retrieve the result with getResult.
- Web (JS)
- Android (Kotlin)
- iOS (Swift)
- Flutter (Dart)
- React Native (TS)
// Process the first side (the side with the card number)
const firstImageData = await fileToImageData(firstFile);
const firstProcessResult = session.process(firstImageData);
const status = firstProcessResult.inputImageAnalysisResult.processingStatus;
if (status === "awaiting-other-side") {
// The card has more data on the other side — process it too
const secondImageData = await fileToImageData(secondFile);
session.process(secondImageData);
}
// Retrieve the final result
const result = session.getResult();
console.log("Card number:", result.cardAccounts[0].cardNumber);
console.log("Expiry:", result.cardAccounts[0].expiryDate.originalString);
// Process the first side (the side with the card number)
val firstResult = session.process(
InputImage.createFromBitmap(firstBitmap)
).getOrThrow()
if (firstResult.inputImageAnalysisResult.processingStatus
== ProcessingStatus.AwaitingOtherSide) {
// The card has more data on the other side — process it too
session.process(InputImage.createFromBitmap(secondBitmap)).getOrThrow()
}
// Retrieve the final result
val result = session.getResult()
val account = result.cardAccounts[0]
println("Card number: ${account.cardNumber}")
println("Expiry: ${account.expiryDate?.originalString}")
// Process the first side (the side with the card number)
let firstResult = try await session.process(inputImage: InputImage(uiImage: firstImage))
if firstResult.processResult?.inputImageAnalysisResult.processingStatus == .awaitingOtherSide {
// The card has more data on the other side — process it too
try await session.process(inputImage: InputImage(uiImage: secondImage))
}
// Retrieve the final result
let result = session.getResult()
let account = result.cardAccounts[0]
print("Card number:", account.cardNumber)
print("Expiry:", account.expiryDate?.originalString ?? "")
Pass the image of the side with the card number as firstSideImage.
Pass secondSideImage only when you need data from the other side, such as a CVV printed on the back.
import 'package:flutter/services.dart';
try {
final result = await blinkCardPlugin.performDirectApiScan(
blinkCardSdkSettings: sdkSettings,
blinkCardSessionSettings: sessionSettings,
firstSideImage: firstImageBase64,
secondSideImage: secondImageBase64, // optional
);
if (result != null) {
final account = result.cardAccounts[0];
print('Card number: ${account.cardNumber}');
print('Expiry: ${account.expiryDate?.originalString}');
}
} on PlatformException catch (e) {
print(e.message);
}
Pass the image of the side with the card number as the first image. Pass the second image only when you need data from the other side, such as a CVV printed on the back.
try {
const result = await perfromDirectApiScan(
sdkSettings,
sessionSettings,
firstImageBase64,
secondImageBase64, // optional
);
const account = result?.cardAccounts[0];
console.log("Card number:", account?.cardNumber);
console.log("Expiry:", account?.expiryDate?.originalString);
} catch (error) {
console.error(error);
}
Clean up
Release the session and SDK when done.
On Web, call session.delete(), then terminate the core instance.
On Android, close the session and the SDK to release native resources.
On iOS, the session releases its resources when it is deallocated; terminate the SDK when you are done with it.
On Flutter and React Native, call unloadSdk to release the SDK.
- Web (JS)
- Android (Kotlin)
- iOS (Swift)
- Flutter (Dart)
- React Native (TS)
session.delete();
await blinkCardCore.terminate();
session.close()
sdk.close()
BlinkCardSdk.terminateBlinkCardSdk()
await blinkCardPlugin.unloadSdk();
import { unloadSdk } from "@microblink/blinkcard-react-native";
await unloadSdk(false);