Scan a passport
Passports have a data page (the page with the photo) that contains two zones:
- VIZ (Visual Inspection Zone): the biographical data printed in the upper half—name, nationality, date of birth, document number, and similar fields
- MRZ (Machine Readable Zone): the two lines of OCR-formatted text at the bottom of the page, encoding the same core fields in a machine-readable format
BlinkID extracts both zones and cross-validates the data between them.
Default behavior
By default, BlinkID scans only the data page (passportDataPageScanOnly: true).
This is the correct setting for most use cases: the data page contains all the core biographical data.
Some passports have a second inner page (for example, a visa page) that additional data can be extracted from.
To also scan this second page, set passportDataPageScanOnly to false and use automatic scanning mode.
The SDK will then prompt the user to turn to the second page when relevant.
Scan the data page (default)
No special configuration is needed—just initialize as normal and BlinkID will scan the passport data page.
- Web (TS)
- Android (Kotlin)
- iOS (Swift)
import { createBlinkId } from "@microblink/blinkid";
const blinkId = await createBlinkId({
licenseKey: "your-license-key",
// passportDataPageScanOnly defaults to true
});
blinkId.addOnResultCallback((result) => {
console.log("Result:", result);
void blinkId.destroy();
});
// passportDataPageScanOnly defaults to true — no special config needed
BlinkIdCameraScanningScreen(
blinkIdSdk = blinkIdSdk,
onScanningSuccess = { result ->
// use result
},
onScanningCanceled = { }
)
// passportDataPageScanOnly defaults to true — no special config needed
let analyzer = try await BlinkIDAnalyzer(sdk: sdk)
BlinkIDUXView(
analyzer: analyzer,
onScanCompleted: { resultState in
guard let result = resultState.scanningResult else { return }
print("Result:", result)
}
)
Also scan the second page
Set passportDataPageScanOnly to false to require a second-page scan for passports that support it.
- Web (TS)
- Android (Kotlin)
- iOS (Swift)
import { createBlinkId } from "@microblink/blinkid";
const blinkId = await createBlinkId({
licenseKey: "your-license-key",
scanningSettings: {
documentCaptureModule: {
passportDataPageScanOnly: false,
},
},
});
BlinkIdCameraScanningScreen(
blinkIdSdk = blinkIdSdk,
sessionSettings = BlinkIdSessionSettings(
scanningSettings = ScanningSettings(
documentCaptureModule = DocumentCaptureModuleSettings(
passportDataPageScanOnly = false,
)
)
),
onScanningSuccess = { result -> },
onScanningCanceled = { }
)
let sessionSettings = BlinkIDSessionSettings(
scanningSettings: ScanningSettings(
documentCaptureModule: DocumentCaptureModuleSettings(
passportDataPageScanOnly: false
)
)
)
let analyzer = try await BlinkIDAnalyzer(
sdk: sdk,
blinkIdSessionSettings: sessionSettings
)
Read biographical data (VIZ)
The VIZ data is available in the aggregated top-level result fields, as well as in the per-side viz field of subResults.
- Web (TS)
- Android (Kotlin)
- iOS (Swift)
blinkId.addOnResultCallback((result) => {
// Top-level aggregated fields
console.log("Full name:", result.fullName?.latin?.value);
console.log("Date of birth:", result.dateOfBirth?.originalString?.latin?.value);
console.log("Nationality:", result.nationality?.latin?.value);
console.log("Document number:", result.documentNumber?.latin?.value);
console.log("Date of expiry:", result.dateOfExpiry?.originalString?.latin?.value);
console.log("Sex:", result.sex?.latin?.value);
// Raw VIZ from the data page
const dataPage = result.subResults[0];
console.log("VIZ:", dataPage.viz);
void blinkId.destroy();
});
onScanningSuccess = { result ->
// Top-level aggregated fields
println("Full name: ${result.fullName?.value}")
println("Date of birth: ${result.dateOfBirth?.value}")
println("Nationality: ${result.nationality?.value}")
println("Document number: ${result.documentNumber?.value}")
println("Date of expiry: ${result.dateOfExpiry?.value}")
println("Sex: ${result.sex?.value}")
// Raw VIZ from the data page
val dataPage = result.subResults[0]
println("VIZ: ${dataPage.viz}")
}
onScanCompleted: { resultState in
guard let result = resultState.scanningResult else { return }
// Top-level aggregated fields
print("Full name:", result.fullName?.value ?? "")
print("Date of birth:", result.dateOfBirth?.value ?? "")
print("Nationality:", result.nationality?.value ?? "")
print("Document number:", result.documentNumber?.value ?? "")
print("Date of expiry:", result.dateOfExpiry?.value ?? "")
print("Sex:", result.sex?.value ?? "")
// Raw VIZ from the data page
let dataPage = result.subResults[0]
print("VIZ:", dataPage.viz as Any)
}
Read MRZ data
MRZ data is available in subResults[0].mrz.
It contains the same core fields as the VIZ, plus MRZ-specific fields like primaryId, secondaryId, opt1, and opt2.
- Web (TS)
- Android (Kotlin)
- iOS (Swift)
blinkId.addOnResultCallback((result) => {
const mrz = result.subResults[0].mrz;
if (mrz) {
console.log("Document number:", mrz.documentNumber);
console.log("Nationality:", mrz.nationality);
console.log("Date of birth:", mrz.dateOfBirth.originalString);
console.log("Date of expiry:", mrz.dateOfExpiry.originalString);
console.log("Gender:", mrz.gender);
console.log("Primary ID:", mrz.primaryId);
console.log("Secondary ID:", mrz.secondaryId);
console.log("Raw MRZ:", mrz.rawMrzString);
console.log("MRZ verified:", mrz.verified);
}
void blinkId.destroy();
});
onScanningSuccess = { result ->
val mrz = result.subResults[0].mrz
if (mrz != null) {
println("Document number: ${mrz.documentNumber}")
println("Nationality: ${mrz.nationality}")
println("Date of birth: ${mrz.dateOfBirth.value}")
println("Date of expiry: ${mrz.dateOfExpiry.value}")
println("Gender: ${mrz.gender}")
println("Primary ID: ${mrz.primaryId}")
println("Secondary ID: ${mrz.secondaryId}")
println("Raw MRZ: ${mrz.rawMrzString}")
println("MRZ verified: ${mrz.verified}")
}
}
onScanCompleted: { resultState in
guard let result = resultState.scanningResult else { return }
if let mrz = result.subResults[0].mrz {
print("Document number:", mrz.documentNumber)
print("Nationality:", mrz.nationality)
print("Date of birth:", mrz.dateOfBirth.value)
print("Date of expiry:", mrz.dateOfExpiry.value)
print("Gender:", mrz.gender)
print("Primary ID:", mrz.primaryId)
print("Secondary ID:", mrz.secondaryId)
print("Raw MRZ:", mrz.rawMrzString)
print("MRZ verified:", mrz.verified)
}
}
The verified field is true when all MRZ check digits are valid—a basic integrity check for the scanned MRZ.