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

# Device-Bound Background Authentication

> Silently re-authenticate returning users on the Android Headless SDK using previously bound identities.

Device-bound background authentication lets a returning user sign in without re-entering their phone number, email, or OTP. When the merchant has one or more identities bound to the device, the SDK can either silently authenticate the single bound identity or show a picker if multiple are available.

Available from `otpless-headless-sdk` **v2.1.0**.

## Flow overview

1. Call `start(authConfig)` to let the SDK drive the entire flow, or fetch the bound identities yourself with `getDeviceBoundIdentities()` and pass a selection to `start(identity, authConfig, callback)`.
2. Handle the `ONETAP` response for the final token. When you use `start(authConfig)`, also handle `AUTO_FLOW_ACTION` for picker lifecycle events.
3. Call `removeDeviceBoundIdentity(identity)` when the user wants to unlink a saved identity.

## Start with the SDK-driven flow

`start(authConfig)` is the simplest way to trigger background auth. It automatically handles the following cases:

* If **one** identity is bound and identity-discovery UI is disabled on the dashboard, it silently starts auth for that identity.
* If **multiple** identities are bound, it shows the built-in OneTap picker.
* If **no** identities are bound, it returns `false` and does nothing.

```kotlin theme={null}
import com.otpless.v2.android.sdk.view.models.OtplessAuthConfig

lifecycleScope.launch(Dispatchers.IO) {
    val started = OtplessSDK.start(
        authConfig = OtplessAuthConfig(isForeground = false)
    )
    if (!started) {
        // No bound identities or merchant config failed to load — fall back.
    }
}
```

`start(authConfig)` returns `true` when a flow was launched, `false` otherwise. The final result is delivered through the callback registered in `initialize` / `setResponseCallback`.

This overload is the **only** entry point that emits `AUTO_FLOW_ACTION` responses — see [Handle responses](#handle-responses) below.

## List device-bound identities

Returns the identities the backend has bound to this device for the merchant. Use it when you want to render your own selection UI instead of the SDK's OneTap picker.

```kotlin theme={null}
import com.otpless.v2.android.sdk.main.OtplessSDK
import com.otpless.v2.android.sdk.view.models.VerifiedContact

lifecycleScope.launch(Dispatchers.IO) {
    val identities: List<VerifiedContact> = OtplessSDK.getDeviceBoundIdentities()
    if (identities.isEmpty()) {
        // No bound identities — fall back to the normal login flow.
    } else {
        // Show your own UI, then call start(identity, authConfig, callback).
    }
}
```

`VerifiedContact` exposes the contact value, provider name, contact type (`PHONE` / `EMAIL`), and a stable `uuid`.

## Start with a merchant-selected identity

Use this overload when you render your own selection UI from `getDeviceBoundIdentities()`.

```kotlin theme={null}
lifecycleScope.launch(Dispatchers.IO) {
    OtplessSDK.start(
        verifiedContact = selectedIdentity,
        authConfig = OtplessAuthConfig(isForeground = false),
        callback = ::onOtplessResponse
    )
}
```

This overload skips the built-in picker and goes straight to authentication. It delivers the final result as a `ONETAP` response on the supplied callback — it does **not** emit `AUTO_FLOW_ACTION`.

## `OtplessAuthConfig`

Both `start` overloads take an `OtplessAuthConfig`.

| Field | Type | Default | Purpose |
| - | - | - | - |
| `isForeground` | `Boolean` | `true` | `false` runs the background flow — response types unrelated to the final `ONETAP` are suppressed. |
| `otp` | `String` | `""` | If set, skips the picker and submits this OTP directly for verification. |
| `tid` | `String?` | `null` | Optional template ID passed to the transaction. |
| `deviceFingerprintMode` | `DeviceFingerprintMode` | `NONE` | Enables device intelligence collection alongside auth. |

## Handle responses

The final identity token always arrives as a `ONETAP` response in your normal `onOtplessResponse` callback.

Picker lifecycle events and background-auth failures arrive as `AUTO_FLOW_ACTION` — **only** when the flow was started via `OtplessSDK.start(authConfig)`. The merchant-selected identity overload (`start(identity, authConfig, callback)`) does not emit `AUTO_FLOW_ACTION`.

```kotlin theme={null}
private fun onOtplessResponse(response: OtplessResponse) {
    OtplessSDK.commit(response)
    when (response.responseType) {
        ResponseTypes.ONETAP -> {
            val token = response.response?.optJSONObject("data")?.optString("token")
            // Process token and proceed.
        }
        ResponseTypes.AUTO_FLOW_ACTION -> {
            if (response.statusCode == 200) {
                val actionType = response.response?.optString("actionType")
                // 21000 = user selected an identity from the picker
                // 21001 = picker dialog shown
            } else {
                val errorCode = response.response?.optString("errorCode")
                // 12001 = user dismissed the picker without selecting
                // 12002 = network failure during background auth, or picker had nothing to show
            }
        }
        else -> Unit
    }
}
```

### `AUTO_FLOW_ACTION` codes

| Code | Meaning |
| - | - |
| `21000` | User selected an identity from the OneTap picker. |
| `21001` | Picker dialog was shown. |
| `12001` | User dismissed the picker without selecting an identity. |
| `12002` | Network failure during background auth, or no usable identities to show. |

## Remove a device-bound identity

Delinks the given identity from this device. Returns `true` on HTTP success, `false` if the SDK is not initialized or the call fails.

```kotlin theme={null}
lifecycleScope.launch(Dispatchers.IO) {
    val removed = OtplessSDK.removeDeviceBoundIdentity(identity)
    if (removed) {
        // Refresh your list — do not call start(identity, ...) with it again.
    }
}
```

## Dismiss the picker

Call `OtplessSDK.closeDialogIfOpen()` to programmatically dismiss the OneTap picker — for example when your activity moves away from the login screen.

## API summary

| Member | Returns | Notes |
| - | - | - |
| `getDeviceBoundIdentities()` | `List<VerifiedContact>` | Empty list if none are bound or the SDK is not initialized. |
| `start(authConfig)` | `Boolean` | `true` if a flow was launched. |
| `start(verifiedContact, authConfig, callback)` | `Unit` | Merchant-selected identity flow. |
| `removeDeviceBoundIdentity(identity)` | `Boolean` | `true` on HTTP success. |
| `closeDialogIfOpen()` | `Unit` | Dismisses the OneTap picker if visible. |


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