> For the complete documentation index, see [llms.txt](https://doc.ozforensics.com/oz-knowledge/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://doc.ozforensics.com/oz-knowledge/general/integration-quick-start-guides/how-to-integrate-oz-liveness-into-your-mobile-application.md).

# How to Integrate Oz Liveness into Your Mobile Application

Oz Liveness Mobile SDK implements the ready-to-use face capture user interface that is essential for seamless customer experience and accurate liveness results.

This guide walks you through your first liveness check using Mobile SDK (iOS or Android) and Oz API.

The described flow combines:

* **Capture mode** – the Mobile SDK captures the Liveness video in the app and the Client backend forwards it to Oz API. Capture results are not sent from the SDK straight to Oz API; the Client backend acts as an intermediary.
* **OzCapsula** – a proprietary encrypted binary container. The SDK packages the captured media into it; only Oz API can decrypt and process it. On the device it is an opaque blob – you cannot decode or inspect it, only forward it as-is.

Each step is marked:

* **\[Oz SDK]** – call our SDK API.
* **\[Your code]** – call your own backend or your own code.

`{{host}}` in the examples below is the base URL of your Oz API deployment, provided to you during deployment setup.

<figure><img src="/files/7ArOaT3OxFXMpR3xBEEd" alt=""><figcaption></figcaption></figure>

## Requirements

| Component   | Minimum version |
| ----------- | --------------- |
| Oz API      | **6.6.1**       |
| iOS SDK     | **10.0.0**      |
| Android SDK | **10.0.0**      |

Use `Content-Type: application/octet-stream` for any container uploaded to Oz API.

Before you begin, make sure you have Oz API credentials. When using SaaS API, you get them [from us](mailto:info@ozforensics.com):

{% code title="" expandable="true" %}

```
Login: j.doe@yourcompany.com
Password: …
API: https://<link>.com
Web Console: https://<link>.com
```

{% endcode %}

For the on-premise Oz API, you need to create a user yourself or ask your team that manages the API. See the [guide on user creation via Web Console](https://doc.ozforensics.com/oz-knowledge/guides/user-guide/oz-webui/webui-users#adding-a-user). Consider the proper user role (`CLIENT` in most cases or `CLIENT ADMIN`, if you are going to make SDK work with the pre-created folders from other API users). In the end, you need to obtain a similar set of credentials as you would get for the SaaS scenario.

We also recommend that you use our logging service called telemetry, as it helps a lot in investigating attacks' details. For Oz API users, the service is enabled by default. For on-premise installations, we'll provide you with credentials.

Oz Liveness Mobile SDK requires a license. License is bound to the `bundle_id` of your application, e.g., `com.yourcompany.yourapp`.

## Instructions

{% stepper %}
{% step %}

### (your code) Add SDK to your project

<details>

<summary>Android</summary>

In the build.gradle of your project, add:

```kotlin
allprojects {
    repositories {
        maven { url "https://ozforensics.jfrog.io/artifactory/main" }
    }
}
```

In the build.gradle of the module, add:

```kotlin
dependencies {
    implementation 'com.ozforensics.liveness:full:<version>'
    // You can find the version needed in the Android changelog
}
```

</details>

<details>

<summary>iOS</summary>

[**CocoaPods**](https://cocoapods.org/)

To integrate OZLivenessSDK into an Xcode project, add to Podfile:

```swift
pod 'OZLivenessSDK', :git => 'https://gitlab.com/oz-forensics/oz-liveness-ios', :tag => '<version>' // You can find the version needed in  iOS changelog

```

**SPM**

Add the following package dependencies via SPM: <https://gitlab.com/oz-forensics/oz-mobile-ios-sdk> (if you need a guide on adding the package dependencies, please refer to the [Apple documentation](https://developer.apple.com/documentation/xcode/adding-package-dependencies-to-your-app)). **OzLivenessSDK** is mandatory. Skip the **OzLivenessSDKOnDevice** file.

</details>
{% endstep %}

{% step %}

### (Oz SDK) Initialize the SDK with a license

Call once per app launch.

<details>

<summary>Android</summary>

```kotlin
OzLivenessSDK.init(
    context,
    listOf(LicenseSource.LicenseAssetId(R.raw.forensics)),
    object : StatusListener<LicensePayload> {
        override fun onSuccess(result: LicensePayload) {
            // SDK ready — proceed to Step 3
        }
        override fun onError(error: OzException) {
            // handle license error
        }
    }
)
```

#### Connect the telemetry receiver

After the license is loaded, point the SDK at the Oz telemetry receiver so it can send telemetry. Pass the event host along with a service token.

```kotlin
val eventsConnection = OzConnection.fromServiceToken(
    host = "<telemetry-receiver-host>",
    token = "<telemetry-service-token>"
)

OzLivenessSDK.setEventsConnection(eventsConnection, object : StatusListener<String?> {
    override fun onSuccess(accessToken: String?) {
        // telemetry receiver ready — proceed to Step 2
    }
    override fun onError(error: OzException) {
        // handle events connection error
    }
})
```

The event host and service token are provided by Oz Forensics. They are distinct from the `session_token` used for capture in Step 2.

</details>

<details>

<summary>iOS</summary>

```swift
OZSDK(licenseSources: [.licenseFilePath(path)]) { licenseData, error in
    if let error = error {
        // handle license error
    } else {
        // SDK ready — proceed to Step 3
    }
}
```

Optional language setup:

```swift
OZSDK.set(languageBundle: Bundle(for: OzSdk.self))
OZSDK.localizationCode = (lang == "en") ? .en : .custom(lang)
```

#### Connect the telemetry receiver

After the license is loaded, point the SDK at the Oz telemetry receiver so it can send telemetry. Pass the event host along with a service token.

```swift
let eventsConnection = Connection.fromServiceToken(
    host: "<telemetry-receiver-host>",
    token: "<telemetry-service-token>",
    sslPins: nil
)

OZSDK.setEventsConnection(eventsConnection) { accessToken, error in
    if let error = error {
        // handle events connection error
    } else {
        // telemetry receiver ready — proceed to Step 2
    }
}
```

The event host and service token are provided by Oz Forensics. They are distinct from the `session_token` used for capture in Step 2.

</details>

{% hint style="warning" %}
During development, the SDK's security checks can cause the analysis to be **declined** – for example, when the app runs with a debugger attached. If you need to run the SDK under these conditions while developing, lower the strictness of the security checks by setting the SDK environment to the `debug` mode:

<details>

<summary>Android</summary>

```kotlin
OzLivenessSDK.setEnvironment(OzLivenessSDK.Environment.DEBUG)
```

</details>

<details>

<summary>iOS</summary>

```swift
OZSDK.setEnvironment(environment: .debug)
```

</details>

The `debug` must be used **only** during development and testing. All release builds must use the default `PRODUCTION` (Android) or `.prod` (iOS) value, where all security checks are enabled.
{% endhint %}
{% endstep %}

{% step %}

### (your code) Get a `session_token` from your backend

OzCapsula uses a session token. The token is mandatory for OzCapsula.

Your backend requests a session token from Oz API. Request it before each capture session, as close to the moment the user starts capture as possible.

**Endpoint:**

```
GET {{host}}/api/authorize/session_token
```

**Request:**

```shell
curl --location -g '{{host}}/api/authorize/session_token' \
--header 'X-Forensic-Access-Token: {{access_token}}' ## optional, depends on installation settings
```

The authorization mechanism for calls to Oz API (`access_token`) depends on your installation settings: when using Instant API, you can integrate it under your own authorization scheme. If access token is required in your case, please check [Authentication](https://doc.ozforensics.com/oz-knowledge/guides/developer-guide/api/oz-api/use-cases/authentication). Also, please note that [access and session token are different tokens issued for different purposes](https://doc.ozforensics.com/oz-knowledge/other/faq#what-is-the-difference-between-access_token-and-session_token).

**Response:**

```json
{
  "session_token": "<session_token>"
}
```

This token is short-lived (default lifetime: 900 seconds). Request one per capture session and pass it to the SDK immediately – do not cache or reuse tokens across sessions.
{% endstep %}

{% step %}

### (Oz SDK) Launch the capture screen with the `session_token`

Pass the `session_token` from **Step 3**.

<details>

<summary>Android</summary>

```kotlin
val captureRequest = CaptureRequest(
    listOf(
        AnalysisProfile(
            Analysis.Type.QUALITY,
            listOf(MediaRequest.ActionMedia(OzAction.Blank))
        )
    )
)
val intent = OzLivenessSDK.createMediaCaptureScreen(captureRequest, sessionToken)
startActivityForResult(intent, REQUEST_LIVENESS_CONTAINER)
```

</details>

<details>

<summary>iOS</summary>

```swift
let mediaRequest = MediaRequest.action(.selfie)

let profile = AnalysisProfile(
    mediaList: [mediaRequest],
    type: .quality,
    params: [:]
)

let request = CaptureRequest(
    analysisProfileList: [profile],
    cameraPosition: .front
)

let livenessVC = try OZSDK.createMediaCaptureScreen(
    delegate,
    request,
    sessionToken: sessionToken          // from Step 3
)

livenessVC.modalPresentationStyle = .fullScreen
parent.present(livenessVC, animated: true)
```

</details>
{% endstep %}

{% step %}

### (Oz SDK) Get the OzCapsula container

{% hint style="info" %}
The container is an **opaque encrypted blob**. You **cannot** decode it, inspect it, or extract individual images / frames / metadata from it on the device. It must be forwarded as-is to your backend. If you need access to captured images (e.g., [the best shot](https://doc.ozforensics.com/oz-knowledge/guides/developer-guide/api/oz-api/basic-scenarios/liveness/best-shot)), retrieve them from the Oz API analysis response on the server side.
{% endhint %}

<details>

<summary>Android</summary>

Extract the `DataContainer` from the result intent.

```kotlin
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (requestCode == REQUEST_LIVENESS_CONTAINER) {
        when (resultCode) {
            OzLivenessResultCode.SUCCESS -> {
                val container = OzLivenessSDK.getContainerFromIntent(data)
                handleContainer(container)
            }
            OzLivenessResultCode.USER_CLOSED_LIVENESS -> { /* user closed the screen */ }
            else -> {
                val errorMessage = OzLivenessSDK.getErrorFromIntent(data)
                /* show error */
            }
        }
    }
}
```

</details>

<details>

<summary>iOS</summary>

Implement the delegate. On success you receive a `DataContainer`.

```swift
extension YourViewController: LivenessDelegate {
    func onResult(container: DataContainer) {
        // Pass container to Step 6
        handleContainer(container)
    }

    func onError(status: OZVerificationStatus?) {
        // user cancelled, capture failed, etc.
    }
}
```

</details>
{% endstep %}

{% step %}

### (your code) Send the container to Oz API

Send the encrypted container to your own backend as raw bytes (`application/octet-stream`). Your backend forwards it to Oz API and returns the analysis response.

{% hint style="warning" %}
Do **not** run the analysis directly from the mobile SDK. In the embedded-SDK integration flow, the analysis call must go through your own backend.
{% endhint %}

<details>

<summary>Android</summary>

{% hint style="info" %}
The code below is illustrative – `YourBackend.uploadOzContainer(...)` is **not** part of Oz SDK. You implement this method yourself in your own backend client; Oz SDK does not provide an upload helper for this integration path.
{% endhint %}

```kotlin
private fun handleContainer(container: DataContainer?) {
    if (container == null) return

    YourBackend.uploadOzContainer(
        container = container.getBytes(),
        contentType = "application/octet-stream"
    )
}
```

{% hint style="warning" %}
Do **not** call `AnalysisRequest.Builder().addContainer(...).build().run(...)`.
{% endhint %}

</details>

<details>

<summary>iOS</summary>

{% hint style="info" %}
The code below is illustrative – `YourBackend.uploadOzContainer` is **not** part of Oz SDK. You implement this method yourself in your own backend client; Oz SDK does not provide an upload helper for this integration path.
{% endhint %}

```swift
func handleContainer(_ container: DataContainer) {
    YourBackend.uploadOzContainer(
        container,
        contentType: "application/octet-stream"
    ) { result in
        DispatchQueue.main.async {
            self.handleAnalysisResult(result)
        }
    }
}
```

{% hint style="warning" %}
Do **not** call `AnalysisRequestBuilder` / `AnalysisRequest.run(...)`.
{% endhint %}

</details>

Your backend submits the container to Oz API in a single synchronous `POST` request.

**Endpoint (Instant API):**

```
POST {{host}}/api/instant/folders/
```

**Endpoint (Full API):**

```
POST {{host}}/api/folders/
```

**Headers:**

* `Content-Type: application/octet-stream` (mandatory).
* `X-Forensic-Access-Token: {{access_token}}`. (optional, depends on installation settings).

**Request body:** the raw container bytes – no JSON envelope, no multipart, no base64 wrapping.

**Request (Instant API):**

```shell
curl -X POST '{{host}}/api/instant/folders/' \
  --header 'X-Forensic-Access-Token: {{access_token}}' ## optional, depends on installation settings
  -H 'Content-Type: application/octet-stream' \
  --data-binary '@/path/to/container.dat'
```

**Request (Full API):**

```shell
curl -X POST '{{host}}/api/folders/' \
  --header 'X-Forensic-Access-Token: {{access_token}}' ## optional, depends on installation settings
  -H 'Content-Type: application/octet-stream' \
  --data-binary '@/path/to/container.dat'
```

**On success:** HTTP `201` with a JSON folder object describing the result. Check **Step 7**.

**On container-level error:** HTTP `400` – see [Exceptions](https://doc.ozforensics.com/oz-knowledge/guides/developer-guide/api/oz-api/ozcapsula-data-container#exceptions).

This guide covers the Liveness analysis. To perform a Face Matching analysis within the OzCapsula flow, see [How to Perform a Face Matching Analysis within OzCapsula Flow](https://doc.ozforensics.com/oz-knowledge/guides/developer-guide/api/oz-api/ozcapsula-data-container/how-to-perform-a-face-matching-analysis-within-ozcapsula-flow).
{% endstep %}

{% step %}

### (your code) Handle the response

Your backend now has the response – a JSON folder object describing the result.

Make the accept/reject decision using only these two top-level fields:

```json
$.resolution_status   // "FINISHED" – analyses completed; "FAILED" – error during analysis processing
$.system_resolution   // "SUCCESS", "DECLINED", or "FAILED"
```

Interpret `system_resolution`:

* `SUCCESS` – all checks passed; accept the user.
* `DECLINED` – one or more checks failed; reject the user.
* `FAILED` – a system error prevented at least one analysis from completing; do not treat as accept or reject.

For per-analysis verdicts (for example, to know whether liveness passed but biometry failed), read `$.analyses[*].resolution_status` with the values described above.
{% endstep %}
{% endstepper %}

## Checklist

* [ ] iOS SDK **10.0.0+** or Android SDK **10.0.0+**, Oz API **6.6.1+**.
* [ ] Step 2 license init runs once per app launch.
* [ ] Step 2 also connects the telemetry receiver with the event host + service token.
* [ ] Step 3 obtains a fresh `session_token` from your backend before every capture.
* [ ] Step 4 passes that `session_token` into `createMediaCaptureScreen(...)`.
* [ ] Step 5 returns a `DataContainer`; no attempt is made to decode/inspect images on-device.
* [ ] Step 6 uploads the container to your own backend as `application/octet-stream`, and your backend submits it to `POST {{host}}/api/instant/folders/` (Instant API) or `POST {{host}}/api/folders/` (Full API).
* [ ] The analysis is never run directly from the mobile SDK.
* [ ] The accept/reject decision uses only `resolution_status` and `system_resolution`.
* [ ] `session_token` is never cached, reused, or shared across sessions/users.

***

With these steps, you are done with basic integration of Mobile SDKs. You will be able to access recorded media and analysis results in Web Console via browser or programmatically via API.

In developer guides, you can also find instructions for customizing the SDK look-and-feel and access the full list of our Mobile SDK methods. Check out the table below:

| Android [sample app](https://gitlab.com/oz-forensics/oz-liveness-android) source codes                                              | iOS [sample app](https://gitlab.com/oz-forensics/public/oz-liveness-ios-sample/-/tree/main) source codes                    |
| ----------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| Android OzLiveness SDK [Developer Guide](https://doc.ozforensics.com/oz-knowledge/guides/developer-guide/sdk/oz-mobile-sdk/android) | iOS OzLiveness SDK [Developer Guide](https://doc.ozforensics.com/oz-knowledge/guides/developer-guide/sdk/oz-mobile-sdk/ios) |
| [Demo app](https://play.google.com/store/apps/details?id=com.ozforensics.liveness.demo\&hl=en) in PlayMarket                        | [Demo app](https://testflight.apple.com/join/mBbPQqnM) in TestFlight                                                        |


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://doc.ozforensics.com/oz-knowledge/general/integration-quick-start-guides/how-to-integrate-oz-liveness-into-your-mobile-application.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
