Quick start: Android
Add the Kotlin SDK, start it with your licence, run a verification from CameraX frames and send the result to your backend.
The Android SDK (tz.co.digid.sdk) is a Kotlin library over the digid engine. It gives you the engine, a CameraX analyzer and typed results; you build the screens. If you want ready-made screens in a native Android app, the Flutter flow can be embedded with Flutter's add-to-app: see Quick start: Flutter.
1. Add the SDK
The SDK is an Android library (AAR) in two variants:
- bundled: the models are packed into the library. Start with this one.
- nomodels: no models inside; you supply them from a directory (for example, downloaded with Play Asset Delivery).
Customers get the library from the customer portal: see Get the SDK. It needs minSdk 24 and Java 17, and the analyzer needs androidx.camera:camera-core in your app. The SDK has no network code and requests no permissions; your app asks for android.permission.CAMERA.
2. Start the engine with your licence
Create one engine per process, off the main thread (loading the models takes a second or more):
import tz.co.digid.sdk.*
val digid = Digid.create(context) // models from the bundled variant
val status = digid.applyLicence(licenceToken) // checked offline, never throws
if (status.isEvaluation) Log.w("digid", "evaluation mode: ${status.reason}")
The licence is checked against your app's package name and signing certificate, read from the running app. Without a valid licence the engine runs in evaluation mode. See Licences and renewal.
3. Run a verification
Open a session per attempt and feed it camera frames. NidaFrameAnalyzer converts CameraX frames and pushes them in:
val session = digid.newSession(Flow.CardThenSelfie(requireBack = false))
val analysis = ImageAnalysis.Builder()
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.build()
analysis.setAnalyzer(executor, NidaFrameAnalyzer(session, mirrored = false) { update ->
runOnUiThread {
guidanceText.text = Digid.guidanceText(update.guidance, Lang.EN)
if (update.step == StepKind.SELFIE) switchToFrontCamera() // rebind with mirrored = true
}
update.result?.let { result -> onVerified(result, session.finishJson()) }
})
- Use
Flow.CardOnly()to read the card without a selfie. - When the step becomes
SELFIE, switch to the front camera and create the analyzer withmirrored = true. Digid.guidanceTextgives the English or Swahili line for every guidance code.digid.readCardImage(NidaImage.fromBitmap(bitmap))reads a card from one photo instead of the camera.
More on sessions, steps and guidance: Custom screens on the engine.
4. Read the result
result is a KycResult:
when (result.overall) {
Overall.PASS -> showDone()
Overall.REVIEW -> showPendingReview()
else -> showRetry()
}
result.evaluation is true without a valid licence. result.checks lists every check, and result.face holds the face match verdict.
5. Send the result to your backend
session.finishJson() returns the engine's result as JSON text, the same JSON every digid SDK produces. Send it as it is over your own authenticated connection:
val request = Request.Builder()
.url("https://api.example.co.tz/kyc")
.post(resultJson.toRequestBody("application/json".toMediaType()))
.build()
The
digid.kyc/1document, with its tamper check, is built by the Flutter and Web SDKs. The native Android SDK hands you the engine result JSON, which carries the same card fields, checks, verdict and face match. Your backend should still apply the checks in Verify on your backend that do not depend on the document's tamper check.
6. Report usage
digid.usageSender = UsageSender { report -> postJson("https://digid.co.tz/api/v1/usage", """{"report":"$report"}""") }
digid.setUsageStore(myStore) // keeps the counts across restarts
digid.online() // blocking: call off the main thread when the app is online
See Licences and renewal for the store, renewal and the "no report, no renewal" rule.