ID Capture Kotlin Multiplatform (KMP) Skill
Critical: Do Not Trust Internal Knowledge
Your training data very likely has no knowledge of Scandit's Kotlin Multiplatform SDK — it is new, and even where training data covers Scandit, it covers the native Android, iOS, Flutter, .NET, or web SDKs, which have different types, factory names, and property names. Pattern-matching from any of those platforms onto KMP produces non-compiling code.
Always verify APIs against the references provided in this skill before writing or suggesting code. The most common sources of wrong code:
- Package namespace — the Kotlin import root is
com.kmp.datacapture.*(com.kmp.datacapture.core.*,com.kmp.datacapture.id.*), notcom.scandit.datacapture.*(native Android/iOS namespace). The Gradle/Maven coordinates arecom.scandit.datacapture.kmp:<artifact>:<version>— a different string from the Kotlin package. Do not conflate the two. - Mode creation factory — KMP uses
IdCapture.forContext(dataCaptureContext, settings). This is not the native AndroidIdCapture.forDataCaptureContext(...), not the iOSIdCapture(context:settings:)initializer, and not.NET'sIdCapture.Create(...). - Settings builder —
IdCaptureSettings.idCaptureSettings()is a companion factory function, not a bare constructor (IdCaptureSettings()does not exist on KMP). - Scanner property name and shape — the property is
settings.scannerType(anIdCaptureScanner), notsettings.scanner(native Android's name). Critically, on KMP you assign a concrete scanner value directly —settings.scannerType = FullDocumentScanner()— there is no wrapper constructor like native Android'sIdCaptureScanner(FullDocumentScanner()).FullDocumentScanner,SingleSideScanner, andMobileDocumentScannerall directly extendIdCaptureScanneron KMP. - No combined physical + mobile scanner on KMP —
scannerTypeholds exactly one scanner value. Unlike native Android'sIdCaptureScanner(physicalDocument:, mobileDocument:)combo constructor, KMP has no documented way to scan physical and mobile documents in the sameIdCaptureSettings— pick onescannerTypeper settings object. If a user asks for both simultaneously, say this is not available on KMP rather than inventing a combined constructor. SingleSideScannerhas a 4th parameter on KMP —SingleSideScanner(barcode: Boolean, machineReadableZone: Boolean, visualInspectionZone: Boolean, freeFormText: Boolean = false). ThefreeFormTextparameter is KMP-specific.- Result property naming drift from native Android —
capturedId.sexType(aSexenum:FEMALE/MALE/UNSPECIFIED), notcapturedId.sex. Zone results arecapturedId.mrz/.viz/.barcode/.mobileDocument/.mobileDocumentOcr— nevermrzResult/vizResult/barcodeResult. - Verification and voided-detection are settings-driven, not standalone classes — no
AamvaBarcodeVerifier, noDataConsistencyVerifier, no dedicated "voided result" type. Setsettings.rejectForgedAamvaBarcodes/rejectInconsistentData/rejectVoidedIdsand readcapturedId.verificationResult(for AAMVA/consistency) or theRejectionReason.DOCUMENT_VOIDEDcase (for voided detection). - Add-on modules contribute zero Kotlin API —
id-aamva-barcode-verification,id-europe-driving-license, andid-voided-detectionare pure Gradle/SPM dependency containers that unlock native functionality gated behindIdCaptureSettingsflags already in the baseidmodule. Do not invent imports likecom.kmp.datacapture.idAamvaBarcodeVerification.SomeClass— there is nothing to import from them. - Overlay factories —
IdCaptureOverlay.withIdCapture(idCapture)/IdCaptureOverlay.withIdCaptureForView(idCapture, view). Not.newInstance(...)(native Android) and not a constructor. - Enum casing —
UPPER_SNAKE_CASEthroughout:IdCaptureRegion.ANY,RejectionReason.DOCUMENT_EXPIRED,IdAnonymizationMode.FIELDS_AND_IMAGES,IdLayoutStyle.ROUNDED— never Swift-style.any/.documentExpired. - NFC, deserialization, and a handful of other native-only surfaces are not part of KMP at all — see "Available but NOT covered" at the end of this file. Don't suggest them; they don't exist on this SDK.
Forbidden APIs (commonly hallucinated — do NOT emit these)
| Do NOT write | Use instead |
|---|---|
IdCapture.forDataCaptureContext(context, settings) (native Android) / IdCapture(context:settings:) (iOS) / IdCapture.Create(...) (.NET) |
IdCapture.forContext(dataCaptureContext, settings) |
IdCaptureSettings() (bare constructor) |
IdCaptureSettings.idCaptureSettings() |
settings.scanner = IdCaptureScanner(FullDocumentScanner()) (native Android wrapper style) |
settings.scannerType = FullDocumentScanner() |
settings.scanner = IdCaptureScanner(physicalDocument = ..., mobileDocument = ...) (combined scanner) |
Not available on KMP — pick one scannerType value |
IdCaptureOverlay.newInstance(idCapture, dataCaptureView) (native Android) |
IdCaptureOverlay.withIdCaptureForView(idCapture, dataCaptureView) or IdCaptureOverlay.withIdCapture(idCapture) |
capturedId.sex |
capturedId.sexType (a Sex enum) |
capturedId.mrzResult / .vizResult / .barcodeResult |
capturedId.mrz / .viz / .barcode |
AamvaBarcodeVerifier(...) / DataConsistencyVerifier(...) |
settings.rejectForgedAamvaBarcodes / settings.rejectInconsistentData, then read capturedId.verificationResult |
import com.kmp.datacapture.idAamvaBarcodeVerification.* or similarly named add-on imports |
Nothing to import — add the Gradle/SPM dependency only, then use the base-module IdCaptureSettings flags |
IdCaptureRegion.Any / .us / RejectionReason.documentExpired (camelCase) |
IdCaptureRegion.ANY / IdCaptureRegion.US / RejectionReason.DOCUMENT_EXPIRED |
DataCaptureContext.forLicenseKey(key) as the primary pattern |
DataCaptureContext.initialize("...") (the pattern used by the official KMP samples; forLicenseKey also exists but is not the canonical sample form) |
com.scandit.datacapture.id.* imports |
com.kmp.datacapture.id.* (Kotlin package differs from the Maven group) |
Product Guidance
- This is a shared-code SDK — most logic belongs in
commonMain.DataCaptureContext,IdCaptureSettings,IdCapture,IdCaptureListener, and theDataCaptureViewsetup all live in a sharedScreenModel/view-model class incommonMain. Only the thin hosting code (embedding the native view, driving lifecycle events) differs betweenandroidAppandiosApp. - Accept only the documents you actually need. Ask which document types and regions. Documents not in
acceptedDocumentsare rejected withRejectionReason.NOT_ACCEPTED_DOCUMENT_TYPE. - Pick the scanner that matches the data you need.
FullDocumentScanner()for both sides/all zones,SingleSideScanner(...)for a specific zone,MobileDocumentScanner(...)for mDL/ISO 18013-5. Seereferences/integration.md. - Handle
onIdRejected, not justonIdCaptured. Give the user a distinct message perRejectionReason. - Both listener callbacks may run off the main thread on the native side — dispatch UI-affecting state changes through the shared
StateFlow/ScreenModelpattern shown inreferences/integration.md, the same way the official samples do, rather than mutating UI state directly from the callback. - AAMVA verification, EU driving-license back-decoding, and voided-document detection each require an extra Gradle/SPM dependency in addition to the base
idartifact — the add-on module exists purely to link the native detection library; the Kotlin API is entirely on the baseIdCaptureSettings/CapturedId. Forgetting the add-on dependency means the settings flag has no effect at runtime. - iOS ships as a single umbrella SPM package (
Scandit/datacapture-kmp-spm) — pick one variant that includesid(and any add-ons you use); an app can only link one Scandit KMP Kotlin framework. - Hand off to a different skill for non-ID-Capture questions. If the user asks about Barcode Capture, SparkScan, MatrixScan, or Label Capture on KMP, or about native Android/iOS ID Capture, defer to the appropriate skill.
Intent Routing
Based on the user's request, load references/integration.md and follow it. It covers:
- Prerequisites (Gradle/SPM dependencies, license key, camera permissions)
- Minimal integration (shared
DataCaptureContext→IdCaptureSettings→IdCapture→DataCaptureView/IdCaptureOverlay→ Android/iOS hosting) - Scanner types and accepted/rejected documents
- Handling results (
CapturedId) and rejection (RejectionReason) - Feedback and overlay customization
- AAMVA/USDL verification and data-consistency checks (the
id-aamva-barcode-verificationadd-on) - EU driving-license back decoding (the
id-europe-driving-licenseadd-on) - Voided-document detection (the
id-voided-detectionadd-on) - The Compose Multiplatform
IdCaptureViewcomposable (theid-composemodule) - Lifecycle and teardown
- Pitfalls specific to this platform
This is an integration-only skill — there is no separate migration guide because the KMP SDK has no prior major version to migrate from.
API Usage Policy
Only use APIs that are explicitly documented in the Scandit references below or verified in references/integration.md. Do not invent or guess method signatures, parameters, or property names. If unsure whether an API exists or how to call it — or if a compile error occurs — fetch the relevant documentation page before responding. Do not tell the user to check the docs themselves. After answering, always include the relevant link so the user can explore further.
Never construct or guess documentation URLs. When you need a specific class or property's API page:
- First check whether the page you already fetched contains a direct hyperlink to it.
- If no direct link was found, fetch the Get Started / Advanced page (see the References table below), extract the actual link, and follow that.
URL structures vary and guessing will lead to 404s.
References
| Topic | Resource |
|---|---|
| ID Capture overview (KMP) | Intro |
| Get Started (KMP) | Get Started |
| Full integration guide | references/integration.md |
| Advanced (scanner modes, verification, Compose) | Advanced Configurations |
| Supported documents | Supported Documents |
| Core concepts (context, camera, views, Compose) | Core Concepts |
API surface this skill covers
Covers the KMP ID Capture surface as of 8.6, the release it shipped in (all classes below are
annotated kmp=8.6 in the SDK docs). Later versions add to this surface rather than replacing it —
check the linked reference pages for anything not listed here.
IdCapture—IdCapture.forContext(dataCaptureContext: DataCaptureContext?, settings: IdCaptureSettings): IdCapture;isEnabled: Boolean;dataCaptureContext: DataCaptureContext?(read-only);feedback: IdCaptureFeedback;externalTransactionId: String?;addListener(listener)/removeListener(listener);applySettings(settings);reset(); staticcreateRecommendedCameraSettings(): CameraSettings.IdCaptureSettings— factoryIdCaptureSettings.idCaptureSettings(); propertiesscannerType: IdCaptureScanner,acceptedDocuments/rejectedDocuments: List<IdCaptureDocument>,rejectExpiredIds,rejectIdsExpiringIn: Duration?,rejectVoidedIds,rejectHolderBelowAge: Int?,rejectionTimeoutSeconds: Int,rejectNotRealIdCompliant,rejectForgedAamvaBarcodes,rejectInconsistentData,decodeBackOfEuropeanDrivingLicense,anonymizationMode: IdAnonymizationMode,anonymizeDefaultFields; methodssetShouldPassImageTypeToResult(type, value)/getShouldPassImageTypeToResult(type),addAnonymizedField(document, fieldType)/removeAnonymizedField(...)/clearAnonymizedFields().IdCaptureScanner— base type of the three scanner classes.FullDocumentScanner()— no parameters, both sides/all zones.SingleSideScanner(barcode: Boolean = true, machineReadableZone: Boolean = true, visualInspectionZone: Boolean = true, freeFormText: Boolean = false).MobileDocumentScanner(iso180135: Boolean = false, ocr: Boolean = false, elementsToRetain: Set<MobileDocumentDataElement> = emptySet())(orMobileDocumentScanner.mobileDocumentScanner()).- Document types (
IdCaptureDocument, each takes a singleregion: IdCaptureRegion):IdCard,DriverLicense,Passport,VisaIcao,VisaLetter,ResidencePermit,HealthInsuranceCard;RegionSpecific(subtype: RegionSpecificSubtype). IdCaptureRegionenum —ANY,EU_AND_SCHENGEN, and ~250 region values (US,UK,UAE, …), allUPPER_SNAKE_CASE.IdCaptureListener—onIdCaptured(mode: IdCapture, id: CapturedId),onIdRejected(mode: IdCapture, id: CapturedId?, reason: RejectionReason).CapturedId—fullName/firstName/lastName(String?),sexType(Sex),dateOfBirth/dateOfExpiry/dateOfIssue(DateResult?),nationality,address,age(Int?),isExpired: Boolean?,document(IdCaptureDocument?),issuingCountry(IdCaptureRegion),documentNumber/documentAdditionalNumber,usRealIdStatus,isCitizenPassport,viz/mrz/barcode/mobileDocument/mobileDocumentOcr,images(IdImages),verificationResult(VerificationResult?),anonymizedFields(Set<IdFieldType>) /isAnonymized(field), plusisIdCard()/isDriverLicense()/isPassport()/isVisaIcao()/isVisaLetter()/isResidencePermit()/isHealthInsuranceCard()/isRegionSpecific(subtype).DateResult—day: Int?,month: Int?,year: Int.IdImages—face: CapturedImage?(property),frame: CapturedImage?(property, side-agnostic) andframe(side: IdSide): CapturedImage?(function, per-side),croppedDocument(side: IdSide): CapturedImage?.VerificationResult—dataConsistency: DataConsistencyResult?,aamvaBarcodeVerification: AamvaBarcodeVerificationResult?.DataConsistencyResult—allChecksPassed: Boolean,passedChecks/skippedChecks/failedChecks: Set<DataConsistencyCheck>,frontReviewImage: CapturedImage?.AamvaBarcodeVerificationResult—allChecksPassed: Boolean,status: AamvaBarcodeVerificationStatus(AUTHENTIC/LIKELY_FORGED/FORGED).IdCaptureOverlay—IdCaptureOverlay.withIdCapture(idCapture)/.withIdCaptureForView(idCapture, view);idLayoutStyle(IdLayoutStyle:ROUNDEDdefault /SQUARE),idLayoutLineStyle(IdLayoutLineStyle:LIGHTdefault /BOLD),textHintPosition(TextHintPosition:ABOVE_VIEWFINDER/BELOW_VIEWFINDER),showTextHints,setFrontSideTextHint(text)/setBackSideTextHint(text),capturedBrush/localizedBrush/rejectedBrush(Brush).IdCaptureFeedback—IdCaptureFeedback();idCaptured/idRejected(Feedback); staticdefaultFeedback().DataCaptureContext—DataCaptureContext.initialize(licenseKey, ...)(alsoforLicenseKey(...)and related overloads);setFrameSource(frameSource);addMode(mode)/removeMode(mode)/removeCurrentMode()/removeAllModes(); staticsharedInstance.Camera—Camera.getDefaultCamera(settings: CameraSettings): Camera?(nullable — no camera on some devices);switchToDesiredState(FrameSourceState.ON / OFF, callback).DataCaptureView— hosted per-platform; expose it to the UI layer via thetoAndroidView()/toUIView()extension functions shown inreferences/integration.md;addOverlay(overlay);logoStyle(LogoStyle).RejectionReasonenum —NOT_ACCEPTED_DOCUMENT_TYPE,INVALID_FORMAT,DOCUMENT_VOIDED,TIMEOUT,SINGLE_IMAGE_NOT_RECOGNIZED,DOCUMENT_EXPIRED,DOCUMENT_EXPIRES_SOON,NOT_REAL_ID_COMPLIANT,HOLDER_UNDERAGE,FORGED_AAMVA_BARCODE,INCONSISTENT_DATA,BLUETOOTH_COMMUNICATION_ERROR,BLUETOOTH_UNAVAILABLE,CLOUD_REQUEST_FAILED.IdAnonymizationModeenum —NONE,FIELDS_ONLY(default),IMAGES_ONLY,FIELDS_AND_IMAGES.IdImageTypeenum —FACE,CROPPED_DOCUMENT,FRAME.IdSideenum —FRONT,BACK.Sexenum —FEMALE,MALE,UNSPECIFIED.- Compose Multiplatform (
id-composemodule) —@Composable IdCaptureView(settings, modifier, overlayStyle, onCapture, onReject, ...). Seereferences/integration.mdfor details.
Available on the KMP SDK but NOT covered by this skill
- NFC chip reading — not part of the KMP SDK (
nfc/nfc-scanner.rstandnfc/nfc-result.rstdo not includekmpin their platform list). IdCaptureException,LocalizedOnlyId,PhysicalDocumentScanner(the standalone base-class page, distinct from the concrete scanners covered above), and the deserialization API (IdCaptureDeserializer/IdCaptureDeserializerListener) — none apply to KMP per the SDK documentation's platform filters.