> ## 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.

# 안드로이드 Kotlin 신분증 인증 SDK: KYC

> Didit의 네이티브 Android Kotlin SDK로 신분증 인증, 라이브니스 감지, NFC 전자여권 판독을 구현하세요. KYC 건당 $0.33부터, 월 $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>



      
      

붙여넣을 곳

서버 기반으로 동작하는 경량 Android SDK로, 최소한의 설정만으로 신원 확인을 수행할 수 있습니다.> [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 이상이 필요합니다.** Illinois, Texas 또는 Colorado 주법의 적용을 받는 사용자의 경우, Didit은 첫 얼굴 촬영 전에 명시적인 [생체 인식 동의](https://docs.didit.me/ko/getting-started/biometric-consent.md)를 요청할 수 있습니다. Android SDK 4.10.0 이상에서는 이 단계가 표시됩니다. 이전 버전에서는 이 단계를 표시할 수 없으며, 해당 세션은 이전과 같이 진행되고 결정 결과에는 동의가 수집되지 않은 것으로 보고됩니다(`biometric_consent.reason: "BIOMETRIC_CONSENT_UNSUPPORTED_SDK"`). 해당 주에서 사용자를 확인한다면 업그레이드하세요.***## 설치### 1단계: 저장소 추가`settings.gradle.kts`의 `repositories` 블록에 Didit Maven 저장소를 추가하세요:```kotlin theme={null}
dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        maven { url = uri("https://raw.githubusercontent.com/didit-protocol/sdk-android/main/repository") }
    }
}
```Groovy 방식인 `settings.gradle`을 사용하는 경우에는 다음과 같이 작성하세요:```groovy theme={null}
dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        maven { url "https://raw.githubusercontent.com/didit-protocol/sdk-android/main/repository" }
    }
}
```### 2단계: 의존성 추가앱의 `build.gradle.kts`에 SDK 의존성을 추가하세요:
(!) 버전은 [GitHub 저장소](https://github.com/didit-protocol/sdk-android)에서 최신 버전을 반드시 확인하세요.```kotlin theme={null}
dependencies {
    implementation("me.didit:didit-sdk:4.13.2")
}
```Groovy 방식인 `build.gradle`을 사용하는 경우에는 다음과 같이 작성하세요:```groovy theme={null}
dependencies {
    implementation "me.didit:didit-sdk:4.13.2"
}
```#### SDK 변형Android SDK는 별도의 아티팩트로 제공되므로, 앱은 사용하는 기능에 해당하는 바이너리 크기만 부담합니다. 위의 `me.didit:didit-sdk`는 전체 번들입니다. 앱에 맞는 아티팩트를 선택하세요:| 아티팩트 | 자동 감지(ML) | NFC 칩 판독 | 사용 시기 |
| - | :-: | :-: | - |
| `me.didit:didit-sdk-core` | ✓ | – | 가장 작은 빌드입니다. Didit 자체 감지기로 자동 캡처하며, 여권/신분증 칩 판독은 지원하지 않습니다 |
| `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 런타임의 네이티브 라이브러리이며, 그 크기는 CPU 아키텍처별로 다릅니다. `arm64-v8a`는 약 10MB, `armeabi-v7a`는 약 7MB, x86 에뮬레이터 아키텍처 두 가지는 각각 약 13MB입니다.이 비용을 한 번만 치르려면 다음과 같이 하세요:1) **Android App Bundle(AAB)을 게시하세요.** Google Play는 기기의 아키텍처와 언어에 맞는 것만 전달하므로, arm64 휴대폰에는 네이티브 코드가 약 10MB(압축 다운로드 약 4MB)만 설치됩니다. 반면 범용 APK에는 네 가지 아키텍처가 모두 들어가 40MB를 넘는데, 이것이 예상보다 큰 크기로 측정되는 가장 흔한 원인입니다.

2) **APK 하나만 배포하고** 사용자층이 ARM 휴대폰과 태블릿인 경우, 포함되는 아키텍처를 제한하세요:

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

   실제로 사용자가 쓰는 모든 ABI는 유지하세요. Chromebook이나 기타 x86 기기를 지원한다면 `x86_64`를 추가하거나, 어떤 기기도 지원을 잃지 않도록 ABI별 APK 분할을 게시하세요.

3) **R8을 활성화하세요.** 릴리스 빌드 타입에 `isMinifyEnabled = true`와 `isShrinkResources = true`를 설정하세요(Groovy `build.gradle`을 사용한다면 `minifyEnabled true`와 `shrinkResources true`). SDK에는 자체 컨슈머 규칙이 포함되어 있어 추가 설정 없이 사용하지 않는 SDK 코드가 제거됩니다.

4) **`resConfigs(...)`로 포함되는 언어를 제한하세요.** APK 빌드에서 SDK의 48개 로케일 전체가 필요하지 않다면 이 설정을 사용하세요. Google Play에서는 AAB 언어 분할이 이를 자동으로 처리합니다.### 3단계: 패키징 제외 설정 추가빌드 충돌을 방지하려면 앱의 `android` 블록에 다음을 추가하세요:```kotlin theme={null}
android {
    packaging {
        resources {
            excludes += "META-INF/versions/9/OSGI-INF/MANIFEST.MF"
        }
    }
}
```이제 끝입니다! Gradle이 모든 전이 의존성을 자동으로 해결합니다.***## 권한SDK에는 다음 권한이 필요합니다. 이 권한은 SDK의 `AndroidManifest.xml`에 선언되어 있으며 앱의 매니페스트에 자동으로 병합됩니다.| 권한 | 출처 AAR | 설명 | 필수 |
| - | - | - | - |
| `INTERNET` | `didit-sdk-core` | API 통신을 위한 네트워크 접근 | 예 |
| `ACCESS_NETWORK_STATE` | `didit-sdk-core` | 네트워크 연결 가능 여부 감지 | 예 |
| `CAMERA` | `didit-sdk-core` | 문서 스캔 및 얼굴 인증 | 예 |
| `NFC` | `didit-sdk-nfc` | 여권 및 신분증의 NFC 칩 읽기 | `didit-sdk-nfc`에서 자동 병합됨 (`me.didit:didit-sdk`의 전이 의존성) |### 카메라 및 NFC 기능SDK는 `android.hardware.camera`와 `android.hardware.nfc`를 선택적 기능(`android:required="false"`)으로 선언합니다. 이렇게 하면 카메라나 NFC 하드웨어가 없는 기기에도 앱을 설치할 수 있으며, SDK는 런타임에 하드웨어가 없는 상황을 적절히 처리합니다.***## 빠른 시작### 1단계: SDK 초기화`Application.onCreate()`에서 SDK를 초기화합니다:```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가 Didit Console의 워크플로 ID를 사용해 세션을 직접 생성합니다. `vendorData`만 사용할 수 있습니다.```kotlin theme={null}
DiditSdk.startVerification(
    workflowId = "your-workflow-id",
    vendorData = "user-123"
) { result ->
    handleResult(result)
}
```### 방법 2: 백엔드 세션 (프로덕션 권장)백엔드가 [인증 세션 생성 API](https://docs.didit.me/ko/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`)
- 사용자 지정 메타데이터 설정
- 세션별 콜백 구성
- 교차 검증을 위한 연락처 정보 및 예상 정보 제공***## 구성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` | 완료되면 인증 UI를 자동으로 닫습니다 (Web SDK 대응 옵션: `closeModalOnComplete`) |
| `defaultDocumentCamera` | `CameraLens` | `CameraLens.BACK` | 문서 촬영 화면이 열릴 때 사용하는 렌즈 |
| `defaultLivenessCamera` | `CameraLens` | `CameraLens.FRONT` | 얼굴(라이브니스) 촬영 화면이 열릴 때 사용하는 렌즈 |
| `showDocumentCameraSwitchButton` | `Boolean` | `true` | 문서 촬영 화면에 촬영 중 카메라 전환 버튼을 표시합니다 |
| `showLivenessCameraSwitchButton` | `Boolean` | `true` | 얼굴 촬영 화면에 촬영 중 카메라 전환 버튼을 표시합니다 |### 카메라 옵션카메라 옵션은 [Web SDK 카메라 옵션](https://docs.didit.me/ko/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` 옵션은 Web SDK의 `DiditSdkConfiguration`과 일치합니다. 모바일 전용 옵션인 `languageLocale`과 `fontFamily`는 모바일 SDK가 전체 인증 UI를 네이티브로 렌더링하기 때문에 존재합니다(iframe 안에서 호스팅 프런트엔드에 렌더링을 위임하는 Web SDK와 다릅니다).> [!NOTE]
>
> **테마 및 색상**: 색상, 배경, 인트로 화면 설정은 SDK 구성이 아니라 Didit Console의 [화이트 라벨 설정](https://business.didit.me)에서 구성합니다. 이렇게 하면 모든 플랫폼에서 브랜딩이 일관되게 유지됩니다.***## 언어 지원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/ko/integration/supported-languages.md)*****## 고급 옵션고급 세션 파라미터(`contact_details`, `expected_details`, `metadata`, `callback`)를 사용하려면 **백엔드 세션** 방식을 사용하세요. 백엔드가 [인증 세션 생성 API](https://docs.didit.me/ko/sessions-api/create-session.md)를 전체 파라미터와 함께 호출한 뒤 `session_token`을 SDK에 전달합니다.***## 결과 처리`VerificationResult` sealed 클래스는 인증의 결과를 제공합니다:### 결과 케이스| 케이스 | 설명 |
| - | - |
| `Completed(session)` | 인증 흐름이 완료됨 (결과는 `session.status`에서 확인) |
| `Cancelled(session)` | 사용자가 인증 흐름을 취소함 |
| `Failed(error, session)` | 인증 중 오류가 발생함 |### SessionData 속성| 속성 | 타입 | 설명 |
| - | - | - |
| `sessionId` | `String` | 고유한 세션 식별자 |
| `status` | `VerificationStatus` | `APPROVED`, `PENDING`, 또는 `DECLINED` |### 오류 유형모든 오류는 `me.didit.sdk.VerificationError`의 sealed 하위 클래스입니다:| 오류 | 설명 |
| - | - |
| `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 상태 관찰맞춤형 로딩 UI를 위해 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에는 자체 소비자 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 결과는 UI 피드백에 편리하지만, 공식 결과는 webhook으로 전달됩니다. HMAC 검증은 [webhook 가이드](https://docs.didit.me/ko/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.