Skip to content

Start typing to search the documentation.

KMP SDK

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

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:

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

Android setup goes in your androidMain source set (typically your MainActivity).

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:

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

API Reference

SelfSdkConfig

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,
)
ParameterDefaultDescription
endpoint"https://api.self.xyz"Self backend API endpoint
environmentPRODSelfEnvironment.PROD (real documents, mainnet) or SelfEnvironment.STG (mock documents, testnet). See the quickstart environment guide for details.
version2Protocol version for the verification flow
appNamenullDisplay name shown in the verification UI
appEndpointnullURL of the backend verifier that verifies the ZK proof
endpointTypederivedDerived from environment if not set: "https" for PROD, "staging_https" for STG
chainIDnullCelo chain ID — 11142220 (testnet) or 42220 (mainnet)

VerificationRequest

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,
)
ParameterDefaultDescription
userIdnullUser identifier for the verification
scopenullVerification scope (e.g., "identity")
disclosureslistOf("ofac")Requested disclosures (e.g., "full_name", "dob", "nationality", "ofac")
verificationIdnullPre-created verification session ID
excludedCountriesemptyList()Country codes to exclude from verification
userIdTypenullType of user ID provided
userDefinedDatanullCustom data passed through the verification flow
selfDefinedDatanullSelf-defined metadata

SelfSdkCallback

interface SelfSdkCallback {
    fun onSuccess()
    fun onFailure(error: SelfSdkError)
    fun onCancelled()
}
MethodWhen it’s called
onSuccess()Verification completed successfully
onFailure(error)Verification failed
onCancelled()User dismissed without completing

SelfSdkError

data class SelfSdkError(
    val code: String,
    val message: String,
)
CodePlatformMeaning
MISSING_ACTIVITYAndroidbindActivity() not called before launch()
VERIFICATION_IN_PROGRESSBothA verification flow is already running
LAUNCHER_NOT_AVAILABLEAndroidCould not initialize ActivityResultLauncher
VERIFICATION_FAILEDBothThe verification flow failed
NO_VIEW_CONTROLLERiOSCould not find a top UIViewController to present

Provider Interfaces

Providers must be registered before calling launch().

Required Providers

ProviderRegistryPlatformPurpose
SecureStorageProviderSdkProviderRegistryBothKeychain/keystore read/write
WebViewProviderIosProviderRegistryiOS onlyWKWebView creation and JS evaluation

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

SecureStorageProvider

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

WebViewProvider (iOS only)

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):

ClassBacking API
EncryptedSharedPreferencesProviderAndroidX EncryptedSharedPreferences (AES256-SIV / AES256-GCM)

iOS (shipped in SelfSdkSwift):

ClassBacking API
SecureStorageProviderImplKeychain Services
WebViewProviderImplWKWebView with WKScriptMessageHandler bridge

Implementing Your Own Providers

For production, implement the provider interfaces directly:

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)