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

# Android SDK для верификации личности на Kotlin

> Android SDK на Kotlin от Didit: верификация личности, проверка живости и NFC-паспортов. От $0.33 за KYC-проверку, $10 бесплатно в месяц.



Промпт для интеграции Android SDK
        Скопировать промпт в буфер обмена



      
      

\<pre>

\`Integrate Didit identity verification into my Android app using the native Kotlin SDK.

\## Requirements
\- Android API 23+ (Android 6.0 Marshmallow). ML-based auto-detection (document/face) requires API 24+ and falls back to manual capture below that.
\- Kotlin 1.9+
\- NFC reading requires NFC-capable device hardware

\## Installation

\### Step 1: Add Maven Repository
In settings.gradle.kts:
dependencyResolutionManagement {
repositories {
    google()
    mavenCentral()
    maven { url = uri("https://raw.githubusercontent.com/didit-protocol/sdk-android/main/repository") }
}
}

\### Step 2: Add Dependency
(!) Regarding the version ensure you check the [GitHub repository](https://github.com/didit-protocol/sdk-android) for the latest version.
In app/build.gradle.kts:
dependencies {
implementation("me.didit:didit-sdk:4.13.2")
}

\### Step 3: Add Packaging Exclusion (required to avoid build conflicts)
In the android { } block of app/build.gradle.kts:
android {
packaging {
    resources {
        excludes += "META-INF/versions/9/OSGI-INF/MANIFEST.MF"
    }
}
}

\## Permissions (auto-merged from SDK manifest)
\- INTERNET (required) — API communication
\- ACCESS_NETWORK_STATE (required) — Network availability
\- CAMERA (required) — Document scanning and face verification
\- NFC (optional) — Read NFC chips in passports/IDs

Camera and NFC are declared as optional features (android:required="false"), so the app installs on all devices.

\## Initialization (required)
In your Application class:
import me.didit.sdk.DiditSdk

class MyApp : Application() {
override fun onCreate() {
    super.onCreate()
    DiditSdk.initialize(this)
}
}

Register in AndroidManifest.xml:
\<application android:name=".MyApp" ...>

\## Backend: Create Session (your server)
POST https://verification.didit.me/v3/session/
Headers: { "x-api-key": DIDIT_API_KEY, "Content-Type": "application/json" }
Body: { "workflow_id": DIDIT_WORKFLOW_ID, "vendor_data": "user-id" }
Response: { "session_id": "uuid", "session_token": "short-token", "url": "https://verify.didit.me/session/short-token", "status": "Not Started" }
Send session_token to the Android app (never expose DIDIT_API_KEY to the device).

\## Jetpack Compose Integration
import me.didit.sdk.DiditSdk
import me.didit.sdk.DiditSdkState
import me.didit.sdk.VerificationResult

// Observe SDK state and launch UI when ready
LaunchedEffect(Unit) {
DiditSdk.state.collect { state ->
    when (state) {
        is DiditSdkState.Ready -> DiditSdk.launchVerificationUI(activity)
        is DiditSdkState.Error -> Log.e("Didit", "Error: state.message")
        else -> { /* Loading, Idle, CreatingSession */ }
    }
}
}

// Start verification with session token from backend
import me.didit.sdk.VerificationStatus

fun startVerification(token: String) {
DiditSdk.startVerification(token = token) { result ->
    when (result) {
        is VerificationResult.Completed -> when (result.session.status) {
            VerificationStatus.APPROVED -> { /* grant access */ }
            VerificationStatus.DECLINED -> { /* show retry */ }
            VerificationStatus.PENDING  -> { /* under review */ }
        }
        is VerificationResult.Cancelled -> { /* user cancelled */ }
        is VerificationResult.Failed    -> { /* show error: result.error.message */ }
    }
}
}

\## Result Statuses (VerificationStatus enum; .rawValue gives "Approved" / "Pending" / "Declined")
\- VerificationStatus.APPROVED — user identity verified, grant access
\- VerificationStatus.DECLINED — verification failed, show retry or contact support
\- VerificationStatus.PENDING  — needs manual review by your compliance team

\## Full Verification Results
The SDK only returns the status. Full decision data (document fields, face match scores, AML hits, etc.) arrives via webhook to your backend.
Set up webhooks: https://docs.didit.me/integration/webhooks

\## Rate Limits (session creation)
\- 600 session-create requests / minute per x-api-key (see https://docs.didit.me/integration/rate-limiting)
\- On 429, retry honouring Retry-After / X-RateLimit-* headers

\## Environment Variables (backend only — never expose in the Android app)
\- DIDIT_API_KEY — from Didit Console > API & Webhooks
\- DIDIT_WORKFLOW_ID — from Didit Console > Workflows

\## Docs
\- Android SDK: https://docs.didit.me/integration/native-sdks/android-sdk
\- GitHub: https://github.com/didit-protocol/sdk-android
\- Webhooks: https://docs.didit.me/integration/webhooks
\- API Reference: https://docs.didit.me/api-reference/overview`

\</pre>



      
      

Вставьте в

Легковесный SDK для Android, который управляется с сервера и для работы требует минимальной настройки. Он предназначен для верификации личности.> [card] [Репозиторий на GitHub](https://github.com/didit-protocol/sdk-android)
>
> Смотрите исходный код и примеры на GitHub> [!NOTE]
>
> ### **Нативный SDK**: Это рекомендуемый подход для Android-приложений. Нативные SDK обеспечивают лучший пользовательский опыт, оптимизированную работу с камерой и полную поддержку NFC.***## Требования| Требование | Минимальная версия |
| - | - |
| Android API | 23+ (Android 6.0 Marshmallow). Автоопределение с помощью ML требует API 24+ (см. ниже). |
| Kotlin | 1.9+ |
| Jetpack Compose | Входит как транзитивная зависимость |> [!NOTE]
>
> **Доступность функций по уровню API**: автоопределение документов и лиц требует API 24+ (собственные легковесные детекторы Didit, входящие в `didit-sdk-core`). На устройствах с API 23 SDK переходит к ручному захвату с кнопкой спуска затвора с таймером. Все остальные функции работают во всём диапазоне API 23+.> [!NOTE]
>
> **Биометрическое согласие требует нативный SDK версии 4.10.0 или новее.** Для людей, на которых распространяется законодательство Иллинойса, Техаса или Колорадо, Didit может запросить явное [биометрическое согласие](https://docs.didit.me/ru/getting-started/biometric-consent.md) перед первым захватом лица. Android SDK 4.10.0 и новее показывают этот шаг. Более ранние версии не могут его показать: их сессии выполняются как прежде, а решение сообщает, что согласие не собрано (`biometric_consent.reason: "BIOMETRIC_CONSENT_UNSUPPORTED_SDK"`). Обновитесь, если вы верифицируете людей в этих штатах.***## Установка### Шаг 1: Добавьте репозиторийДобавьте репозиторий Didit Maven в блок `repositories` вашего файла `settings.gradle.kts`:```kotlin theme={null}
dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        maven { url = uri("https://raw.githubusercontent.com/didit-protocol/sdk-android/main/repository") }
    }
}
```Или, если используется `settings.gradle` (Groovy):```groovy theme={null}
dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        maven { url "https://raw.githubusercontent.com/didit-protocol/sdk-android/main/repository" }
    }
}
```### Шаг 2: Добавьте зависимостьДобавьте зависимость SDK в файл `build.gradle.kts` вашего приложения:
(!) Перед установкой проверьте в [репозитории GitHub](https://github.com/didit-protocol/sdk-android), какая версия последняя.```kotlin theme={null}
dependencies {
    implementation("me.didit:didit-sdk:4.13.2")
}
```Или, если используется `build.gradle` (Groovy):```groovy theme={null}
dependencies {
    implementation "me.didit:didit-sdk:4.13.2"
}
```#### Варианты SDKAndroid SDK поставляется в виде отдельных артефактов, поэтому ваше приложение занимает в бинарном коде только столько места, сколько нужно для используемых функций. `me.didit:didit-sdk` выше — это полный пакет. Выберите подходящий вариант:| Артефакт | Автоопределение (ML) | Чтение NFC-чипа | Когда использовать |
| - | :-: | :-: | - |
| `me.didit:didit-sdk-core` | ✓ | – | самая компактная сборка; автоматический захват с помощью собственных детекторов Didit, без чтения чипа паспорта или ID-документа |
| `me.didit:didit-sdk-autodetection` | ✓ | – | устарел и пуст начиная с 4.11.0; зависит только от `didit-sdk-core`, поэтому используйте вместо него `didit-sdk-core` |
| `me.didit:didit-sdk-nfc` | ✓ | ✓ | чтение чипа поверх `didit-sdk-core` |
| `me.didit:didit-sdk` | ✓ | ✓ | все функции (полный пакет) |Начиная с `4.6.0`, нативная подпись кошелька WalletConnect поставляется как отдельное необязательное дополнение `me.didit:didit-sdk-wallet`.
Полный пакет `me.didit:didit-sdk` уже включает его, поэтому при установке пакета ничего не меняется.
Если вы используете один из облегчённых артефактов и хотите, чтобы подтверждение владения кошельком выполнялось нативно внутри приложения, добавьте `me.didit:didit-sdk-wallet` рядом с ним. Без этого дополнения SDK завершает подтверждение владения кошельком через защищённый сценарий в браузере, так что функция работает в любом случае.
Перенос WalletConnect из `didit-sdk-core` также убрал несколько мегабайт зависимостей для кошельков из каждой сборки, которой они не нужны.Все артефакты публикуются в одной версии, поэтому для всех используйте `4.13.2`.#### Как уменьшить размер загрузкиМодели обнаружения, которые используются для автозахвата, загружаются во время работы приложения и кэшируются, поэтому они никогда не учитываются в размере вашего приложения.
Автозахват добавляет в приложение нативную библиотеку ML-рантайма, а её размер зависит от архитектуры процессора: примерно 10 МБ для `arm64-v8a`, 7 МБ для `armeabi-v7a` и 13 МБ для каждой из двух x86-архитектур эмуляторов.Чтобы заплатить за это только один раз:1) **Публикуйте Android App Bundle (AAB).** Google Play доставляет только архитектуру и языки конкретного устройства, поэтому телефон на arm64 устанавливает примерно 10 МБ нативного кода (около 4 МБ в сжатом виде при загрузке). Универсальный APK содержит все четыре архитектуры и весит более 40 МБ, что чаще всего и приводит к неожиданно большим показателям размера.

2) **Если вы распространяете один APK**, а ваша аудитория — телефоны и планшеты на ARM, ограничьте включаемые архитектуры:

   ```kotlin theme={null}
   android {
       defaultConfig {
           ndk { abiFilters += listOf("arm64-v8a", "armeabi-v7a") }
       }
   }
   ```

   Сохраните все ABI, которые реально используют ваши пользователи: добавьте `x86_64`, если вы поддерживаете Chromebook или другие x86-устройства, либо публикуйте APK с разделением по ABI, чтобы ни одно устройство не потеряло поддержку.

3) **Включите R8**: установите `isMinifyEnabled = true` и `isShrinkResources = true` в типе сборки release (`minifyEnabled true` и `shrinkResources true`, если вы используете Groovy `build.gradle`). SDK поставляется с собственными consumer-правилами, поэтому неиспользуемый код SDK удаляется без дополнительной настройки.

4) **Ограничьте включаемые языки** с помощью `resConfigs(...)`, если вам не нужны все 48 локалей SDK в сборке APK. Разделение AAB по языкам делает это автоматически в Google Play.### Шаг 3: Добавьте исключение упаковкиДобавьте это в блок `android` вашего приложения, чтобы избежать конфликтов сборки:```kotlin theme={null}
android {
    packaging {
        resources {
            excludes += "META-INF/versions/9/OSGI-INF/MANIFEST.MF"
        }
    }
}
```Вот и всё! Gradle автоматически разрешит все транзитивные зависимости.***## РазрешенияSDK требует следующие разрешения. Они объявлены в `AndroidManifest.xml` SDK и автоматически добавляются в манифест вашего приложения:| Разрешение | Источник AAR | Описание | Обязательно |
| - | - | - | - |
| `INTERNET` | `didit-sdk-core` | Доступ к сети для обмена данными с API | Да |
| `ACCESS_NETWORK_STATE` | `didit-sdk-core` | Определение доступности сети | Да |
| `CAMERA` | `didit-sdk-core` | Сканирование документов и проверка лица | Да |
| `NFC` | `didit-sdk-nfc` | Считывание NFC-чипов в паспортах и ID-картах | Автоматически добавляется из `didit-sdk-nfc` (транзитивная зависимость `me.didit:didit-sdk`) |### Функции камеры и NFCSDK объявляет `android.hardware.camera` и `android.hardware.nfc` как необязательные функции (`android:required="false"`). Благодаря этому ваше приложение можно установить на устройства без камеры или NFC-модуля. SDK корректно обработает отсутствие этого оборудования во время работы.***## Быстрый старт### Шаг 1: Инициализация SDKИнициализируйте SDK в `Application.onCreate()`:```kotlin theme={null}
import me.didit.sdk.DiditSdk

class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        DiditSdk.initialize(this)
    }
}
```### Шаг 2: Запуск верификации и обработка результатов```kotlin theme={null}
import me.didit.sdk.DiditSdk
import me.didit.sdk.DiditSdkState
import me.didit.sdk.VerificationResult

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Observe SDK state to launch UI when ready
        lifecycleScope.launch {
            DiditSdk.state.collect { state ->
                when (state) {
                    is DiditSdkState.Ready -> DiditSdk.launchVerificationUI(this@MainActivity)
                    is DiditSdkState.Error -> Log.e("Didit", "Error: ${state.message}")
                    else -> { /* Loading, Idle, CreatingSession */ }
                }
            }
        }
    }

    private fun startVerification() {
        // Method 1: UniLink (no backend required)
        DiditSdk.startVerification(
            workflowId = "your-workflow-id",
            vendorData = "user-123"
        ) { result ->
            handleResult(result)
        }

        // Method 2: Backend Session (recommended for production)
        // DiditSdk.startVerification(
        //     token = "your-session-token"
        // ) { result ->
        //     handleResult(result)
        // }
    }

    private fun handleResult(result: VerificationResult) {
        when (result) {
            is VerificationResult.Completed -> {
                Log.d("Didit", "Session: ${result.session.sessionId}")
                Log.d("Didit", "Status: ${result.session.status}")
            }
            is VerificationResult.Cancelled -> {
                Log.d("Didit", "User cancelled")
            }
            is VerificationResult.Failed -> {
                Log.e("Didit", "Failed: ${result.error.message}")
            }
        }
    }
}
```***## Способы интеграцииSDK поддерживает два способа интеграции:### Способ 1: UniLink (самый простой)Бэкенд не нужен. SDK создаёт сессию напрямую, используя ID вашего сценария из Didit Console. Доступен только `vendorData`.```kotlin theme={null}
DiditSdk.startVerification(
    workflowId = "your-workflow-id",
    vendorData = "user-123"
) { result ->
    handleResult(result)
}
```### Способ 2: Сессия на бэкенде (рекомендуется для продакшна)Ваш бэкенд создаёт сессию через [API создания сессии верификации](https://docs.didit.me/ru/sessions-api/create-session.md) (`POST /v3/session/`) с полной поддержкой параметров (`contact_details`, `expected_details`, `metadata`, `callback` и других), после чего передаёт `session_token` в SDK.```kotlin theme={null}
// Your backend creates a session and returns the token
val sessionToken = yourBackend.createVerificationSession(userId = currentUser.id)

// Pass the token to the SDK
DiditSdk.startVerification(
    token = sessionToken
) { result ->
    handleResult(result)
}
```Этот подход даёт вам полный контроль над следующими возможностями:- Связывание сессий с вашими пользователями (`vendor_data`)
- Задание пользовательских метаданных
- Настройка callback для каждой сессии
- Передача контактных данных и ожидаемых данных для перекрёстной проверки***## КонфигурацияНастройте поведение SDK:```kotlin theme={null}
import me.didit.sdk.Configuration
import me.didit.sdk.core.localization.SupportedLanguage

val configuration = Configuration(
    languageLocale = SupportedLanguage.SPANISH,  // Force Spanish language
    showLanguageSelector = true,                  // Let the user change language
    fontFamily = "my_custom_font",                // Custom font (must be in res/font/)
    loggingEnabled = true                         // Enable debug logging
)

DiditSdk.startVerification(
    token = "your-session-token",
    configuration = configuration
) { result ->
    handleResult(result)
}
```### Параметры конфигурации| Свойство | Тип | По умолчанию | Описание |
| - | - | - | - |
| `languageLocale` | `SupportedLanguage?` | Локаль устройства | Принудительно задаёт определённый язык |
| `showLanguageSelector` | `Boolean` | `false` | Показывает на экране приветствия селектор языка в стиле веб-версии |
| `fontFamily` | `String?` | Системный шрифт | Имя пользовательского ресурса шрифта (из `res/font/`) |
| `loggingEnabled` | `Boolean` | `false` | Включает отладочное логирование SDK |
| `showCloseButton` | `Boolean` | `true` | Показывает кнопку закрытия (X) на экранах шагов верификации |
| `showExitConfirmation` | `Boolean` | `true` | Показывает диалог подтверждения, когда пользователь пытается выйти |
| `closeOnComplete` | `Boolean` | `false` | Автоматически закрывает интерфейс верификации по завершении (аналог в Web SDK: `closeModalOnComplete`) |
| `defaultDocumentCamera` | `CameraLens` | `CameraLens.BACK` | Объектив, который используется при открытии экрана съёмки документа |
| `defaultLivenessCamera` | `CameraLens` | `CameraLens.FRONT` | Объектив, который используется при открытии экрана съёмки лица (проверка живости) |
| `showDocumentCameraSwitchButton` | `Boolean` | `true` | Показывает переключатель камеры на экране съёмки документа |
| `showLivenessCameraSwitchButton` | `Boolean` | `true` | Показывает переключатель камеры на экране съёмки лица |### Параметры камерыПараметры камеры совпадают с [параметрами камеры Web SDK](https://docs.didit.me/ru/integration/web-sdks/javascript-sdk.md#параметры-камеры).
Каждый объектив задаётся как `CameraLens.FRONT` или `CameraLens.BACK`.
Если на устройстве нет запрошенного объектива, SDK использует первую камеру, которая у него есть.
Переключатель камеры также скрывается на устройствах с одной камерой.
Чтобы закрепить за пользователем объектив по умолчанию для этого шага, установите для переключателя значение `false`.```kotlin theme={null}
val configuration = Configuration(
    defaultLivenessCamera = CameraLens.BACK,
    showLivenessCameraSwitchButton = false
)
```> [!NOTE]
>
> Параметры `showLanguageSelector`, `showCloseButton`, `showExitConfirmation` и `closeOnComplete` совпадают с `DiditSdkConfiguration` Web SDK. Параметры `languageLocale` и `fontFamily` специфичны для мобильного SDK, потому что мобильный SDK отрисовывает весь интерфейс верификации нативно (в отличие от Web SDK, который делегирует отображение размещённому фронтенду внутри iframe).> [!NOTE]
>
> **Тема и цвета**: цвета, фоны и настройки начальных экранов задаются в [настройках White label](https://business.didit.me) в Didit Console, а не в конфигурации SDK. Так обеспечивается единый брендинг на всех платформах.***## Поддержка языковSDK поддерживает **54 языка**. Если язык не указан, SDK использует локаль устройства, а если локаль не поддерживается, использует английский язык.```kotlin theme={null}
// Use device locale (default)
val config = Configuration()

// Force specific language
val config = Configuration(languageLocale = SupportedLanguage.FRENCH)

// Let the user change language on the welcome screen
val config = Configuration(showLanguageSelector = true)
```**[Посмотреть все поддерживаемые языки →](https://docs.didit.me/ru/integration/supported-languages.md)*****## Расширенные параметрыДля расширенных параметров сессии (`contact_details`, `expected_details`, `metadata`, `callback`) используйте способ **Сессия на бэкенде**. Ваш бэкенд вызывает [API создания сессии верификации](https://docs.didit.me/ru/sessions-api/create-session.md) с полным набором параметров, а затем передаёт `session_token` в SDK.***## Обработка результатовSealed-класс `VerificationResult` содержит результат верификации:### Варианты результата| Вариант | Описание |
| - | - |
| `Completed(session)` | Процесс верификации завершён (результат смотрите в `session.status`) |
| `Cancelled(session)` | Пользователь отменил процесс верификации |
| `Failed(error, session)` | Во время верификации произошла ошибка |### Свойства SessionData| Свойство | Тип | Описание |
| - | - | - |
| `sessionId` | `String` | Уникальный идентификатор сессии |
| `status` | `VerificationStatus` | `APPROVED`, `PENDING` или `DECLINED` |### Типы ошибокВсе ошибки являются sealed-подклассами `me.didit.sdk.VerificationError`:| Ошибка | Описание |
| - | - |
| `SessionExpired` | Срок действия сессии истёк |
| `NetworkError` | Проблема с сетевым подключением |
| `CameraAccessDenied` | Разрешение на использование камеры не предоставлено |
| `NotInitialized` | SDK не инициализирован: сначала вызовите `DiditSdk.initialize(context)` |
| `ApiError` | Бэкенд вернул ответ с ошибкой |
| `RetryBlocked` | Повторную верификацию выполнить нельзя (например, достигнуто максимальное число попыток) |
| `Unknown(message)` | Прочая ошибка с сообщением |### Потеря соединенияЕсли устройство теряет соединение во время верификации, SDK показывает экран «Нет подключения к интернету» вместо того, чтобы оставлять текущий шаг в состоянии загрузки. SDK продолжает проверять соединение в фоновом режиме и сам закрывает экран, как только Didit снова становится доступен. Пользователь продолжает с того шага, на котором остановился.
На экране есть кнопка **Try Again**, и если `showCloseButton` равен `true` (по умолчанию), кнопка **Exit Verification**.
Выход завершает верификацию с `VerificationResult.Failed(error = VerificationError.NetworkError, session = ...)`, поэтому ваше приложение узнает, что пользователь вышел из-за потери соединения.### Полный пример обработки результата```kotlin theme={null}
DiditSdk.startVerification(token = "your-token") { result ->
    when (result) {
        is VerificationResult.Completed -> {
            when (result.session.status) {
                VerificationStatus.APPROVED -> {
                    Log.d("Didit", "Approved! Session: ${result.session.sessionId}")
                    // User is verified - grant access
                }
                VerificationStatus.PENDING -> {
                    Log.d("Didit", "Under review. Session: ${result.session.sessionId}")
                    // Show "verification in progress" UI
                }
                VerificationStatus.DECLINED -> {
                    Log.d("Didit", "Declined. Session: ${result.session.sessionId}")
                    // Handle declined verification
                }
            }
        }
        is VerificationResult.Cancelled -> {
            Log.d("Didit", "Cancelled: ${result.session?.sessionId ?: "unknown"}")
            // User chose to cancel - maybe show retry option
        }
        is VerificationResult.Failed -> {
            Log.e("Didit", "Error: ${result.error.message}")
            // Handle error - show retry or contact support
        }
    }
}
```***## Наблюдение за состоянием SDKВы можете отслеживать состояние SDK, например для собственного интерфейса загрузки:```kotlin theme={null}
lifecycleScope.launch {
    DiditSdk.state.collect { state ->
        when (state) {
            is DiditSdkState.Idle -> {
                // Ready to start verification
            }
            is DiditSdkState.CreatingSession -> {
                // Show "Creating session..." progress
            }
            is DiditSdkState.Loading -> {
                // Show loading indicator
            }
            is DiditSdkState.Ready -> {
                // Verification UI is ready - launch it
                DiditSdk.launchVerificationUI(this@MainActivity)
            }
            is DiditSdkState.Error -> {
                // Show error message
                Log.e("Didit", "Error: ${state.message}")
            }
        }
    }
}
```***## ProGuard / R8SDK включает собственные consumer-правила ProGuard. Дополнительная настройка не требуется.Если вы используете полный режим R8, возможно, потребуется добавить в `gradle.properties` следующее:```properties theme={null}
android.enableR8.fullMode=false
```***## Пример от начала до конца (сессия на бэкенде → Android SDK → результат)Этот подход подходит для продакшена. Ваш бэкенд создаёт сессию, ваше Android-приложение получает `session_token`, а SDK запускает процесс верификации.### 1. Бэкенд: создание сессии```ts theme={null}
// Node.js / Express example. Run on your server. NEVER ship DIDIT_API_KEY to the device.
import express from "express";

const app = express();
app.use(express.json());

app.post("/api/verification/start", async (req, res) => {
  const response = await fetch("https://verification.didit.me/v3/session/", {
    method: "POST",
    headers: {
      "x-api-key": process.env.DIDIT_API_KEY!,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      workflow_id: process.env.DIDIT_WORKFLOW_ID,
      vendor_data: req.body.userId, // your stable user id
    }),
  });

  if (!response.ok) {
    return res.status(500).json({ error: `Didit ${response.status}` });
  }

  const { session_id, session_token } = await response.json();
  // Persist session_id <-> userId so you can match the webhook later.
  res.json({ session_id, session_token });
});
```### 2. Android: обмен токеном и запуск SDK```kotlin theme={null}
import android.app.Activity
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import me.didit.sdk.DiditSdk
import me.didit.sdk.VerificationResult
import me.didit.sdk.VerificationStatus
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.MediaType.Companion.toMediaType
import org.json.JSONObject

class VerificationFlow(private val activity: ComponentActivity) {
    private val http = OkHttpClient()
    private val json = "application/json".toMediaType()

    fun start(userId: String) {
        activity.lifecycleScope.launch {
            val token = withContext(Dispatchers.IO) {
                val body = JSONObject().put("userId", userId).toString().toRequestBody(json)
                val req = Request.Builder()
                    .url("https://your-backend.example.com/api/verification/start")
                    .post(body)
                    .build()
                http.newCall(req).execute().use { resp ->
                    JSONObject(resp.body!!.string()).getString("session_token")
                }
            }

            DiditSdk.startVerification(token = token) { result ->
                when (result) {
                    is VerificationResult.Completed -> when (result.session.status) {
                        VerificationStatus.APPROVED -> { /* optimistic UI; rely on webhook for source of truth */ }
                        VerificationStatus.PENDING  -> { /* show "under review" */ }
                        VerificationStatus.DECLINED -> { /* show retry/contact support */ }
                    }
                    is VerificationResult.Cancelled -> { /* user cancelled */ }
                    is VerificationResult.Failed    -> { /* show error: result.error.message */ }
                }
            }
        }
    }
}
```### 3. Бэкенд: получение итогового решения через webhookРезультат SDK удобен для отображения в интерфейсе, но окончательное решение приходит через webhook. Подробнее о проверке HMAC см. в [руководстве по webhook](https://docs.didit.me/ru/integration/webhooks.md).```ts theme={null}
app.post("/api/webhooks/didit", express.raw({ type: "application/json" }), (req, res) => {
  // 1. Verify the X-Signature header (see Webhooks docs)
  // 2. Parse the body
  const event = JSON.parse(req.body.toString());
  if (event.webhook_type === "status.updated") {
    // event.session_id, event.status ("Approved" | "Declined" | "In Review" | ...)
    // Look up the user by session_id and update their verification state.
  }
  res.sendStatus(200);
});
```***## Полный пример```kotlin theme={null}
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch
import me.didit.sdk.Configuration
import me.didit.sdk.DiditSdk
import me.didit.sdk.DiditSdkState
import me.didit.sdk.VerificationResult
import me.didit.sdk.VerificationStatus
import me.didit.sdk.core.localization.SupportedLanguage

class MainActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Observe SDK state
        lifecycleScope.launch {
            DiditSdk.state.collect { state ->
                when (state) {
                    is DiditSdkState.Ready -> DiditSdk.launchVerificationUI(this@MainActivity)
                    is DiditSdkState.Error -> Log.e("Didit", "Error: ${state.message}")
                    else -> { /* Idle, Loading, CreatingSession */ }
                }
            }
        }
    }

    fun startVerification() {
        val config = Configuration(
            languageLocale = SupportedLanguage.ENGLISH,
            loggingEnabled = true
        )

        // Method 1: UniLink (no backend required)
        DiditSdk.startVerification(
            workflowId = "your-workflow-id",
            vendorData = "user-123",
            configuration = config
        ) { result ->
            handleResult(result)
        }

        // Method 2: Backend Session (recommended for production)
        // DiditSdk.startVerification(
        //     token = "your-session-token",
        //     configuration = config
        // ) { result ->
        //     handleResult(result)
        // }
    }

    private fun handleResult(result: VerificationResult) {
        when (result) {
            is VerificationResult.Completed -> {
                when (result.session.status) {
                    VerificationStatus.APPROVED -> {
                        Log.d("Didit", "Approved! Session: ${result.session.sessionId}")
                    }
                    VerificationStatus.PENDING -> {
                        Log.d("Didit", "Under review")
                    }
                    VerificationStatus.DECLINED -> {
                        Log.d("Didit", "Declined")
                    }
                }
            }
            is VerificationResult.Cancelled -> {
                Log.d("Didit", "Cancelled: ${result.session?.sessionId ?: "unknown"}")
            }
            is VerificationResult.Failed -> {
                Log.e("Didit", "Error: ${result.error.message}")
            }
        }
    }
}
```***


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.