# Mobile SDKs

> Android, iOS, React Native and Flutter wrappers around the hosted flow — one contract, the same behaviour everywhere.

Source: https://docs.okyc.stream/mobile

The mobile SDKs open the hosted verification flow in a hardened, full-screen WebView and hand your app a typed
result. They all implement the same [contract](https://docs.okyc.stream/reference/sdk-contract.md): the same results, events, failure codes and
options, tested against one shared definition. The tables on these pages are generated from it.

- [Android](https://docs.okyc.stream/mobile/android.md): Kotlin, API 24+. Okyc.start or an Activity Result contract.

- [iOS](https://docs.okyc.stream/mobile/ios.md): Swift, iOS 15+. async/await, a view controller or SwiftUI.

- [React Native](https://docs.okyc.stream/mobile/react-native.md): RN 0.74+ on react-native-webview.

- [Flutter](https://docs.okyc.stream/mobile/flutter.md): Flutter 3.24+ on webview_flutter.

## The integration on every platform

1. Your **backend** creates the session with its secret key (`POST /v1/sessions`) and sends the app only the
   returned `url`. Never put an `sk_…` key in an app.
2. The app starts the SDK with that URL. The SDK loads the flow, asks for the camera when a step needs it, and
   reports progress events.
3. The SDK returns one result: `completed`, `cancelled` or `failed`.
4. Your backend waits for the [webhook](https://docs.okyc.stream/backend/webhooks.md) (or calls `GET /v1/sessions/:id`) for the decision.
   `completed` only means the applicant finished; the status it carries is a hint.

## Results

| Result      | Fields                           | When                                                                 |
| ----------- | -------------------------------- | -------------------------------------------------------------------- |
| `completed` | `session_id`, `status`           | `complete` event received                                            |
| `cancelled` | `session_id?`                    | user closed the flow (✕, back → confirm, swipe down) or `exit` event |
| `failed`    | `code`, `message`, `session_id?` | the flow could not run or continue                                   |

## Events

| Event            | When                                                                                       | Payload                                                                        | Mobile SDKs |
| ---------------- | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ | ----------- |
| `ready`          | The flow loaded and the session is ready.                                                  | `status`: session status                                                       | delivered   |
| `step_started`   | A step was shown for the first time.                                                       | `step`: step key                                                               | delivered   |
| `step_completed` | A step was completed.                                                                      | `step`: step key                                                               | delivered   |
| `complete`       | The applicant finished; the SDK returns `completed` and closes after the auto-close delay. | `status`: approved \| declined \| review \| processing \| expired \| cancelled | delivered   |
| `error`          | A recoverable error happened inside the flow (the flow keeps running).                     | `step`: step key (optional)<br />`code`: error code                            | delivered   |
| `exit`           | The flow asked to close; the SDK returns `cancelled`.                                      | —                                                                              | delivered   |
| `resize`         | Web embeds only; mobile SDKs ignore it.                                                    | `height`: px                                                                   | ignored     |

## Options

| Contract name       | Android            | iOS                | React Native       | Flutter            | Type                          | Default    | Description                                                                                                                                                                |
| ------------------- | ------------------ | ------------------ | ------------------ | ------------------ | ----------------------------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `session_url`       | `sessionUrl`       | `sessionURL`       | `sessionUrl`       | `sessionUrl`       | `string`                      | required   | The `url` returned by `POST /v1/sessions`. Never embed a secret API key in an app.                                                                                         |
| `locale`            | `locale`           | `locale`           | `locale`           | `locale`           | `string`                      | `null`     | Flow language (`en`, `hi`); must be one of the workflow's languages. Sent as `?locale=`.                                                                                   |
| `theme`             | `theme`            | `theme`            | `theme`            | `theme`            | `light` \| `dark` \| `system` | `"system"` | Colour scheme of the flow. Sent as `?theme=`.                                                                                                                              |
| `allowed_hosts`     | `allowedHosts`     | `allowedHosts`     | `allowedHosts`     | `allowedHosts`     | `string[]`                    | `[]`       | Extra hosts the WebView may load (self-hosting).                                                                                                                           |
| `block_screenshots` | `blockScreenshots` | `blockScreenshots` | `blockScreenshots` | `blockScreenshots` | `boolean`                     | `true`     | Android: FLAG\_SECURE. iOS: cover the flow in the app switcher and while the screen is recorded.                                                                           |
| `auto_close_delay`  | `autoCloseDelay`   | `autoCloseDelay`   | `autoCloseDelay`   | `autoCloseDelay`   | `duration_ms`                 | 2000 ms    | How long the result screen stays visible after `complete` before the SDK closes and returns `completed`. `0` or `null` = return the result immediately (no result screen). |

Platform-only options: Android: `title`; iOS: `presentation`; React Native: none; Flutter: `confirmExit`, `title`.

## Failure codes

| Code                  | Meaning                                                                    | Android | iOS | React Native | Flutter |
| --------------------- | -------------------------------------------------------------------------- | ------- | --- | ------------ | ------- |
| `invalid_config`      | Missing or malformed session URL or configuration.                         | ✓       | ✓   | ✓            | ✓       |
| `load_failed`         | The flow page failed to load (HTTP error or blank page).                   | ✓       | ✓   | ✓            | ✓       |
| `network_error`       | No connection or DNS/TLS failure while loading.                            | ✓       | ✓   | ✓            | ✓       |
| `navigation_blocked`  | Repeated navigation to non-allowlisted hosts.                              | ✓       | ✓   | ✓            | ✓       |
| `permission_denied`   | The camera permission was permanently denied and the flow cannot continue. | ✓       | ✓   | ✓            | ✓       |
| `flow_error`          | The flow reported an unrecoverable error.                                  | ✓       | ✓   | ✓            | ✓       |
| `webview_unavailable` | The WebView renderer crashed or is unavailable.                            | ✓       | ✓   | ✓            | —       |
| `presentation_failed` | No view controller was available to present the flow.                      | —       | ✓   | —            | —       |

## Navigation rules

* Loads inside the WebView: `<flow origin>`, `<allowed_hosts>`, and DigiLocker (`digilocker.meripehchaan.gov.in`, `*.digilocker.gov.in`, `api.digitallocker.gov.in`) so its sign-in returns to the flow.
* Other http(s) links: open in the system browser.
* Other schemes (`file:`, `intent:`, `javascript:` …): blocked.
* `locale` is sent as `?locale=`, `theme` is sent as `?theme=`, before the fragment.
* Session token: URL fragment `#t=` — never sent to servers, never logged.

## Security defaults

* JavaScript and DOM storage on; no file:// access; no mixed content; no third-party cookies
* WebView debugging only in debug builds
* Camera/microphone granted to the WebView only for the flow origin, after OS permission
* Clear cookies, cache and storage for the flow origin when the flow ends
* Never log the session URL

## Bridge protocol

You don't need this to integrate; it's how the flow talks to the SDK.

Protocol version **1**. The flow sends JSON strings with the fields `source`, `v`, `type`, `sessionId`, `payload`, where `source` is `okyc` and `v` is `1`. Messages larger than 10,240 bytes are dropped. Payload rule: ids and statuses only — never personal data.

| Platform     | Bridge                   | Name                 | Method        |
| ------------ | ------------------------ | -------------------- | ------------- |
| Android      | `javascript_interface`   | `OkycAndroid`        | `postMessage` |
| iOS          | `script_message_handler` | `okyc`               | —             |
| React Native | `react_native_webview`   | `ReactNativeWebView` | —             |
| Flutter      | `javascript_channel`     | `OkycFlutter`        | —             |
