# Mobile SDK contract

> Results, events, failure codes, options and security defaults shared by every mobile SDK.

Source: https://docs.okyc.stream/reference/sdk-contract

Android, iOS, React Native and Flutter implement the same contract, and each SDK is tested against it. These
tables are generated from that shared definition, so they match every SDK.

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

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

## Configuration

Contract names are snake\_case; each SDK exposes them in its language's naming.

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

## Bridges and messages

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`        | —             |

## Navigation

* 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

* 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
