> ## Documentation Index
> Fetch the complete documentation index at: https://docs.get-rial.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Kotlin SDK

> Native Android capture for rial — v0.10.1.

Native Android capture for rial — the SDK takes the photo, signs it with device signals (hardware attestation, location) and submits it for verification.

## Requirements

|                        |                                                                                       |
| ---------------------- | ------------------------------------------------------------------------------------- |
| **minSdk**             | 24                                                                                    |
| **compileSdk**         | 34                                                                                    |
| **Kotlin**             | 1.9.22 (AGP 8.2.1)                                                                    |
| **Java/Kotlin target** | 17                                                                                    |
| **Compose**            | Only if you use `com.getrial:sdk` or `sdk-ui` — `sdk-core` has no Compose dependency. |

## Install

<CodeGroup>
  ```groovy build.gradle theme={null}
  implementation 'com.getrial:sdk:0.10.1'          // camera + template flow (Compose)
  // or, camera-only without Compose:
  implementation 'com.getrial:sdk-core:0.10.1'
  ```
</CodeGroup>

<Note>
  **Access.** Published to GitHub Packages. The repository is private — you'll need a PAT with `read:packages` scope from a GitHub account that has access to `rial-kotlin`. Ask your rial contact for access.

  ```groovy settings.gradle theme={null}
  dependencyResolutionManagement {
      repositories {
          google()
          mavenCentral()
          maven {
              url 'https://maven.pkg.github.com/Rial-ventures-Inc/rial-kotlin'
              credentials {
                  username = providers.gradleProperty('gpr.user').orNull
                  password = providers.gradleProperty('gpr.token').orNull
              }
          }
      }
  }
  ```

  Set `gpr.user` and `gpr.token` in `~/.gradle/gradle.properties` — never commit them.

  Public distribution via Maven Central is planned — this step disappears then.
</Note>

Permissions (`CAMERA`, `INTERNET`, location) are contributed by the SDK's own manifest via the manifest merger — there's nothing to declare yourself. Each module also ships its own R8 consumer rules, so integrating the SDK doesn't require writing any ProGuard/R8 rules of your own.

## Configure once

Call this from your `Application.onCreate`:

```kotlin theme={null}
Rial.configure(this, publishableKey = "pk_live_…")
// staging for testing: Rial.configure(this, publishableKey = "pk_test_…", environment = RialEnvironment.Staging)
```

## Template flow (turnkey)

The org and template slugs come from the template link published in the dashboard (`/l/{org}/{template}`):

```kotlin theme={null}
Rial.templateFlow(orgSlug = "acme", templateSlug = "inspeccion", activity = this)
```

This renders the complete flow: typed steps, the tenant's branding, camera, review, and a durable submit backed by WorkManager — offline retries, and it resumes after the process is killed. Rotations are survived by reusing the same verification instead of starting over.

For finer control, combine `Rial.openFlow(...)` with `RialTemplateFlow(outcome.session, activity)`.

## Camera only (bring your own UI)

```kotlin theme={null}
val camera = RialCameraView(context).apply {
    onCaptured = { result -> /* result.captureId */ }
    onError = { e -> /* … */ }
    start(activity, publishableKey, baseUrl)
}
// custom shutter: showShutter = false + camera.capture()
// bytes you already have (weaker proof): Rial.capture().capture(jpegBytes)
```

## Offline & durability

The SDK is built to survive a flaky connection and a killed process, not just a happy path.

**Template flow.** Once a photo is captured, submitting it is a durable job: the complete submission intent (token, snapshots, answers) is persisted to disk *before* WorkManager takes ownership of it — so a job that's merely serialized but never scheduled doesn't count as saved. A transient failure (a `5xx`, no network) returns WorkManager's `retry` and keeps the job; the app can be killed and reopened mid-upload and the submission resumes where it left off. Only a terminal outcome — success, or a non-retryable rejection — clears the durable job and its JPEG together.

**Camera-only path.** If the device is offline at capture time, `RialClient.capture(...)` hands the work to the same offline queue instead of failing: see `RialResult` below.

## Error handling

**`RialResult`** — returned by `RialClient.capture(...)`:

```kotlin theme={null}
data class RialResult(
    val captureId: String?,
    val queued: Boolean = false,
    val queueId: String? = null,
)
```

Usually `captureId` is the server-assigned id. If the device was offline at capture time, the upload was handed to the offline queue instead: `queued` is `true`, `captureId` is `null` (the server assigns one once the queued upload completes), and `queueId` is the WorkManager work id — observe it (e.g. `WorkManager.getWorkInfoByIdFlow`) to drive a UI badge from "pending" to "uploaded."

**`FlowOutcome`** — returned by `Rial.openFlow(...)` / `RialFlowClient.open(...)` when opening a template link:

```kotlin theme={null}
sealed interface FlowOutcome {
    data class Ready(val session: RialFlowSession) : FlowOutcome
    data class Unavailable(val reason: Reason) : FlowOutcome

    enum class Reason { NotFound, Expired, AlreadyUsed, Transport }
}
```

`NotFound` covers a link that's missing, still a draft, paused, or out of capacity — the server doesn't distinguish these to the outside on purpose, so the SDK doesn't invent a distinction either. `Expired` is a verification past its `expires_at`. `AlreadyUsed` means it's already `completed` — nothing left to capture. `Transport` is a network failure or a response the SDK couldn't parse.

## Testing against staging

```kotlin theme={null}
Rial.configure(this, publishableKey = "pk_test_…", environment = RialEnvironment.Staging)
```

## Modules

| Module            | Contains                                                               |
| ----------------- | ---------------------------------------------------------------------- |
| `com.getrial:sdk` | Everything, including Compose UI.                                      |
| `sdk-core`        | Camera, device signals, and the offline submission queue — no Compose. |
| `sdk-flow`        | Template flow model and state machine.                                 |
| `sdk-ui`          | The template flow's Compose screen.                                    |

Dependencies run in one direction: `ui → flow → core`.

## Next steps

<CardGroup cols={2}>
  <Card title="Authentication" icon="key" href="/authentication">
    Environments, and how `publishableKey` fits alongside the secret key.
  </Card>

  <Card title="Verdicts" icon="badge-check" href="/verdicts">
    What the verification you just captured against will come back with.
  </Card>
</CardGroup>
