# KMP SDK (Alpha)

{% hint style="warning" %}
The Self KMP SDK is currently in **alpha**. To get access or try it out, please [contact the Self team](https://self.xyz/#contact).
{% endhint %}

## Overview

The Self KMP SDK lets you add Self identity verification to a Kotlin Multiplatform app while keeping the verification API in shared Kotlin code.

It's a good fit if your app already uses Kotlin Multiplatform and you want to keep verification logic in shared code.

The SDK provides:

* shared configuration with `SelfSdkConfig`
* shared request construction with `VerificationRequest`
* a common launch API and callback interface

You'll need to provide:

* secure storage setup on both platforms
* Android activity binding before launch
* iOS WebView provider and secure storage via `SelfSdkSwift` or your own implementations

The SDK currently supports Android and iOS. On iOS, you also need the `SelfSdkSwift` companion package or equivalent native provider implementations.

## How It Works

```mermaid
sequenceDiagram
    participant Host as Host App
    participant SDK as Self SDK
    participant UI as Self Verification UI
    participant Self as Self Protocol

    Note over Host,SDK: App setup
    Host->>SDK: Configure `SelfSdkConfig`
    Host->>SDK: Call `launch(request, callback)`

    Note over SDK,UI: Verification session
    SDK->>UI: Launch hosted verification flow
    activate UI
    UI->>UI: Complete KYC flow
    UI->>UI: Delete KYC documents

    Note over UI,Self: Proof lifecycle
    UI->>Self: Register identity proof
    UI->>Self: Disclose & verify proof
    Self-->>UI: Return verification outcome

    Note over UI,Host: Completion
    alt Verification succeeds
        UI-->>SDK: Success
        SDK-->>Host: `onSuccess()`
    else Verification fails
        UI-->>SDK: Failure
        SDK-->>Host: `onFailure(error)`
    else User cancels
        UI-->>SDK: Cancelled
        SDK-->>Host: `onCancelled()`
    end
    deactivate UI
```

## Quick Start

All calling code lives in your `commonMain` source set:

```kotlin
val sdk = SelfSdk.configure(
    SelfSdkConfig(
        environment = SelfEnvironment.PROD,
    )
)

val request = VerificationRequest(
    userId = "user-uuid",
    scope = "identity",
    disclosures = listOf("ofac"),
)

sdk.launch(
    request = request,
    callback = object : SelfSdkCallback {
        override fun onSuccess() {
            println("Verification completed successfully")
        }

        override fun onFailure(error: SelfSdkError) {
            println("Error [${error.code}]: ${error.message}")
        }

        override fun onCancelled() {
            println("User dismissed verification")
        }
    }
)
```

## Platform Setup

{% tabs %}
{% tab title="Android" %}
Android setup goes in your `androidMain` source set (typically your `MainActivity`).

```kotlin
import android.os.Bundle
import androidx.activity.ComponentActivity
import xyz.self.sdk.api.SelfSdk
import xyz.self.sdk.providers.EncryptedSharedPreferencesProvider
import xyz.self.sdk.providers.SdkProviderRegistry

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

        // 1. Register providers (demo provider — implement your own for production)
        if (SdkProviderRegistry.secureStorage == null) {
            SdkProviderRegistry.secureStorage = EncryptedSharedPreferencesProvider(this)
        }

        // 2. Bind the activity (required for launching the verification screen)
        SelfSdk.bindActivity(this)

        // Now SelfSdk.configure() and sdk.launch() work from commonMain
        setContent { App() }
    }
}
```

`bindActivity()` registers an `ActivityResultLauncher` on the given `ComponentActivity`. Without it, `launch()` fails with `MISSING_ACTIVITY`.

**Convenience overload** — combines configure + bind + launch in one call:

```kotlin
SelfSdk.launch(
    activity = this,
    config = SelfSdkConfig(environment = SelfEnvironment.PROD),
    request = VerificationRequest(userId = "user-uuid"),
    callback = myCallback,
)
```

{% endtab %}

{% tab title="iOS" %}
iOS requires a Swift companion package (`SelfSdkSwift`) that provides native provider implementations using Keychain Services, CryptoKit, and WKWebView.

```swift
import SwiftUI
import ComposeApp      // Your KMP framework (exports KMP types)
import SelfSdkSwift    // Swift companion package

// Declare that Swift classes conform to KMP protocols.
// Required because Swift needs to see both the protocol (from KMP)
// and the class (from SelfSdkSwift) to establish conformance.
extension SecureStorageProviderImpl: SecureStorageProvider {}
extension WebViewProviderImpl: WebViewProvider {}

@main
struct iOSApp: App {
    init() {
        // Register providers (demo providers — implement your own for production)
        SdkProviderRegistry.shared.secureStorage = SecureStorageProviderImpl()
        IosProviderRegistry.shared.webView = WebViewProviderImpl()
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}
```

KMP compiles Kotlin `object` singletons to Objective-C classes — Swift accesses them via `.shared`.

Once providers are registered, call `SelfSdk.configure()` and `sdk.launch()` from your shared `commonMain` code — no iOS-specific launch code is needed. The Quick Start example above works as-is on both platforms.

`SelfSdkSwift` is a convenience, not a requirement. You can provide your own classes as long as they conform to the KMP protocols (`SecureStorageProvider`, `WebViewProvider`).
{% endtab %}
{% endtabs %}

## API Reference

### SelfSdkConfig

```kotlin
SelfSdkConfig(
    endpoint: String = "https://api.self.xyz/verifier",
    environment: SelfEnvironment = SelfEnvironment.PROD,
    version: Int = 2,
    appName: String? = null,
    appEndpoint: String? = null,
    endpointType: String? = null,  // derived from environment if not set
    chainID: Int? = null,
)
```

| Parameter      | Default                  | Description                                                                                                                                                                                                |
| -------------- | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `endpoint`     | `"https://api.self.xyz"` | Self backend API endpoint                                                                                                                                                                                  |
| `environment`  | `PROD`                   | `SelfEnvironment.PROD` (real documents, mainnet) or `SelfEnvironment.STG` (mock documents, testnet). See the [quickstart environment guide](/self-pass/quickstart.md#choose-your-environment) for details. |
| `version`      | `2`                      | Protocol version for the verification flow                                                                                                                                                                 |
| `appName`      | `null`                   | Display name shown in the verification UI                                                                                                                                                                  |
| `appEndpoint`  | `null`                   | URL of the backend verifier that verifies the ZK proof                                                                                                                                                     |
| `endpointType` | derived                  | Derived from `environment` if not set: `"https"` for PROD, `"staging_https"` for STG                                                                                                                       |
| `chainID`      | `null`                   | Celo chain ID — `11142220` (testnet) or `42220` (mainnet)                                                                                                                                                  |

### VerificationRequest

```kotlin
VerificationRequest(
    userId: String? = null,
    scope: String? = null,
    disclosures: List<String> = listOf("ofac"),
    verificationId: String? = null,
    excludedCountries: List<String> = emptyList(),
    userIdType: String? = null,
    userDefinedData: String? = null,
    selfDefinedData: String? = null,
)
```

| Parameter           | Default          | Description                                                                     |
| ------------------- | ---------------- | ------------------------------------------------------------------------------- |
| `userId`            | `null`           | User identifier for the verification                                            |
| `scope`             | `null`           | Verification scope (e.g., `"identity"`)                                         |
| `disclosures`       | `listOf("ofac")` | Requested disclosures (e.g., `"full_name"`, `"dob"`, `"nationality"`, `"ofac"`) |
| `verificationId`    | `null`           | Pre-created verification session ID                                             |
| `excludedCountries` | `emptyList()`    | Country codes to exclude from verification                                      |
| `userIdType`        | `null`           | Type of user ID provided                                                        |
| `userDefinedData`   | `null`           | Custom data passed through the verification flow                                |
| `selfDefinedData`   | `null`           | Self-defined metadata                                                           |

### SelfSdkCallback

```kotlin
interface SelfSdkCallback {
    fun onSuccess()
    fun onFailure(error: SelfSdkError)
    fun onCancelled()
}
```

| Method             | When it's called                    |
| ------------------ | ----------------------------------- |
| `onSuccess()`      | Verification completed successfully |
| `onFailure(error)` | Verification failed                 |
| `onCancelled()`    | User dismissed without completing   |

### SelfSdkError

```kotlin
data class SelfSdkError(
    val code: String,
    val message: String,
)
```

| Code                       | Platform | Meaning                                            |
| -------------------------- | -------- | -------------------------------------------------- |
| `MISSING_ACTIVITY`         | Android  | `bindActivity()` not called before `launch()`      |
| `VERIFICATION_IN_PROGRESS` | Both     | A verification flow is already running             |
| `LAUNCHER_NOT_AVAILABLE`   | Android  | Could not initialize `ActivityResultLauncher`      |
| `VERIFICATION_FAILED`      | Both     | The verification flow failed                       |
| `NO_VIEW_CONTROLLER`       | iOS      | Could not find a top `UIViewController` to present |

## Provider Interfaces

Providers must be registered before calling `launch()`.

### Required Providers

| Provider                | Registry              | Platform | Purpose                              |
| ----------------------- | --------------------- | -------- | ------------------------------------ |
| `SecureStorageProvider` | `SdkProviderRegistry` | Both     | Keychain/keystore read/write         |
| `WebViewProvider`       | `IosProviderRegistry` | iOS only | WKWebView creation and JS evaluation |

Android does not need a `WebViewProvider` — the SDK manages the WebView internally.

### SecureStorageProvider

```kotlin
interface SecureStorageProvider {
    fun get(key: String): String?
    fun set(key: String, value: String)
    fun remove(key: String)
    fun clear()
}
```

### WebViewProvider (iOS only)

```kotlin
interface WebViewProvider {
    fun createWebView(
        onMessageReceived: (String) -> Unit,
        isDebugMode: Boolean,
        queryParams: String? = null,
    ): UIView

    fun evaluateJs(js: String)
    fun getViewController(): UIViewController
    fun isBridgeRequestAllowed(): Boolean
    fun configureRemoteLoading(remoteWebAppBaseURL: String?)
    fun configureDevServer(devServerUrl: String?)
}
```

### Demo Implementations

The SDK ships demo implementations for quick prototyping. **These are not intended for production use** — you should implement your own `SecureStorageProvider` backed by your app's secure storage strategy.

**Android** (shipped in the SDK):

| Class                                | Backing API                                                   |
| ------------------------------------ | ------------------------------------------------------------- |
| `EncryptedSharedPreferencesProvider` | AndroidX EncryptedSharedPreferences (AES256-SIV / AES256-GCM) |

**iOS** (shipped in `SelfSdkSwift`):

| Class                       | Backing API                                    |
| --------------------------- | ---------------------------------------------- |
| `SecureStorageProviderImpl` | Keychain Services                              |
| `WebViewProviderImpl`       | WKWebView with `WKScriptMessageHandler` bridge |

### Implementing Your Own Providers

For production, implement the provider interfaces directly:

{% tabs %}
{% tab title="Android" %}

```kotlin
class MySecureStorage(context: Context) : SecureStorageProvider {
    override fun get(key: String): String? { /* ... */ }
    override fun set(key: String, value: String) { /* ... */ }
    override fun remove(key: String) { /* ... */ }
    override fun clear() { /* ... */ }
}

SdkProviderRegistry.secureStorage = MySecureStorage(context)
```

{% endtab %}

{% tab title="iOS" %}

```swift
class MySecureStorage: NSObject, SecureStorageProvider {
    func get(key: String) -> String? { /* ... */ }
    func set(key: String, value: String) { /* ... */ }
    func remove(key: String) { /* ... */ }
    func clear() { /* ... */ }
}

SdkProviderRegistry.shared.secureStorage = MySecureStorage()
```

{% endtab %}
{% endtabs %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.self.xyz/self-pass/kmp-sdk.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
