> ## Documentation Index
> Fetch the complete documentation index at: https://eurusys-6c0957fa.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Desktop JVM (Kotlin & Java)

> Use the MBKYC SDK from a desktop JVM application — Kotlin (coroutines) or pure Java (CompletableFuture).

For desktop JVM apps there are two packages with the same capabilities — pick the one that matches your stack:

* **`mbkyc-kotlin-desktop`** — Kotlin, suspend functions + coroutines.
* **`mbkyc-java-desktop`** — pure Java, no Kotlin runtime; every operation returns a `CompletableFuture`.

<Warning>
  The two packages share a package namespace and each bundle what they need to run — **depend on exactly one**, never both on the same classpath.
</Warning>

<CardGroup cols={2}>
  <Card title="Kotlin example" icon="download" href="/examples/mbkyc-kotlin-example.zip" cta="mbkyc-kotlin-example.zip">
    A self-contained coroutine CLI. Fill in the config block and `./gradlew run`.
  </Card>

  <Card title="Java example" icon="download" href="/examples/mbkyc-java-example.zip" cta="mbkyc-java-example.zip">
    The `CompletableFuture` equivalent. Fill in the config block and `./gradlew run`.
  </Card>
</CardGroup>

Card-read and fingerprint flows require the matching hardware support installed on the machine — see the [package matrix](/distribution/package-matrix#desktop-services--native-host) and [Supported devices](/distribution/supported-devices).

<Info>
  To read a card or capture a fingerprint, install the matching [desktop service](/distribution/services-overview) first — `mbkyc-pcsc-service` for card readers and your fingerprint vendor's service. Without them, the reader lists return empty. Manual entry needs no services.
</Info>

## Kotlin desktop

### Install

```kotlin theme={null}
dependencies {
    implementation("ae.gov.icp.mbkyc:mbkyc-kotlin-desktop:0.7.0")
}
```

The package runs on every supported host out of the box.

### Use

```kotlin theme={null}
val mbkyc = MBKYC.create(
    baseUrl      = "https://api.client.ae",
    apiKeyId     = "key-id",
    tokenSigner  = TokenSignerHandler { signingInput -> myBackend.sign(signingInput) },
    logConfig    = LogConfig(logDir = File("/var/log/myapp")),
).getOrThrow()

mbkyc.use { s ->
    val readers = s.listSmartcardReaders().getOrThrow()
    s.registerDevice("lobby-kiosk").getOrThrow()
    val result = s.validate(EmiratesIdCard(readers.first().id)).getOrThrow()
}
```

The API matches [Android](/sdks/android), without the `context` parameter and without `EmiratesIdNfc`.

## Java desktop

* **Group:** `ae.gov.icp.mbkyc`
* **Artifact:** `mbkyc-java-desktop`
* **Java:** 11+

### Install

```xml theme={null}
<dependency>
  <groupId>ae.gov.icp.mbkyc</groupId>
  <artifactId>mbkyc-java-desktop</artifactId>
  <version>0.7.0</version>
</dependency>
```

### Use

Every call returns `CompletableFuture<MBKYCResult<T>>`. Use `.join()`/`.get()` to block, or `thenApply`/`thenCompose` for non-blocking flows.

```java theme={null}
import ae.gov.icp.mbkyc.*;
import ae.gov.icp.mbkyc.data.*;
import java.io.File;
import java.util.List;

// The API secret never enters the SDK — you sign request inputs on its behalf.
TokenSignerHandler signer = signingInput -> myBackend.sign(signingInput);

MBKYC mbkyc = MBKYC.create(
        "https://api.client.ae",
        "key-id",
        signer,
        new LogConfig(new File("/tmp/mbkyc")))
    .getOrThrow();

try (mbkyc) {
    List<ReaderInfo> readers = mbkyc.listSmartcardReaders().join().getOrThrow();

    VerificationResult result = mbkyc
        .validate(new EmiratesIdCard(readers.get(0).id))
        .join()
        .getOrThrow();

    // Biometric variant:
    List<ReaderInfo> fpReaders = mbkyc.listFingerprintReaders().join().getOrThrow();
    Fingerprint fp = new Fingerprint(fpReaders.get(0).id, recommended ->
        recommended.isEmpty()
            ? FingerSelection.custom(Finger.RightThumb)
            : FingerSelection.recommended(recommended.get(0)));
    VerificationResult biometric = mbkyc
        .validate(new EmiratesIdCard(readers.get(0).id, fp))
        .join()
        .getOrThrow();
}
```

<Info>
  In the Java SDK the token signer and recommendation handlers are ordinary **blocking** methods invoked on background threads under a timeout.
</Info>

`MBKYCResult` is `Ok`/`Err` — use `isOk()`, `getOrNull()`, `getOrThrow()`, `errorOrNull()`, `map`/`flatMap`.

## Full API reference

The two desktop packages expose the same operations over the same [data types](/reference/data-types) — only the async wrapper differs.

| Method                                                                         | Kotlin returns                    | Java returns                                         |
| ------------------------------------------------------------------------------ | --------------------------------- | ---------------------------------------------------- |
| `create(baseUrl, apiKeyId, tokenSigner, logConfig, timeoutMs?, extraHeaders?)` | `MBKYCResult<MBKYC>`              | `MBKYCResult<MBKYC>`                                 |
| `listSmartcardReaders()`                                                       | `MBKYCResult<List<ReaderInfo>>`   | `CompletableFuture<MBKYCResult<List<ReaderInfo>>>`   |
| `listFingerprintReaders()`                                                     | `MBKYCResult<List<ReaderInfo>>`   | `CompletableFuture<MBKYCResult<List<ReaderInfo>>>`   |
| `registerDevice(name)`                                                         | `MBKYCResult<Unit>`               | `CompletableFuture<MBKYCResult<Void>>`               |
| `checkRegistration()`                                                          | `MBKYCResult<RegistrationStatus>` | `CompletableFuture<MBKYCResult<RegistrationStatus>>` |
| `validate(request)`                                                            | `MBKYCResult<VerificationResult>` | `CompletableFuture<MBKYCResult<VerificationResult>>` |
| `exportLogs(output: File)`                                                     | `MBKYCResult<Unit>`               | `CompletableFuture<MBKYCResult<Void>>`               |
| `close()`                                                                      | — (`AutoCloseable`)               | — (`AutoCloseable`)                                  |

### Validation requests by document

Pick the request type for the document you're verifying. The type names are the same in Kotlin and Java — only the construction syntax differs (Kotlin named args vs. Java `new`).

<Tabs>
  <Tab title="Emirates ID">
    **Manual entry** — no hardware required. Construct `EmiratesIdManual`.

    **Card read** — needs a smart card reader + the `mbkyc-pcsc-service` desktop service. Construct `EmiratesIdCard` with a `readerId` from `listSmartcardReaders()`.

    **+ Fingerprint** — attach a `Fingerprint` argument to either Emirates ID request above (needs a fingerprint sensor + its service; get the `readerId` from `listFingerprintReaders()`).
  </Tab>

  <Tab title="Passport">
    **Manual entry** — no hardware required. No biometric variant. Construct `PassportManual`.
  </Tab>
</Tabs>

> Desktop has **no** `context` parameter and **no** `EmiratesIdNfc`. The Kotlin request types match [Android](/sdks/android#full-api-reference); the Java equivalents are `EmiratesIdCard`, `EmiratesIdManual`, `PassportManual`, and `Fingerprint` constructors.

Cancel by cancelling the coroutine (Kotlin) or the `CompletableFuture` (Java).
