---
Source: https://docs.microblink.com/blinkid/how-the-sdk-works
Title: How the SDK works
Description: Understand the two ways to integrate BlinkID—the UX package and the core session—and how they relate
---

# How the SDK works

BlinkID ships as two layers that you can integrate at different levels of abstraction:

- the **UX package**, a ready-made scanning experience with a camera UI, user guidance, and timeouts
- the **core session**, a headless scanning engine that processes images and returns extracted data

The UX package is built on top of the core session.
Whichever layer you integrate, the same engine does the extraction; the difference is how much of the camera handling and user guidance the SDK does for you.

This article explains what each layer is, how they relate, and when to pick one over the other.
For step-by-step integration, see the quickstart for your platform.

## The core session

The core session is the scanning engine with no UI attached.
You feed it images one frame at a time, and it tells you what it extracted and what is still missing.

The lifecycle is the same on every platform:

1. **Initialize the SDK** with your license key.
2. **Create a scanning session**, passing [session settings](./session-settings.md) (input image source, scanning mode, and [scanning settings](./scanning-settings.md)).
3. **Process images** by calling `process` for each frame, whether the frames come from a camera, a file, or an upload.
4. **Inspect the result** after each frame, then **retrieve the final result** once scanning is complete.

<Tabs queryString="platform">

  <TabItem value="web" label="Web (TS)" default>

  ```typescript
  import { loadBlinkIdCore } from "@microblink/blinkid-core";

  const blinkIdCore = await loadBlinkIdCore({ licenseKey: "your-license-key" });

  const session = await blinkIdCore.createBlinkIdScanningSession({
    inputImageSource: "photo",
  });

  const processResult = await session.process(image);
  // inspect processResult, feed more frames as needed

  const result = await session.getResult();
  ```

  </TabItem>
  <TabItem value="android" label="Android (Kotlin)">

  ```kotlin
  val sdk = BlinkIdSdk.initializeSdk(
      context,
      BlinkIdSdkSettings(licenseKey = "your-license-key")
  ).getOrThrow()

  val session = sdk.createScanningSession(
      BlinkIdSessionSettings(inputImageSource = InputImageSource.Photo)
  ).getOrThrow()

  val processResult = session.process(image).getOrThrow()
  // inspect processResult, feed more frames as needed

  val result = session.getResult().getOrNull()
  ```

  </TabItem>
  <TabItem value="ios" label="iOS (Swift)">

  ```swift
  let sdk = try await BlinkIDSdk.createBlinkIDSdk(
      withSettings: BlinkIDSdkSettings(licenseKey: "your-license-key")
  )

  let session = try await sdk.createScanningSession(
      sessionSettings: BlinkIDSessionSettings(inputImageSource: .photo)
  )

  let frameResult = try await session.process(inputImage: image)
  // inspect frameResult, feed more frames as needed

  let result = session.getResult()
  ```

  </TabItem>
</Tabs>

The core session gives you full control, but it leaves everything around the engine to you: opening the camera, drawing an overlay, telling the user to move closer or flip the document, and deciding when to stop.

### The processing model

Every call to `process` returns the analysis for that single frame.
Two parts of that per-frame result drive the flow:

- the **processing status**, which says what happened with the frame (for example, `success`, `awaiting-other-side`, or a failure reason)
- the **result completeness**, which reports, per module, whether the data that module extracts has been captured and, if not, why

## The UX package

The UX package wraps the core session in a complete scanning screen.
It opens the camera, runs the per-frame `process` loop for you, shows a reticle and real-time instructions, handles the front-to-back transition for double-sided documents, applies [timeouts](./timeouts.md), and calls you back with the final result.

<Tabs queryString="platform">

  <TabItem value="web" label="Web (TS)" default>

  On web, `createBlinkId` sets up the engine, camera, and UI in one call.
  Register a result callback to receive the extracted data.

  ```typescript
  import { createBlinkId } from "@microblink/blinkid";

  const blinkId = await createBlinkId({ licenseKey: "your-license-key" });

  blinkId.addOnResultCallback((result) => {
    console.log("First name:", result.firstName?.latin?.value);
    void blinkId.destroy();
  });
  ```

  </TabItem>
  <TabItem value="android" label="Android (Kotlin)">

  On Android, `BlinkIdCameraScanningScreen` is a composable that owns the camera and UI.
  It calls `onScanningSuccess` with the final result.

  ```kotlin
  BlinkIdCameraScanningScreen(
      blinkIdSdk = blinkIdSdk,
      onScanningSuccess = { result ->
          println("First name: ${result.firstName?.value}")
      },
      onScanningCanceled = { }
  )
  ```

  </TabItem>
  <TabItem value="ios" label="iOS (Swift)">

  On iOS, `BlinkIDUXView` is a SwiftUI view driven by a `BlinkIDAnalyzer`.
  It calls `onScanCompleted` with the final result.

  ```swift
  let analyzer = try await BlinkIDAnalyzer(sdk: sdk)

  BlinkIDUXView(
      analyzer: analyzer,
      onScanCompleted: { resultState in
          guard let result = resultState.scanningResult else { return }
          print("First name:", result.firstName?.value ?? "")
      }
  )
  ```

  </TabItem>
</Tabs>

Even with the UX package doing the heavy lifting, you still reach the same per-frame data when you need it.
Web, iOS and Android expose a frame-process callback that hands you the result completeness, lets you advance to the next step, trigger a timeout, or retrieve the raw frame.
This means you can keep the prebuilt UI and still apply custom stop-scanning logic.

## Which one to use

Use the **UX package** when you want a production-ready scanning experience with the least effort.
It is the recommended starting point for almost every camera-based integration: the UI, guidance, and timeouts are the result of extensive user testing, and you can still customize behavior through its callbacks and settings.

Use the **core session** when the UX package's screen does not fit your use case, for example:

- you are scanning from **static images or uploads** rather than a live camera, where there is no UI to show (see [scan from a static image](./scan-image.md))
- you are building a **fully custom camera UI** and want to drive the engine yourself
- you are running scanning in a **non-interactive or server-side context**

A useful rule of thumb: start with the UX package, and drop down to the core session only for the specific part of the flow that needs it.

## Packages per platform

Each platform splits the two layers into separate packages or modules, so you can depend only on what you integrate.



<!-- interactive component omitted -->


  

<!-- interactive component omitted -->


  

<!-- interactive component omitted -->


</Tabs>

## Related articles

- [Migrate to v8000](./migration-v8000.md)
- [Scan in real time via camera](./scan-camera.md)
- [Scan from a static image](./scan-image.md)
- [Session settings](./session-settings.md)
- [Scanning settings](./scanning-settings.md)


Last updated on Jul 24, 2026
