KYC Docs Get credentials
Docs › Blink SDK › Android

Android

Drop-in identity verification for Android: CameraX capture screens for the document and the selfie. Your backend creates the session; the SDK runs the document and liveness steps.

package com.blink.kyc:blink-kyc-android:1.0.13 minSdk 24 host any Activity

Prepare the environment

Add the Blink Maven repository and the dependency:

settings.gradle.ktskotlin
dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        maven("https://blink-pay.net/maven/releases")   // Blink SDK repository
    }
}
app/build.gradle.ktskotlin
android {
    defaultConfig { minSdk = 24 }
}

dependencies {
    implementation("com.blink.kyc:blink-kyc-android:1.0.13")
}

The SDK declares the camera permission and its capture screens; they are merged into your manifest and the drop-in screens request the runtime permission themselves.

Verification flow

Get a session token from your backend (Session API), build a BlinkConfig, then present the capture screens:

VerifyActivity.ktkotlin
import com.blink.kyc.BlinkConfig
import com.blink.kyc.BlinkError
import com.blink.kyc.BlinkKyc
import com.blink.kyc.BlinkStepError
import com.blink.kyc.DocumentType

val config = BlinkConfig.builder(sessionToken)
    .baseUrl("https://kyc-api.blink-pay.net")
    .issuingCountry("SDN")                    // SDN | ARE
    .documentType(DocumentType.PASSPORT)      // PASSPORT | NATIONAL_ID
    .build()                                  // throws BlinkError (BLINK_CONFIG) on a bad value

BlinkKyc(config)
    .onProgress { step, state -> Log.d("kyc", "$step $state") }
    .present(this)                            // the SDK owns the camera UI
    .whenComplete { result, error ->
        runOnUiThread {
            when (val e = (error as? java.util.concurrent.CompletionException)?.cause ?: error) {
                null -> Log.i("kyc", "${result.result}")   // VERIFIED | REJECTED | REVIEW: a device copy
                is BlinkStepError -> Log.w("kyc", "${e.step}: ${e.code}")   // e.g. DOCUMENT_UNREADABLE
                is BlinkError -> Log.e("kyc", "${e.code} (${e.httpStatus})")  // switch on e.code
            }
        }
    }

Ask your backend for the authoritative result before acting on the device copy.

CallEffect
BlinkConfig.builder(sessionToken) … .build()The session settings: baseUrl, issuingCountry (SDN or ARE, default SDN), documentType (default PASSPORT), profile, locale ("ar" for Arabic), theme, strings, rejectExpiredDocuments, timeoutMs.
BlinkKyc(config)Create the client.
.document(type), .face()Limit the journey to these steps. With neither, the session's purpose decides. type must match the config.
.onProgress { step, state -> }Progress events, e.g. DOCUMENT_FRONT UPLOADING.
.present(activity)Run the journey on the built-in capture screens; returns a CompletableFuture<SessionResult>.
.capture(hooks).runAsync()Supply the media yourself (headless); same future.
.status()Read-only session progress from Blink. Blocking: call it off the main thread.

SessionResult carries result (VERIFIED, REJECTED or REVIEW) and detail.

Headless capture

Bring your own camera: return the document image for each side and the liveness frames, and the SDK handles challenges, uploads and the verdict. The hooks are called on a worker thread, so they may block.

HeadlessCapture.ktkotlin
BlinkKyc(config)
    .capture(object : CaptureHooks {
        override fun document(type: DocumentType, side: DocumentSide) =
            DocumentCapture(grabDocumentJpeg(side))            // FRONT, then BACK for an ID
        override fun liveness(actions: List<String>) =
            LivenessCapture(recordFrames(actions), null)       // JPEG frames for the server's actions
    })
    .runAsync()
    .whenComplete { result, error -> /* as above */ }

UI customisation

VerifyActivity.ktkotlin
val config = BlinkConfig.builder(sessionToken)
    .theme(BlinkTheme.builder()
        .accent(0xFF15803D.toInt())
        .introColor(0xFF143A68.toInt())
        .logo(BlinkImage.resource(R.drawable.bank_logo))
        .build())
    .locale("ar")                             // Blink's built-in Arabic, right to left
    .build()

Every field and the copy you can replace are in Customisation.

Next

Handle the device outcome and errors (SDK result), then read the authoritative decision on your backend (Result API).