> ## 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.

# Flutter SDK

> Integrate the OTPless Headless SDK in Flutter for the authentication flow — add the dependency, initialize, start with a requestId, and handle callbacks.

This page covers the integration of the OTPless Headless SDK in Flutter. The SDK accepts the `requestId` generated by the [Create API](/docs/server-initiated-auth/create-api), performs the authentication, and reports progress through callbacks. Your backend confirms the final result via the [Status Check API](/docs/server-initiated-auth/status-check-api).

## Requirements

| Requirement | Version |
| - | - |
| Android Minimum SDK | 21 |
| iOS | 13.0+ |

## Step 1: Add SDK dependency

Add the dependency to your `pubspec.yaml`:

```yaml pubspec.yaml theme={null}
dependencies:
  otpless_headless_flutter: ^<latest_version>
```

Then fetch it:

```bash theme={null}
flutter pub get
```

<Note>
  Check the latest version of the [SDK on pub.dev](https://pub.dev/packages/otpless_headless_flutter).
</Note>

<Warning>
  Make sure your authentication channel is enabled on the [OTPLESS dashboard](https://otpless.com/dashboard/customer/channels).
</Warning>

## Step 2: Android setup

If your `MainActivity` extends `FlutterActivity`, change it to extend `FlutterFragmentActivity` for lifecycle awareness:

```kotlin MainActivity.kt theme={null}
import io.flutter.embedding.android.FlutterFragmentActivity

class MainActivity : FlutterFragmentActivity() {
}
```

## Step 3: Initialize the SDK

Create an `Otpless` instance, initialize it with your App ID, and register the response callback:

```dart theme={null}
import 'package:otpless_headless_flutter/otpless_flutter.dart';

final _otplessHeadlessPlugin = Otpless();

@override
void initState() {
    super.initState();
    _otplessHeadlessPlugin.initialize("YOUR_APP_ID");
    _otplessHeadlessPlugin.setResponseCallback(onOtplessResponse);
}
```

<Note>
  Replace `YOUR_APP_ID` with your actual App ID from the [OTPLESS dashboard](https://dashboard.otpless.com/login).
</Note>

## Step 4: Start authentication with the requestId

Pass the `requestId` returned by the [Create API](/docs/server-initiated-auth/create-api) to `start()`:

```dart theme={null}
final Map<String, dynamic> args = {
    "requestId": "REQUEST_ID_FROM_API"
};

_otplessHeadlessPlugin.start(onOtplessResponse, args);
```

<Note>
  Start polling the [Status Check API](/docs/server-initiated-auth/status-check-api) from your backend immediately after calling `start()`. The SDK callback and the server status run in parallel.
</Note>

## Step 5: Handle callbacks

```dart theme={null}
void onOtplessResponse(dynamic response) {
    _otplessHeadlessPlugin.commitResponse(response);

    final responseType = response["responseType"];
    switch (responseType) {
        case "SDK_READY":
            // SDK initialized successfully — enable your continue button
            // or proceed with user authentication.
            break;
        case "FAILED":
            // SDK initialization failed
            if (response["statusCode"] == 5003) {
                // Please try to initialize the SDK again.
            }
            break;
        case "INITIATE":
            // Authentication has been initiated
            if (response["statusCode"] != 200) {
                handleInitiateError(response);
            } else {
                // Authentication is in progress — show a loading state.
            }
            break;
        case "VERIFY":
            // Verification failed for the attempted authentication.
            if (response["statusCode"] == 9106) {
                // Verification timed out — treated as AUTH_TERMINATED.
            }
            break;
        case "ONETAP":
            // Final success response — returns token / idToken
            break;
        case "AUTH_TERMINATED":
            // Auth could not complete. Emitted when pre-checks fail,
            // or when authentication was attempted and then failed.
            // Treat as a terminal failure and rely on the Status Check API
            // for the exact error.
            break;
    }
}
```

## Callback reference

The SDK works in two steps — initialization and `start()` — and each step has its own set of callbacks.

#### Step 1: Initialization callbacks

Emitted when you initialize the SDK (Step 3).

| Callback | Meaning |
| - | - |
| `SDK_READY` | SDK initialization completed. |
| `FAILED` | SDK failed to initialize (e.g. `statusCode 5003`). |

#### Step 2: Start callbacks

Emitted after you invoke `start()` (Step 4).

| Callback | State | Meaning |
| - | - | - |
| `INITIATE` | Non-terminal | Authentication is in progress after pre-checks pass. |
| `VERIFY` | Non-terminal | Verification failed for the attempted authentication (e.g. `statusCode 9106` on timeout). |
| `ONETAP` | **Success — terminal** | Authentication completed successfully. Returns `token` / `idToken`. |
| `AUTH_TERMINATED` | **Failed — terminal** | Emitted in two cases: (1) pre-checks failed → terminated directly; (2) authentication was initiated and then failed. |

<Warning>
  `AUTH_TERMINATED` is a terminal failure. Always treat the [Status Check API](/docs/server-initiated-auth/status-check-api) result as authoritative.
</Warning>

<Note>
  For the `errorCode` / `statusCode` values surfaced in SDK callbacks, see [SDK Error Codes](/docs/server-initiated-auth/sdk-error-codes).
</Note>

## Next step

<Card title="Status Check API" icon="circle-check" href="/docs/server-initiated-auth/status-check-api">
  Confirm the authoritative auth status from your server.
</Card>


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