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

# Quickstart

> How to quickly get up and running with the Stytch Android SDK

# Android Quickstart

<Note>This guide is for our new Kotlin Multiplatform SDK which is in **Public beta**. If you are instead looking for the stable version, please see [stytch-android](https://github.com/stytchauth/stytch-android) and the usage docs [here](https://stytchauth.github.io/stytch-android/).</Note>

This guide walks you through adding Stytch authentication to an Android app using `consumer-headless` or `b2b-headless`. Both SDKs are distributed via Maven Central and require **Kotlin 2.3.0 or later**.

***

## 1. Install the SDK

Make sure `mavenCentral()` is in your repository configuration:

```kotlin theme={null}
// settings.gradle.kts
dependencyResolutionManagement {
    repositories {
        mavenCentral()
    }
}
```

Add the dependency that matches your project type:

```kotlin theme={null}
// Consumer (B2C apps)
dependencies {
    implementation("com.stytch.sdk:consumer-headless:1.0.0")
}

// B2B (organizations/members)
dependencies {
    implementation("com.stytch.sdk:b2b-headless:1.0.0")
}
```

If you prefer callback-style APIs over coroutines, see [Callback Extensions](#callback-extensions) at the end of this guide.

***

## 2. Initialize the Client

Create the client once — in your `Application` class or at the entry point of your auth flow. The client is a singleton; calling `createStytchConsumer` again returns the same instance.

```kotlin theme={null}
import com.stytch.sdk.consumer.createStytchConsumer
import com.stytch.sdk.data.StytchClientConfiguration

class App : Application() {
    val stytch by lazy {
        createStytchConsumer(
            StytchClientConfiguration(
                context = applicationContext,
                publicToken = "public-token-live-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
            )
        )
    }
}
```

For B2B apps, replace `createStytchConsumer` with `createStytchB2B` and import from `com.stytch.sdk.b2b`.

Your public token is in the [Stytch Dashboard](https://stytch.com/dashboard/api-keys). Make sure you've also enabled the auth methods you want to use under **SDK Configuration**.

***

## 3. Observe Authentication State

The SDK exposes a `StateFlow` that emits whenever the authentication state changes. Collect it from a `ViewModel` or `lifecycleScope`:

```kotlin theme={null}
import com.stytch.sdk.consumer.data.ConsumerAuthenticationState

lifecycleScope.launch {
    app.stytch.authenticationStateFlow.collect { state ->
        when (state) {
            is ConsumerAuthenticationState.Authenticated -> {
                val user = state.user
                val session = state.session
                val sessionToken = state.sessionToken
                // Navigate to your authenticated UI
            }
            is ConsumerAuthenticationState.Unauthenticated -> {
                // Show your login UI
            }
            is ConsumerAuthenticationState.Loading -> {
                // SDK is restoring a persisted session — show a splash screen
            }
        }
    }
}
```

To read the current state synchronously (e.g., during a navigation check), use `stytch.authenticationStateFlow.value`.

***

## 4. Auth Methods

All SDK methods are `suspend` functions. Call them from a coroutine scope and handle errors with `try/catch`. Errors are thrown as `StytchError`.

### SMS OTP Example

```kotlin theme={null}
import com.stytch.sdk.consumer.networking.models.OTPsSMSLoginOrCreateParameters
import com.stytch.sdk.consumer.networking.models.OTPsAuthenticateParameters

// Step 1: Send OTP
var methodId: String? = null

viewModelScope.launch {
    try {
        val response = stytch.otp.sms.loginOrCreate(
            OTPsSMSLoginOrCreateParameters(phoneNumber = "+15551234567")
        )
        methodId = response.methodId
    } catch (e: StytchError) {
        // Handle error
    }
}

// Step 2: Verify the code
viewModelScope.launch {
    try {
        stytch.otp.authenticate(
            OTPsAuthenticateParameters(
                token = userEnteredCode,
                methodId = methodId!!,
                sessionDurationMinutes = 30,
            )
        )
    } catch (e: StytchError) {
        // Handle error
    }
}
```

***

## 5. Handle Deeplinks

Magic links and password reset emails redirect back to your app via a custom URL scheme.

**1. Register a redirect URL** in the [Stytch Dashboard](https://stytch.com/dashboard/redirect-urls). Use a scheme like `myapp://auth`.

**2. Add an intent filter** to your activity in `AndroidManifest.xml`:

```xml theme={null}
<activity ...>
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="myapp" android:host="auth" />
    </intent-filter>
</activity>
```

**3. Handle the intent** in your activity:

```kotlin theme={null}
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    if (intent.action == Intent.ACTION_VIEW) {
        intent.data?.toString()?.let { url ->
            viewModel.handleDeeplink(url)
        }
    }
}
```

**4. Authenticate the deeplink** in your ViewModel:

```kotlin theme={null}
import com.stytch.sdk.consumer.data.DeeplinkAuthenticationStatus

fun handleDeeplink(url: String) {
    viewModelScope.launch {
        try {
            when (val status = stytch.authenticate(url, sessionDurationMinutes = 30)) {
                is DeeplinkAuthenticationStatus.Authenticated -> {
                    // User is now logged in
                }
                is DeeplinkAuthenticationStatus.ManualHandlingRequired -> {
                    // Password reset token — prompt the user for a new password,
                    // then call stytch.passwords.resetByEmail(token, newPassword)
                    val resetToken = status.token
                }
                is DeeplinkAuthenticationStatus.UnknownDeeplink -> {
                    // Not a Stytch deeplink
                }
            }
        } catch (e: StytchError) {
            // Handle error
        }
    }
}
```

***

## 6. Session Management

Sessions are automatically persisted across app launches and validated on startup.

```kotlin theme={null}
// Manually authenticate (validate) the current session
stytch.session.authenticate(
    SessionsAuthenticateParameters(sessionDurationMinutes = 30)
)

// Hydrate a client with a session token received out-of-band
stytch.hydrate("existing-session-token")

// Sign out
stytch.session.revoke()
```

***

## Callback Extensions

If your project uses callback-style APIs or calls the SDK from Java, swap the base artifact for the extensions variant — it re-exports the base SDK, so no other changes are needed:

```kotlin theme={null}
// Before
implementation("com.stytch.sdk:consumer-headless:1.0.0")

// After
implementation("com.stytch.sdk:consumer-headless-extensions:1.0.0")

// Same for B2B
implementation("com.stytch.sdk:b2b-headless-extensions:1.0.0")
```

Every `suspend` method then gains an `onSuccess`/`onFailure` overload that returns a cancellable `Job`:

```kotlin theme={null}
val job = stytch.otp.authenticate(
    request = OTPsAuthenticateParameters(token = code, methodId = methodId, sessionDurationMinutes = 30),
    onSuccess = { response -> /* handle success */ },
    onFailure = { error -> /* handle error */ },
)
```
