
Verilive Android SDK is a library that allows you to integrate liveness verification into your Android application. It provides a simple API based on Android's Activity Result Contract pattern.
Min SDK: 24 (Android 7.0)
Compile SDK: 36
Permissions: Camera (android.permission.CAMERA)
Create a session in your backend by making a POST request to /liveness2/session endpoint.
curl -X POST https://services.<region>.verigram.kz/liveness2/session \
-H 'Content-Type: application/json' \
-H 'X-Verigram-Api-Version: 2.0' \
-H 'X-Verigram-Api-Key: your-test-api-key' \
-d '{ "person_id": "end-user-id" }'
The endpoint returns a JSON object containing session_id, access_token, person_id, and flow_id. Pass these values directly to your Android app.
Register the Activity Result launcher using VeriliveLauncherContract:
val veriliveLauncher = registerForActivityResult(VeriliveLauncherContract()) { result ->
if (result.success) {
// Verification succeeded
// Fetch the final verdict from your backend using flow_id
} else {
// Verification failed, cancelled, or errored
val errorCode = result.errorCode
val errorMessage = result.errorMessage
}
}
Launch the verification with the parameters obtained from your backend:
veriliveLauncher.launch(
VeriliveParams(
baseUrl = "", // empty string for default, or your on-premises URL
personId = "your-person-id",
accessToken = "your-access-token",
flowId = "your-flow-id",
sessionId = "your-session-id",
)
)
Handle the result
If result.success == true, you must fetch the final verdict from your backend using the flow_id. The Android SDK does not provide the liveness verdict directly. This behavior is deliberate; frontend results are not reliable and can be tampered with by an attacker.
To get the results of the Flow, make a GET request to the endpoint /flow/{flow_id}/result:
curl -X GET https://services.<region>.verigram.kz/flow/{flow-id}/result \
-H 'Content-Type: application/json' \
-H 'X-Verigram-Api-Version: 2.0' \
-H 'X-Verigram-Api-Key: your-test-api-key'
VeriliveLauncherContractAn ActivityResultContract<VeriliveParams, VeriliveResult> that launches the liveness verification flow and returns the result.
Usage:
val launcher = registerForActivityResult(VeriliveLauncherContract()) { result ->
// handle result
}
In Jetpack Compose:
val launcher = rememberLauncherForActivityResult(VeriliveLauncherContract()) { result ->
// handle result
}
VeriliveParamsConfiguration for starting a verification flow. Implements Parcelable.
data class VeriliveParams(
val baseUrl: String,
val personId: String,
val accessToken: String,
val flowId: String,
val sessionId: String,
val attemptsLeft: Int? = null,
) : Parcelable
Fields:
baseUrl: String — API endpoint URL. Use an empty string "" for the default endpoint. Set to your on-premises URL if applicable.
personId: String — The unique identifier for the individual undergoing verification.
accessToken: String — Authentication token obtained from the backend.
flowId: String — The flow identifier for the verification session.
sessionId: String — A unique ID for the current session/attempt.
attemptsLeft: Int? — Optional. Number of retry attempts allowed (default: null).
VeriliveResultThe result returned after the verification flow completes.
data class VeriliveResult(
val success: Boolean,
val errorCode: String?,
val errorMessage: String?,
)
Fields:
success: Boolean — true if the liveness check passed, false otherwise (failed, cancelled, or errored).
errorCode: String? — Machine-readable error code for programmatic handling. See Error Codes below. null when success == true.
errorMessage: String? — Human-readable error description. null when success == true.
VeriliveActivityThe Activity that hosts the verification UI. You don't need to interact with it directly — use VeriliveLauncherContract instead.
Declared in the SDK's AndroidManifest.xml:
<activity
android:name=".VeriliveActivity"
android:exported="false" />
Errors are communicated through VeriliveResult. Use errorCode for programmatic handling and errorMessage for display.
|
| Meaning |
|---|---|---|
|
| Verification succeeded |
|
| Liveness check did not pass |
|
| Backend returned an error |
|
| Network connectivity issue |
|
| User pressed back or cancelled |
|
| Unexpected SDK error |
Error Code | Description |
|---|---|
| The liveness check did not pass. The user's face was not verified as live. |
| The backend returned an error response during the verification process. |
| A network connectivity issue prevented the verification from completing. |
| The user cancelled the verification (back button). |
| An unexpected internal error occurred. |
Example:
val launcher = registerForActivityResult(VeriliveLauncherContract()) { result ->
if (result.success) {
// Success — fetch result from backend using flow_id
Log.d("Verilive", "Verification passed")
} else {
when (result.errorCode) {
"CANCELLED_BY_USER" -> Log.i("Verilive", "User cancelled")
"LIVENESS_FAILED" -> Log.w("Verilive", "Liveness failed: ${result.errorMessage}")
else -> Log.e("Verilive", "Error: ${result.errorCode}, ${result.errorMessage}")
}
}
}
The SDK supports two retry modes:
When the liveness check fails and attemptsLeft > 0, the SDK automatically shows a retry screen and handles the retry internally via the server endpoint.
veriliveLauncher.launch(
VeriliveParams(
baseUrl = "",
personId = personId,
accessToken = accessToken,
flowId = flowId,
sessionId = sessionId,
attemptsLeft = 3,
)
)
Set Verilive.onRetry before launching the SDK to handle retry logic yourself. The callback is called on a background thread after the user taps "Retry", so you can make synchronous network calls.
import org.verigram.verilive.Verilive
import org.verigram.verilive.VeriliveRetryResult
Verilive.onRetry = { params ->
// Request new session credentials from your backend (runs on background thread)
val response = URL("https://your-api.com/retry-session")
.openConnection().let { /* ... */ }
VeriliveRetryResult(
sessionId = response.sessionId,
accessToken = response.accessToken,
attemptsLeft = params.attemptsLeft - 1,
)
}
veriliveLauncher.launch(VeriliveParams(...))
If the callback returns null or throws an exception, the SDK falls back to server-managed retry.
The SDK requires camera access. The permission is declared in the SDK's manifest and will be merged into your app's manifest automatically:
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
The SDK handles camera permission requests internally. If the user denies camera access, the verification cannot proceed.
class MainActivity : AppCompatActivity() {
private val veriliveLauncher = registerForActivityResult(
VeriliveLauncherContract()
) { result ->
if (result.success) {
Log.d("Verilive", "Verification passed")
// Fetch result from your backend
} else {
Log.w("Verilive", "Failed: ${result.errorCode}, ${result.errorMessage}")
}
}
private fun startVerification(
personId: String,
accessToken: String,
flowId: String,
sessionId: String,
) {
veriliveLauncher.launch(
VeriliveParams(
baseUrl = "",
personId = personId,
accessToken = accessToken,
flowId = flowId,
sessionId = sessionId,
)
)
}
}
@Composable
fun VerificationScreen() {
val launcher = rememberLauncherForActivityResult(
VeriliveLauncherContract()
) { result ->
if (result.success) {
// verification passed
} else {
// result.errorCode, result.errorMessage
}
}
Button(onClick = {
launcher.launch(
VeriliveParams(
baseUrl = "",
personId = "person-id",
accessToken = "access-token",
flowId = "flow-id",
sessionId = "session-id",
attemptsLeft = 3,
)
)
}) {
Text("Start Verification")
}
}