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

# Passkey enrollment

> Add a passkey for a user who is already signed in to your native Android or iOS app.

Passkey enrollment adds a passkey to an account that is already signed in to your app. Use it from account security, after login, or after registration.

## How enrollment works

<Steps>
  <Step title="Authorize the current user">
    Your app provides a valid OwnID Access Token for the signed-in user.
  </Step>

  <Step title="Check availability">
    The SDK checks the current configuration, required input, and platform support before your app enables the enrollment action.
  </Step>

  <Step title="Create and enroll the passkey">
    The SDK presents the platform passkey UI and enrolls the new passkey. On success, it returns the enrolled user's login ID so your app can refresh its account or security UI.
  </Step>
</Steps>

<Info>
  Enrollment adds a passkey to the current account. It does not sign the user in or create, refresh, or replace your app session.
</Info>

## Before you start

* Complete the Core SDK setup for [Android](/mobile-sdks/setup/android) or [iOS](/mobile-sdks/setup/ios).
* Complete [Passkey setup](/mobile-sdks/setup/passkeys).
* Obtain a valid OwnID Access Token for the current signed-in user. See the [Android Access Token guide](https://github.com/OwnID/ownid-android-sdk/blob/master/docs/setup/access-token.md) or [iOS Access Token guide](https://github.com/OwnID/ownid-ios-sdk/blob/master/docs/setup/access-token.md).
* Keep the current app session and account UI available if enrollment is unavailable, canceled, or fails.

## Start enrollment

Create the enrollment flow with the current user's Access Token. Check availability before showing or enabling the enrollment action, then keep the returned controller until `whenSettled()` completes.

Availability is a preflight check, not a completion guarantee. Platform UI, device state, tenant configuration, or backend token validation can still cancel or fail after `start()`.

<Tabs>
  <Tab title="Android" icon="android" iconType="brands">
    ```kotlin wrap theme={null}
    viewModelScope.launch {
        val passkeyEnroll = OwnId.headless
            .withContext { authz = Authz.fromToken(accessToken) }
            .passkeys.enroll

        passkeyEnroll.availability().onUnavailable { message ->
            // Hide or disable enrollment. Keep message for diagnostics.
            return@launch
        }

        val controller = passkeyEnroll.start()

        controller.whenSettled()
            .onSuccess { response ->
                // Refresh account UI for response.loginId.
            }
            .onCanceled { reason ->
                // Keep the current session and allow a later retry.
            }
            .onError { error ->
                // Show app-owned retry or fallback UI.
            }
    }
    ```
  </Tab>

  <Tab title="iOS" icon="apple" iconType="brands">
    ```swift wrap theme={null}
    Task { @MainActor in
        let passkeyEnroll = OwnID.headless
            .withContext { context in
                context.authz = .fromToken(accessToken)
            }
            .passkeys.enroll

        let availability = await passkeyEnroll.availability()
            .onUnavailable { message in
                // Hide or disable enrollment. Keep message for diagnostics.
            }

        guard case .available = availability else { return }

        let controller = passkeyEnroll.start()

        await controller.whenSettled()
            .onSuccess { response in
                // Refresh account UI for response.loginID.
            }
            .onCanceled { reason in
                // Keep the current session and allow a later retry.
            }
            .onError { error in
                // Show app-owned retry or fallback UI.
            }
    }
    ```
  </Tab>
</Tabs>

## Handle the enrollment result

| Result   | Meaning                                                                           | What your app should do                                                      |
| -------- | --------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| Success  | Server enrollment completed for the returned login ID.                            | Refresh the account or security UI and show that a passkey is now available. |
| Canceled | The user, app, SDK, or platform UI ended the attempt before enrollment completed. | Keep the current session intact and let the user try again later.            |
| Failure  | Required input, passkey creation, or server enrollment could not complete.        | Keep the current session intact and show app-owned retry or fallback UI.     |

Keep one controller per enrollment attempt. Start another attempt only after the current controller settles, and end the active controller if its owning screen or lifecycle is destroyed.

<Warning>
  Treat OwnID Access Tokens and full enrollment results as sensitive. Never log them.
</Warning>

## Continue with the full guides

<Columns cols={2}>
  <Card title="Android enrollment guide" icon="android" iconType="brands" href="https://github.com/OwnID/ownid-android-sdk/blob/master/docs/flows/passkey-enrollment.md">
    Context options, availability, controller ownership, failures, and security details.
  </Card>

  <Card title="iOS enrollment guide" icon="apple" iconType="brands" href="https://github.com/OwnID/ownid-ios-sdk/blob/master/docs/flows/passkey-enrollment.md">
    Context options, availability, controller ownership, failures, and security details.
  </Card>
</Columns>
