---
title: "Android SDK guide | Veriff.com"
slug: "android-sdk-guide"
description: "Integrate with the Veriff Android SDK for seamless user verification."
updated: 2026-05-28T08:17:15Z
published: 2026-05-28T08:17:15Z
---

> ## Documentation Index
> Fetch the complete documentation index at: https://devdocs.veriff.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Android SDK guide

## Requirements and prerequisites

### Requirements

- **Veriff Android SDK needs your minSdkVersion to be of 26 or above**
- Kotlin `1.9.0` or above
- Supports `android-gradle-plugin` version `7.2.0` and above
- The SDK is using AndroidX support libraries

**List of all direct transitive dependencies**

- androidx.annotation:annotation:1.7.1
- androidx.appcompat:appcompat:1.6.1
- androidx.constraintlayout:constraintlayout:2.1.4
- androidx.core:core-ktx:1.9.0
- androidx.databinding:viewbinding:8.8.0
- androidx.exifinterface:exifinterface:1.3.7
- androidx.fragment:fragment-ktx:1.5.7
- androidx.lifecycle:lifecycle-runtime-ktx:2.5.1
- androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1
- androidx.vectordrawable:vectordrawable-seekable:1.0.0-beta01
- com.google.android.gms:play-services-mlkit-face-detection:17.1.0
- com.google.android.material:material:1.4.0
- com.google.auto.value:auto-value-annotations:1.7
- com.google.dagger:dagger:2.48.1
- com.squareup.okhttp3:logging-interceptor:4.10.0
- com.squareup.okhttp3:okhttp:4.10.0
- com.squareup.okio:okio:3.2.0
- com.squareup.okio:okio-jvm:3.2.0
- org.bouncycastle:bcprov-jdk18on:1.76
- org.bouncycastle:bcutil-jdk18on:1.76
- org.jetbrains.kotlin:kotlin-stdlib:1.9.0
- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.0
- org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4
- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4
- androidx.annotation:annotation-experimental:1.1.0
- androidx.concurrent:concurrent-futures:1.1.0
- androidx.core:core:1.9.0
- androidx.lifecycle:lifecycle-common:2.5.1
- androidx.lifecycle:lifecycle-livedata:2.5.1
- com.google.guava:listenablefuture:1.0
- androidx.compose:compose-bom:2023.08.00
- androidx.compose.foundation:foundation:1.5.0
- androidx.compose.foundation:foundation-layout:1.5.0
- androidx.compose.material3:material3:1.1.1
- androidx.compose.runtime:runtime:1.5.0
- androidx.compose.ui:ui:1.5.0
- com.google.android.play:integrity:1.3.0

### Prerequisites

- You have an active account with Veriff (see the [Getting Started](/v1/docs/getting-started) section)
- You have an integration with Veriff (see the [Getting Started](/v1/docs/getting-started) section)
- You have set up webhook(s) to get replies from Veriff (see the [Webhooks](/v1/docs/webhooks-guide) section)

---

## Add Android SDK to the project

#### Step 1: Add a new Maven destination

Open the root `build.gradle` file and add a new Maven destination to the repositories in the `allprojects` section:

KotlinGroovy

```kotlin
allprojects {
    repositories {
        maven { url = uri("https://cdn.veriff.me/android/") }
        google()
        mavenCentral()
    }
}
```

```groovy
allprojects {
   repositories {
       maven { url "https://cdn.veriff.me/android/" }
       google()
       mavenCentral()
   }
}
```

Alternatively, when using Gradle 7+, add the Maven repository to your `settings.gradle` file:

KotlinGroovy

```kotlin
dependencyResolutionManagement {
    repositories {
        maven { url = uri("https://cdn.veriff.me/android/") }
        google()
        mavenCentral()
    }
}
```

```groovy
dependencyResolutionManagement {
    repositories {
        maven { url "https://cdn.veriff.me/android/" }
        google()
        mavenCentral()
    }
}
```

#### Step 2: Add the Veriff SDK dependency

Add to the application `build.gradle` file:

KotlinGroovy

```kotlin
implementation("com.veriff:veriff-library:$latestVeriffAndroidSdkVersion")
```

```groovy
implementation 'com.veriff:veriff-library:$latestVeriffAndroidSdkVersion'
```

---

## Add permissions

The Veriff Android SDK automatically adds required permissions to your app through Android's manifest merger. You do not need to manually declare these permissions in your app's `AndroidManifest.xml`, they are included automatically when you integrate the SDK.

The SDK requires the following permissions to function properly.

### Runtime permissions (requires user approval)

| Permission | Purpose | When required? |
| --- | --- | --- |
| CAMERA | Capture photos and videos for identity verification | When the end-user starts verification flow |
| RECORD_AUDIO | Record video with audio during verification | When the end-user starts video recording |

### Normal permissions (auto-granted)

| Permission | Purpose | When required? / Details |
| --- | --- | --- |
| INTERNET | Upload verification media and communicate with Veriff servers | Required for all SDK operations |
| ACCESS_NETWORK_STATE | Check network connectivity before uploads | Ensures uploads don't fail on poor connections |
| WAKE_LOCK | Keep CPU running during background uploads | Prevents incomplete uploads when screen turns off (max 2 minutes) |
| NFC | Read NFC-enabled identity documents | Used for passport/ID card NFC reading feature. If enabled, you can set up [NFC data listener](/v1/docs/android-sdk-nfc-data-listener) to get real-time data. |
| VIBRATE | Provide haptic feedback | Enhances user experience during verification |

#### Note about FOREGROUND SERVICE permissions

Starting from **Android SDK v9.0.0**, the Veriff SDK **no longer declares** the following permissions in its AndroidManifest:

```xml
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
```

The SDK previously used a foreground service for data upload operations. This has been replaced with an alternative approach that does not require foreground service permissions.

**If your app was declaring** these permissions **ONLY because the Veriff SDK required** them, you **can safely remove** them from your app's `AndroidManifest.xml`.

### FAQ - Permissions

#### Can I reduce the number of permissions?

No. All permissions are required for core SDK functionality. Removing any permission will cause crashes.

#### Why does the SDK need WAKE_LOCK?

To ensure verification uploads complete even if the user locks their phone. Without it, uploads may fail on slow connections.

#### Is NFC permission required if we don't use NFC verification?

The permission is declared but harmless if unused. It's a "normal" permission (auto-granted) and will not prompt users.

#### Will these permissions affect App Store approval?

No, these are standard permissions for identity verification SDKs. Ensure your privacy policy accurately describes their usage.

---

## Start the verification flow

The verification flow must be launched from the `activity` class with a unique session (= `sessionURL`).

KotlinJava

```kotlin
import com.veriff.Sdk

val intent = Sdk.createLaunchIntent(activity, sessionUrl)
startActivityForResult(intent, REQUEST_CODE)
```

```java
import com.veriff.Sdk;

Intent intent = Sdk.createLaunchIntent(activity, sessionUrl);
startActivityForResult(intent, REQUEST_CODE);
```

| **Parameter** | **Description** |
| --- | --- |
| `REQUEST_CODE`* | An constant integer that is defined in your activity and identifies the request. It helps differentiate this request from others when the result is returned in the `onActivityResult`. A constant integer that identifies the request. It helps differentiate this request from others when the result is returned in the `onActivityResult` callback |
| `sessionUrl`* | A combination of the `base URL` and the `sessionToken`, created as soon as a verification session is created. You can find its value in the response payload of your <meta charset="utf-8">[POST /sessions](https://veriff-dev-documentation.document360.io/apidocs/v1sessions)[↗] call, as `verification.url`. See [Create a verification session](/v1/docs/how-to-generate-sessions-manually) article for more info. |

* Required parameter

> [!WARNING]
> A session is valid for 7 days and expires automatically after that.

### What happens after the end-user leaves the flow?

When the end-user leaves the verification flow, i.e. a session is canceled or finished, the Veriff SDK is closed. The end-user is taken back to your application. The **end-user flow completion status** is returned to the host application, see the [Get verification session status](/v1/docs/android-sdk-guide#verification-session-status) sub-section below for more info.

---

## Verification session status

When a session is cancelled or finished, the Veriff SDK is closed and the end-user is taken back to your application.

Callbacks about the **end-user flow completion status** are sent to your mobile application. To capture the status, override the `onActivityResult` method in your `activity` that started the verification flow.

> [!NOTE]
> **This is not the verification session decision.**
> 
> This indicates whether the end-user was able to complete all the steps in the verification flow or not.

KotlinJava

```kotlin
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    if (requestCode == REQUEST_CODE) {
        val result = Result.fromResultIntent(data)
        result?.let { 
            handleResult(it)
        }
    }
    super.onActivityResult(requestCode, resultCode, data)
}

private fun handleResult(result: Result) {
    when (result.status) {
        Status.DONE -> {
            // The end-user successfully submitted the session, the session is completed from their perspective
        }
        Status.CANCELED -> {
            // The end-user canceled the verification process
        }
        Status.ERROR -> {
            // An error occurred during the flow, Veriff has already shown UI, no need to display
            // a separate error message here
            Log.w(TAG, "Verification error occurred: ${result.error}")
        }
    }
}
```

```java
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == REQUEST_CODE) {
        Result result = Result.fromResultIntent(data);
        if (result != null) {
            handleResult(result); //see below for handling the result
        }
    }
    super.onActivityResult(requestCode, resultCode, data);
}

public void handleResult(Result result) {
    switch (result.getStatus()) {
    case DONE:
        // The end-user successfully submitted the session, the session is completed from their perspective
        break;
    case CANCELED:
        // The end-user canceled the verification process
        break;
    case ERROR:
        // An error occurred during the flow, Veriff has already shown UI, no need to display
        // a separate error message here
        Log.w(TAG, "Verification error occurred: " + result.getError());
        break;
    }
}
```

---

## Verification session decision

Veriff returns info about the verification session decision, extracted data, registries checks etc. via **webhooks**. You need to set up webhooks and tie them to your system.

→*See Webhooks Guide > [Set up webhooks](https://veriff-dev-documentation.document360.io/docs/webhooks-guide#set-up-webhooks) sub-section for detailed overview of the setup process*

---

## Add error logging

To turn on logging, add your logging implementation instance (instance of `com.veriff.Logger` class) to the SDK before launching the SDK.

KotlinJava

```kotlin
Sdk.setLogger(myLoggerImplementation)
val intent = Sdk.createLaunchIntent(activity, sessionUrl)
startActivityForResult(intent, REQUEST_CODE)
```

```java
Sdk.setLogger(myLoggerImplementation);
Intent intent = Sdk.createLaunchIntent(activity, sessionUrl);
startActivityForResult(intent, REQUEST_CODE);
```

---

## Customize the SDK

> [!NOTE]
> <meta charset="utf-8">SDK customization is optional

It is possible to customize the look and feel of the UI to match your branding. You can change primitives such as colors and fonts, set the language settings for the SDK, and change if/how an intro screen is displayed.

### Match the brand

You can **customize the look and feel**of the SDK flow by **passing a**`Branding`**object via**`Configuration`**to**`createLaunchIntent` as shown and explained in the example below.

Note: all custom values for branding are optional. If a value is not provided for them, the default Veriff color and logo will be used.

#### Branding properties list

| **Property** | **Type** | **Description** | **Usage** |
| --- | --- | --- | --- |
| `primary` | ColorInt | Main brand color used for primary actions and accents | Buttons, highlights |
| `secondary` | ColorInt | Secondary brand color for alternative actions | Secondary buttons |
| `background` | ColorInt | Main background color | Screen backgrounds |
| `onBackground` | ColorInt | Primary text/content color on background | Main text |
| `onBackgroundSecondary` | ColorInt | Secondary text color for supporting content | Hint text, secondary info |
| `onBackgroundTertiary` | ColorInt | Tertiary text color for less important content | Disabled text, placeholders |
| `cameraOverlay` | ColorInt | Background color for camera overlay | Camera screen background |
| `onCameraOverlay` | ColorInt | Text/content color on camera overlay | Camera screen text/content |
| `onCameraOverlaySecondary` | ColorInt | Secondary text/content color displayed on camera overlay | Camera screen text/content |
| `outline` | ColorInt | Border and divider color | Borders, separators |
| `onPrimary` | ColorInt | Content color on primary color backgrounds | Text on primary buttons |
| `onSecondary` | ColorInt | Content color on secondary color backgrounds | Text on secondary buttons |
| `feedbackSuccess` | ColorInt | Background color for success feedback | Background |
| `onFeedbackSuccess` | ColorInt | Content color on success feedback background | Text/content |
| `feedbackError` | ColorInt | Background color for error feedback | Background |
| `onFeedbackError` | ColorInt | Content color on error feedback background | Text/content |
| `buttonRadius` | Float (DP) | Corner radius for buttons in density pixels | Button styling |
| `logo` | DrawableRes | Brand logo resource ID | Header/branding display |
| `notificationIcon` | DrawableRes | Notification icon resource ID | Push notifications |
| `font` | Font | Custom font configuration (optional) | Typography styling |

Deprecated `Branding` colors (will stop being used in future versions):

| **Property** | **Type** | **Description** | **Usage** |
| --- | --- | --- | --- |
| `error` | ColorInt | Error state color | Error messages, feedback |
| `success` | ColorInt | Success state color | Success messages, feedback |

#### `Branding`**object options**

KotlinJava

```kotlin
import com.example.mysdk.Branding
import com.example.mysdk.Configuration
import com.example.mysdk.Sdk

// ... other imports

fun launchSdk() {
    val branding = Branding.Builder()
        .logo(R.drawable.logo)
        .background(resources.getColor(R.color.background_color))
        .onBackground(resources.getColor(R.color.on_background_color))
        .onBackgroundSecondary(resources.getColor(R.color.on_background_secondary_color))
        .onBackgroundTertiary(resources.getColor(R.color.on_background_tertiary_color))
        .primary(resources.getColor(R.color.primary_color))
        .onPrimary(resources.getColor(R.color.on_primary_color))
        .secondary(resources.getColor(R.color.secondary_color))
        .onSecondary(resources.getColor(R.color.on_secondary_color))
        .cameraOverlay(resources.getColor(R.color.camera_overlay_color))
        .onCameraOverlay(resources.getColor(R.color.on_camera_overlay_color))
        .onCameraOverlaySecondary(resources.getColor(R.color.on_camera_overlay_secondary_color))
        .outline(resources.getColor(R.color.outline_color))
        .feedbackSuccess(resources.getColor(R.color.success_background_color))
        .feedbackError(resources.getColor(R.color.error_background_color))
        .onFeedbackSuccess(resources.getColor(R.color.on_feedback_success_color))
        .onFeedbackError(resources.getColor(R.color.on_feedback_error_color))
        .error(resources.getColor(R.color.error_color))
        .buttonRadius(48f)
        .build()

    val configuration = Configuration.Builder()
        .branding(branding)
        .build()

    val intent = Sdk.createLaunchIntent(this, sessionUrl, configuration)
    startActivityForResult(intent, REQUEST_CODE)
}
```

```java
Branding branding = new Branding.Builder()
        .logo(R.drawable.logo)
        // Screen and dialog background color
        .background(getResources().getColor(R.color.background_color))
        // Non-surface content (such as text, icons) displayed on `background`.
        // Accessibility: Must have a contrast ratio of at least 4.5:1 vs `background`
        .onBackground(getResources().getColor(R.color.on_background_color))
        // Secondary non-surface content (such as text) displayed on `background`.
        // Accessibility: Must have a contrast ratio of at least 4.5:1 vs `background`
        .onBackgroundSecondary(getResources().getColor(R.color.on_background_secondary_color))
        // Tertiary non-surface content (such as text) displayed on `background`.
        // Accessibility: Must have a contrast ratio of at least 4.5:1 vs `background`
        .onBackgroundTertiary(getResources().getColor(R.color.on_background_tertiary_color))
        // Primary surfaces (such as buttons) displayed on `background`.
        // Accessibility: Must have a contrast ratio of at least 3:1 vs `background`
        .primary(getResources().getColor(R.color.primary_color))
        // Non-surface content (such as text) displayed on `primary` surfaces.
        // Accessibility: Must have a contrast ratio of at least 4.5:1 vs `primary`
        .onPrimary(getResources().getColor(R.color.on_primary_color))
        // Secondary surfaces (such as bullet points and illustrations) displayed on `background`.
        // Accessibility: Must have a contrast ratio of at least 3:1 vs `background`
        .secondary(getResources().getColor(R.color.secondary_color))
        // Non-surface content (such as text) displayed on `secondary` surfaces.
        // Accessibility: Must have a contrast ratio of at least 4.5:1 vs `secondary`
        .onSecondary(getResources().getColor(R.color.on_secondary_color))
        // Backgroung color of the overlay area on all the screens with camera
        .cameraOverlay(getResources().getColor(R.color.camera_overlay_color))
        // Secondary color for elements on all the screens with camera on top of camera overlay
        .onCameraOverlaySecondary(getResources().getColor(R.color.on_camera_overlay_secondary_color))
        // Color used for various outlines and boundaries of UI elements
        .outline(getResources().getColor(R.color.outline_color))
        // Success background
        .feedbackSuccess(getResources().getColor(R.color.success_color))
        // Success content on top of success background color
        .onFeedbackSuccess(getResources().getColor(R.color.on_success_color))
        // Error background
        .feedbackError(getResources().getColor(R.color.error_color))
        // Error content on top of feedback error background color
        .onFeedbackError(getResources().getColor(R.color.on_error_color))
        // Button corner radius, in `dp`
        .buttonRadius(48f)
        .build();

Configuration configuration = new Configuration.Builder()
        .branding(branding)
        .build();

Intent intent = Sdk.createLaunchIntent(activity, sessionUrl, configuration);
startActivityForResult(intent, REQUEST_CODE);
```

*→ See the [Visual SDK customization guides](https://devdocs.veriff.com/docs/sdk-guide#visual-sdk-customization-guides) article for design examples*

#### Font

You can **customize the fonts**used in the SDK by **passing the resource IDs of the fonts you want to use**. Make sure that you have **added** the **font files**to the **font resource folder** in your app.

A custom font can be set by passing a `com.veriff.Font` object to the `font` method of `Branding` builder. The `com.veriff.Font` builder accepts three types of fonts via the `setRegular`, `setMedium` and `setBold` methods.

KotlinJava

```kotlin
brandingBuilder.font = com.veriff.Font.Builder()
    .setRegular(R.font.comic_neue_regular)
    .setMedium(R.font.comic_neue_medium)
    .setBold(R.font.comic_neue_bold)
    .build()
```

```java
brandingBuilder.font(
    new com.veriff.Font.Builder()
        .setRegular(R.font.comic_neue_regular)
        .setMedium(R.font.comic_neue_medium)
        .setBold(R.font.comic_neue_bold)
        .build()
);
```

### Intro screen

It is possible to customise if the end-user flow begins with Veriff intro screen or no intro screen at all.

In some cases, it is possible to skip the Veriff’s generic introduction screen and use your own before Veriff’s SDK is launched.

> [!NOTE]
> <meta charset="utf-8">Check with your **Solutions Engineer** to confirm if this feature can be enabled for your integration

If it is possible, then:

- You agree your own introduction screen visuals and copy with our Solutions Engineer and get relevant legal documents signed in case they are needed
- After that Veriff will enable custom introduction screen from backend for your integrations
- After you have implemented your own introduction screen you can change the configuration option specified below

KotlinJava

```kotlin
val configuration = Configuration.Builder()
    .customIntroScreen(true)
    .build()
```

```java
Configuration configuration = new Configuration.Builder()
        .customIntroScreen(true)
        .build();
```

> [!CAUTION]
> Adding the configuration alone in your app is not enough to enable the custom intro screen. Make sure to contact your solutions engineer so they can enable the feature for your integration.

### User interface locale settings

You can set a `locale(java.util.Locale)` for the SDK from the app itself.

KotlinJava

```kotlin
val appLocale = Locale.ENGLISH
val configuration = Configuration.Builder()
    .locale(appLocale)
    .build()

val intent = Sdk.createLaunchIntent(activity, sessionUrl, configuration)
startActivityForResult(intent, REQUEST_CODE)
```

```java
Locale appLocale = Locale.ENGLISH;
Configuration configuration = new Configuration.Builder()
        .locale(appLocale)
        .build();

Intent intent = Sdk.createLaunchIntent(activity, sessionUrl, configuration);
startActivityForResult(intent, REQUEST_CODE);
```

*→ See*[*Supported languages in SDKs*](/v1/docs/sdk-guide#supported-languages)*article for more info*

### Light/dark mode

**Light/dark mode is supported through branding settings** (`Branding` object), but not through automatic system detection. You can define two sets of branding colors and manually switch between them based on the end-user's system preference. This means that **runtime theme switching is not supported**: if the end-user flow is already started with provided set of colors, changing theme in the setting will not be reflected in the SDK.

In a nutshell it works as follows:

1. You create two separate `Branding` configurations (one for light, one for dark) for your host app.
2. Your host app detects the end-user’s system's dark/light mode.
3. You pass the appropriate branding configuration when launching the Veriff SDK.

*→ See the*[*table with branding properties*](/v1/docs/android-sdk-guide#branding-properties-list)*above for list of available branding properties.*

#### Step 1: create two branding configurations

**Light mode**

The sample code below uses placeholder color values. Ensure that you use your brand colors.

**Kotlin**

```kotlin
val lightBranding = Branding.Builder()
    // Background Colors
    .background(Color.WHITE)  // #FFFFFF
    
    // Primary Brand Colors
    .primary(Color.parseColor("#03282C"))      // Dark teal
    .secondary(Color.parseColor("#14E5C5"))    // Light teal/cyan
    
    // Content on Light Background
    .onBackground(Color.parseColor("#03282C"))       // Dark text
    .onBackgroundSecondary(Color.parseColor("#757575")) // Gray text
    .onBackgroundTertiary(Color.parseColor("#11615C"))  // Muted text
    
    // Primary Button Colors
    .onPrimary(Color.WHITE)   // White text on primary
    .onSecondary(Color.parseColor("#03282C"))  // Dark text on secondary
    
    // Camera Screen
    .cameraOverlay(Color.parseColor("#03282C"))    // Dark overlay
    .onCameraOverlay(Color.WHITE)                  // White text
    .onCameraOverlaySecondary(Color.parseColor("#CBFAF3")) // Light teal
    
    // Feedback & Borders
    .outline(Color.parseColor("#919191"))      // Gray borders
    .feedbackSuccess(Color.parseColor("#BEF6D2")) // Light Green background
    .feedbackError(Color.parseColor("#FDCED8")) // Light Red background
    .onFeedbackSuccess(Color.parseColor("#085423")) // Green content
    .onFeedbackError(Color.parseColor("#610519")) // Red content
    
    // Styling
    .buttonRadius(48f)  // Fully rounded buttons
    .logo(R.drawable.my_logo_light)
    .notificationIcon(R.drawable.ic_notification)
    
    .build()
```

**Java**

```java
Branding lightBranding = new Branding.Builder()
    // Background Colors
    .background(Color.WHITE)  // #FFFFFF

    // Primary Brand Colors
    .primary(Color.parseColor("#03282C"))      // Dark teal
    .secondary(Color.parseColor("#14E5C5"))    // Light teal/cyan

    // Content on Light Background
    .onBackground(Color.parseColor("#03282C"))          // Dark text
    .onBackgroundSecondary(Color.parseColor("#757575")) // Gray text
    .onBackgroundTertiary(Color.parseColor("#11615C"))  // Muted text

    // Primary Button Colors
    .onPrimary(Color.WHITE)                            // White text on primary
    .onSecondary(Color.parseColor("#03282C"))           // Dark text on secondary

    // Camera Screen
    .cameraOverlay(Color.parseColor("#03282C"))         // Dark overlay
    .onCameraOverlay(Color.WHITE)                       // White text
    .onCameraOverlaySecondary(Color.parseColor("#CBFAF3")) // Light teal

    // Feedback & Borders
    .outline(Color.parseColor("#919191"))               // Gray borders
    .feedbackSuccess(Color.parseColor("#BEF6D2")) // Light Green background
    .feedbackError(Color.parseColor("#FDCED8")) // Light Red background
    .onFeedbackSuccess(Color.parseColor("#085423")) // Green content
    .onFeedbackError(Color.parseColor("#610519")) // Red content

    // Styling
    .buttonRadius(48f)  // Fully rounded buttons
    .logo(R.drawable.my_logo_light)
    .notificationIcon(R.drawable.ic_notification)

    .build();
```

**Dark mode**

The sample code below uses placeholder color values. Ensure that you use your brand colors.

**Kotlin**

```kotlin
val darkBranding = Branding.Builder()
    // Background Colors
    .background(Color.parseColor("#03282C"))  // Dark background
    
    // Primary Brand Colors (More vibrant in dark mode)
    .primary(Color.parseColor("#14E5C5"))     // Bright teal/cyan
    .secondary(Color.parseColor("#14E5C5"))   // Same bright teal
    
    // Content on Dark Background
    .onBackground(Color.WHITE)                    // White text
    .onBackgroundSecondary(Color.parseColor("#B0B0B0"))  // Light gray
    .onBackgroundTertiary(Color.parseColor("#14E5C5"))   // Brand color accent
    
    // Primary Button Colors
    .onPrimary(Color.parseColor("#03282C"))  // Dark text on bright primary
    .onSecondary(Color.WHITE)                // White text on secondary
    
    // Camera Screen
    .cameraOverlay(Color.BLACK)              // Pure black overlay
    .onCameraOverlay(Color.WHITE)            // White text
    .onCameraOverlaySecondary(Color.parseColor("#CBFAF3")) // Light teal

    // Feedback & Borders
    .outline(Color.parseColor("#505050"))      // Dark gray borders
    .feedbackSuccess(Color.parseColor("#085423")) // Dark Green background
    .feedbackError(Color.parseColor("#610519")) // Dark Red background
    .onFeedbackSuccess(Color.parseColor("#98E8B4")) // Green content
    .onFeedbackError(Color.parseColor("#FA94AA")) // Red content
    
    // Styling
    .buttonRadius(48f)
    .logo(R.drawable.my_logo_dark)  // Light-colored logo for dark background
    .notificationIcon(R.drawable.ic_notification)
    
    .build()
```

**Java**

```java
Branding darkBranding = new Branding.Builder()
    // Background Colors
    .background(Color.parseColor("#03282C"))  // Dark background

    // Primary Brand Colors (More vibrant in dark mode)
    .primary(Color.parseColor("#14E5C5"))     // Bright teal/cyan
    .secondary(Color.parseColor("#14E5C5"))   // Same bright teal

    // Content on Dark Background
    .onBackground(Color.WHITE)                             // White text
    .onBackgroundSecondary(Color.parseColor("#B0B0B0"))   // Light gray
    .onBackgroundTertiary(Color.parseColor("#14E5C5"))    // Brand color accent

    // Primary Button Colors
    .onPrimary(Color.parseColor("#03282C"))  // Dark text on bright primary
    .onSecondary(Color.WHITE)               // White text on secondary

    // Camera Screen
    .cameraOverlay(Color.BLACK)             // Pure black overlay
    .onCameraOverlay(Color.WHITE)           // White text
    .onCameraOverlaySecondary(Color.parseColor("#CBFAF3")) // Light teal

    // Feedback & Borders
    .outline(Color.parseColor("#505050"))    // Dark gray borders
    .feedbackSuccess(Color.parseColor("#085423")) // Dark Green background
    .feedbackError(Color.parseColor("#610519")) // Dark Red background
    .onFeedbackSuccess(Color.parseColor("#98E8B4")) // Green content
    .onFeedbackError(Color.parseColor("#FA94AA")) // Red content

    // Styling
    .buttonRadius(48f)
    .logo(R.drawable.my_logo_dark)  // Light-colored logo for dark background
    .notificationIcon(R.drawable.ic_notification)

    .build();
```

#### Step 2: detect system theme

The sample code below uses placeholder values. Your production code must reference your real values.

**Kotlin**

```kotlin
import android.content.res.Configuration

fun isSystemDarkModeEnabled(context: Context): Boolean {
    val isDarkMode = when (context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) {
        Configuration.UI_MODE_NIGHT_YES -> true
        Configuration.UI_MODE_NIGHT_NO -> false
        else -> false
    }
    return isDarkMode
}

// Usage in Activity
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    
    val isDarkMode = isSystemDarkModeEnabled(this)
    val branding = if (isDarkMode) darkBranding else lightBranding
    
    // Pass branding to Veriff SDK
    startVeriffFlow(branding)
}
```

**Java**

```java
import android.content.Context;
import android.content.res.Configuration;

public static boolean isSystemDarkModeEnabled(Context context) {
    int uiMode = context.getResources().getConfiguration().uiMode
            & Configuration.UI_MODE_NIGHT_MASK;

    if (uiMode == Configuration.UI_MODE_NIGHT_YES) {
        return true;
    } else if (uiMode == Configuration.UI_MODE_NIGHT_NO) {
        return false;
    } else {
        return false;
    }
}

// Usage in Activity
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    boolean isDarkMode = isSystemDarkModeEnabled(this);
    Branding branding = isDarkMode ? darkBranding : lightBranding;

    // Pass branding to Veriff SDK
    startVeriffFlow(branding);
}
```

#### Step 3: launch app with appropriate branding configuration

The sample code below uses placeholder values. Your production code must reference your real values.

**Kotlin**

```kotlin
class MyActivity : AppCompatActivity() {
    
    // Define theme configurations
    private val lightBranding = Branding.Builder()
        .background(Color.WHITE)
        .primary(Color.parseColor("#03282C"))
        .secondary(Color.parseColor("#14E5C5"))
        .onBackground(Color.parseColor("#03282C"))
        .onBackgroundSecondary(Color.parseColor("#757575"))
        .onBackgroundTertiary(Color.parseColor("#11615C"))
        .onPrimary(Color.WHITE)
        .onSecondary(Color.parseColor("#03282C"))
        .cameraOverlay(Color.parseColor("#03282C"))
        .onCameraOverlay(Color.WHITE)
        .onCameraOverlaySecondary(Color.parseColor("#CBFAF3"))
        .outline(Color.parseColor("#919191"))
        .feedbackSuccess(Color.parseColor("#BEF6D2")) 
        .feedbackError(Color.parseColor("#FDCED8"))
        .onFeedbackSuccess(Color.parseColor("#085423"))
        .onFeedbackError(Color.parseColor("#610519"))
        .buttonRadius(48f)
        .logo(R.drawable.logo_light)
        .build()
    
    private val darkBranding = Branding.Builder()
        .background(Color.parseColor("#03282C"))
        .primary(Color.parseColor("#14E5C5"))
        .secondary(Color.parseColor("#14E5C5"))
        .onBackground(Color.WHITE)
        .onBackgroundSecondary(Color.parseColor("#B0B0B0"))
        .onBackgroundTertiary(Color.parseColor("#14E5C5"))
        .onPrimary(Color.parseColor("#03282C"))
        .onSecondary(Color.WHITE)
        .cameraOverlay(Color.BLACK)
        .onCameraOverlay(Color.WHITE)
        .onCameraOverlaySecondary(Color.parseColor("#0F3C43"))
        .outline(Color.parseColor("#505050"))
        .feedbackSuccess(Color.parseColor("#085423"))
        .feedbackError(Color.parseColor("#610519"))
        .onFeedbackSuccess(Color.parseColor("#98E8B4")) 
        .onFeedbackError(Color.parseColor("#FA94AA"))
        .buttonRadius(48f)
        .logo(R.drawable.logo_dark)
        .build()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        // Detect system theme and apply appropriate branding
        val branding = getThemeAwareBranding()
        
        // Launch Veriff with appropriate branding
        launchVeriff(branding)
    }
    
    private fun getThemeAwareBranding(): Branding {
        val isDarkMode = when (resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) {
            Configuration.UI_MODE_NIGHT_YES -> true
            Configuration.UI_MODE_NIGHT_NO -> false
            else -> false
        }
        
        return if (isDarkMode) darkBranding else lightBranding
    }
    
    private fun launchVeriff(branding: Branding) {
        val sessionBundle = Bundle().apply {
            putString("SESSION_TOKEN", "your_session_token")
            // Pass branding to Veriff SDK
        }
        
        // Start Veriff activity with branding
        // Implementation depends on your Veriff SDK integration method
    }
}
```

**Java**

```java
import android.content.res.Configuration;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;

public class MyActivity extends AppCompatActivity {

    // Define theme configurations
    private final Branding lightBranding = new Branding.Builder()
        .background(Color.WHITE)
        .primary(Color.parseColor("#03282C"))
        .secondary(Color.parseColor("#14E5C5"))
        .onBackground(Color.parseColor("#03282C"))
        .onBackgroundSecondary(Color.parseColor("#757575"))
        .onBackgroundTertiary(Color.parseColor("#11615C"))
        .onPrimary(Color.WHITE)
        .onSecondary(Color.parseColor("#03282C"))
        .cameraOverlay(Color.parseColor("#03282C"))
        .onCameraOverlay(Color.WHITE)
        .onCameraOverlaySecondary(Color.parseColor("#CBFAF3"))
        .outline(Color.parseColor("#919191"))
        .feedbackSuccess(Color.parseColor("#BEF6D2")) 
        .feedbackError(Color.parseColor("#FDCED8"))
        .onFeedbackSuccess(Color.parseColor("#085423"))
        .onFeedbackError(Color.parseColor("#610519"))
        .buttonRadius(48f)
        .logo(R.drawable.logo_light)
        .build();

    private final Branding darkBranding = new Branding.Builder()
        .background(Color.parseColor("#03282C"))
        .primary(Color.parseColor("#14E5C5"))
        .secondary(Color.parseColor("#14E5C5"))
        .onBackground(Color.WHITE)
        .onBackgroundSecondary(Color.parseColor("#B0B0B0"))
        .onBackgroundTertiary(Color.parseColor("#14E5C5"))
        .onPrimary(Color.parseColor("#03282C"))
        .onSecondary(Color.WHITE)
        .cameraOverlay(Color.BLACK)
        .onCameraOverlay(Color.WHITE)
        .onCameraOverlaySecondary(Color.parseColor("#0F3C43"))
        .outline(Color.parseColor("#505050"))
        .feedbackSuccess(Color.parseColor("#085423"))
        .feedbackError(Color.parseColor("#610519"))
        .onFeedbackSuccess(Color.parseColor("#98E8B4")) 
        .onFeedbackError(Color.parseColor("#FA94AA"))
        .buttonRadius(48f)
        .logo(R.drawable.logo_dark)
        .build();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Detect system theme and apply appropriate branding
        Branding branding = getThemeAwareBranding();

        // Launch Veriff with appropriate branding
        launchVeriff(branding);
    }

    private Branding getThemeAwareBranding() {
        int uiMode = getResources().getConfiguration().uiMode
                & Configuration.UI_MODE_NIGHT_MASK;

        boolean isDarkMode = (uiMode == Configuration.UI_MODE_NIGHT_YES);

        return isDarkMode ? darkBranding : lightBranding;
    }

    private void launchVeriff(Branding branding) {
        Bundle sessionBundle = new Bundle();
        sessionBundle.putString("SESSION_TOKEN", "your_session_token");
        // Pass branding to Veriff SDK

        // Start Veriff activity with branding
        // Implementation depends on your Veriff SDK integration method
    }
}
```

#### Multiple theme presets management

For managing multiple theme presets, use a data class.

The sample code below uses placeholder values. If you decide to create a data class, your production code must reference the real class name.

**Kotlin**

```kotlin
@Serializable
data class ThemeConfig(
    val name: String,
    @ColorInt val primary: Int,
    @ColorInt val secondary: Int,
    @ColorInt val background: Int,
    @ColorInt val onBackground: Int,
    @ColorInt val onBackgroundSecondary: Int,
    @ColorInt val onBackgroundTertiary: Int,
    @ColorInt val cameraOverlay: Int,
    @ColorInt val onCameraOverlay: Int,
    @ColorInt val onCameraOverlaySecondary: Int,
    @ColorInt val outline: Int,
    @ColorInt val feedbackSuccess: Int,
    @ColorInt val onFeedbackSuccess: Int,
    @ColorInt val feedbackError: Int,
    @ColorInt val onFeedbackError: Int,
    val buttonRadius: Float = 8f,
) {
    fun toBranding(): Branding = Branding.Builder()
        .primary(primary)
        .secondary(secondary)
        .background(background)
        .onBackground(onBackground)
        .onBackgroundSecondary(onBackgroundSecondary)
        .onBackgroundTertiary(onBackgroundTertiary)
        .cameraOverlay(cameraOverlay)
        .onCameraOverlay(onCameraOverlay)
        .onCameraOverlaySecondary(onCameraOverlaySecondary)
        .outline(outline)
        .feedbackSuccess(feedbackSuccess)
        .onFeedbackSuccess(onFeedbackSuccess)
        .feedbackError(feedbackError)
        .onFeedbackError(onFeedbackError)
        .onPrimary(onPrimary)
        .onSecondary(onSecondary)
        .buttonRadius(buttonRadius)
        .build()
}

// Usage
val themes = mapOf(
    "light" to ThemeConfig("light", /* colors... */),
    "dark" to ThemeConfig("dark", /* colors... */)
)

val selectedTheme = if (isDarkMode) themes["dark"] else themes["light"]
val branding = selectedTheme?.toBranding()
```

**Java**

```java
// ThemeConfig.java
public class ThemeConfig {

    public final String name;
    public final int primary;
    public final int secondary;
    public final int background;
    public final int onBackground;
    public final int onBackgroundSecondary;
    public final int onBackgroundTertiary;
    public final int cameraOverlay;
    public final int onCameraOverlay;
    public final int onCameraOverlaySecondary;
    public final int outline;
    public final int onPrimary;
    public final int onSecondary;
    public final int feedbackSuccess;
    public final int onFeedbackSuccess;
    public final int feedbackError;
    public final int onFeedbackError;
    public final float buttonRadius;

    public ThemeConfig(
            String name,
            int primary,
            int secondary,
            int background,
            int onBackground,
            int onBackgroundSecondary,
            int onBackgroundTertiary,
            int cameraOverlay,
            int onCameraOverlay,
            int onCameraOverlaySecondary,
            int outline,
            int feedbackSuccess,
            int onFeedbackSuccess,
            int feedbackError,
            int onFeedbackError,
            int onPrimary,
            int onSecondary,
            float buttonRadius
    ) {
        this.name = name;
        this.primary = primary;
        this.secondary = secondary;
        this.background = background;
        this.onBackground = onBackground;
        this.onBackgroundSecondary = onBackgroundSecondary;
        this.onBackgroundTertiary = onBackgroundTertiary;
        this.cameraOverlay = cameraOverlay;
        this.onCameraOverlay = onCameraOverlay;
        this.onCameraOverlaySecondary = onCameraOverlaySecondary;
        this.outline = outline;
        this.error = error;
        this.feedbackSuccess = feedbackSuccess;
        this.onFeedbackSuccess = onFeedbackSuccess;
        this.feedbackError = feedbackError;
        this.onFeedbackError = onFeedbackError;
        this.onPrimary = onPrimary;
        this.onSecondary = onSecondary;
        this.buttonRadius = buttonRadius;
    }

    // Overload with default buttonRadius = 8f
    public ThemeConfig(String name, int primary, int secondary, int background,
            int onBackground, int onBackgroundSecondary, int onBackgroundTertiary,
            int cameraOverlay, int onCameraOverlay, int onCameraOverlaySecondary, int outline, 
            int feedbackError, int onFeedbackError, int feedbackSuccess, int onFeedbackSuccess, 
            int onPrimary, int onSecondary) {
        this(name, primary, secondary, background, onBackground,
             onBackgroundSecondary, onBackgroundTertiary, cameraOverlay,
             onCameraOverlay, onCameraOverlaySecondary, outline,
             feedbackSuccess, onFeedbackSuccess, feedbackError, onFeedbackError 
             onPrimary, onSecondary, 8f);
    }

    public Branding toBranding() {
        return new Branding.Builder()
            .primary(primary)
            .secondary(secondary)
            .background(background)
            .onBackground(onBackground)
            .onBackgroundSecondary(onBackgroundSecondary)
            .onBackgroundTertiary(onBackgroundTertiary)
            .cameraOverlay(cameraOverlay)
            .onCameraOverlay(onCameraOverlay)
            .onCameraOverlaySecondary(onCameraOverlaySecondary)
            .outline(outline)
            .feedbackSuccess(feedbackSuccess)
            .onFeedbackSuccess(onFeedbackSuccess)
            .feedbackError(feedbackError)
            .onFeedbackError(onFeedbackError)
            .onPrimary(onPrimary)
            .onSecondary(onSecondary)
            .buttonRadius(buttonRadius)
            .build();
    }
}

// Usage
Map<String, ThemeConfig> themes = new HashMap<>();
themes.put("light", new ThemeConfig("light", /* colors... */));
themes.put("dark",  new ThemeConfig("dark",  /* colors... */));

ThemeConfig selectedTheme = isDarkMode ? themes.get("dark") : themes.get("light");
Branding branding = selectedTheme != null ? selectedTheme.toBranding() : null;
```

#### **Best practices**

- Define both light and dark variants for all colors
- Test with actual system dark mode toggle
- Ensure sufficient contrast for accessibility (WCAG AA minimum)
- Store theme configurations for easy updates
- Use proper color values that work in both themes
- Do not use hardcoded colors without theme consideration
- Ensure you have defined `onCameraOverlay` for camera screens

---

## Need to exclude ML Kit support?

Veriff Android SDK uses [ML Kit](https://developers.google.com/ml-kit)[↗] for things like automatic selfie capture and accessibility feedback. It uses on-device models that are pre-downloaded by Play services.

If your app cannot use Play services at all, then you can exclude the transitive `mlkit` dependency. This will remove any dependencies on ML Kit and disable the use of these ML modules at runtime.

KotlinGroovy

```kotlin
implementation("com.veriff:veriff-library:6.+") {
    exclude(group = "com.veriff", module = "mlkit")
}
```

```groovy
implementation('com.veriff:veriff-library:6.+') {
    exclude group: 'com.veriff', module: 'mlkit'
}
```

---

## Changelog

| Date | Description |
| --- | --- |
| May 28, 2026 | Info about `foreground_service` and `foreground_service_data_sync` removed from several sections: - from [Normal permissions (auto-granted)](/v1/docs/android-sdk-guide#normal-permissions-autogranted) table - Foreground service permissions in Play store - FAQ section [Note about FOREGROUND SERVICE permissions](/v1/docs/android-sdk-guide#note-about-foreground-service-permissions) added |
| May 7, 2026 | Updated `Branding` with new and deprecated color tokens |
| Mar 9, 2026 | - Kotlin dependency updated to 1.9.0 - List of direct transitive dependencies updated - [Light/dark mode](/v1/docs/android-sdk-guide#lightdark-mode) info added - `Branding` properties [table](/v1/docs/android-sdk-guide#branding-properties-list) added |
| Feb 3, 2026 | Link to new article "[Android SDK: NFC data listener](/v1/docs/android-sdk-nfc-data-listener)” added to [Normal permissions (auto-granted)](/v1/docs/android-sdk-guide#normal-permissions-autogranted) table |
| Jan 19, 2026 | Details added to [Add permissions](/v1/docs/android-sdk-guide#add-permissions) section: - tables with runtime permissions and “normal” permissions added - any mention of removing permissions removed - FAQ about permissions added |
| Mar 12, 2025 | Documentation published |

## Related

- [Create verification session](/how-to-generate-sessions-manually.md)
- [Intro to the API](/quick-guide-of-idv-using-the-api.md)
- [Webhooks Guide](/webhooks-guide.md)
- [Backwards compatible changes](/backwards-compatible-changes.md)
- [Android SDK migration guide](/android-sdk-migration-guide.md)
- [Android SDK changelog](/android-sdk-changelog.md)
