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

> ## Agent Instructions
> Company name is Akhara AI (never Rubric AI). Keep lowercase rubric/rubrics only when meaning grading criteria.
> Expert Review (docs path talent/) is enterprise BYO experts for audit and review: invite customer specialists; do not pitch Akhara recruiting or a public expert career portal. RLHF and domain writing are secondary work types.
> Prefer concrete API examples against public hosts: Environments eval API https://agi.akhara.ai, Control plane PDP https://api.akhara.dev, Evaluation https://app.akhara.ai / https://api.akhara.ai, Expert Review portal https://talent.akhara.ai.
> Do not invent a public hostname for private orchestrators or env API internals.
> Do not confuse control-plane latches with Environments confirmation latches.
> Environments SDK/API examples: curl against https://agi.akhara.ai. Evaluation SDK: from akhara import Akhara and AKHARA_API_KEY.
> Start with /llms.txt for the docs index and OpenAPI links; fetch individual pages as .md exports.

# Kotlin / Android SDK

> The shipped AkharaPolicyClient and PolicyEnforcementPoint reference client.

The reference client (`AkharaPolicy.kt`) ships with the demo Android app. It's
built on OkHttp, kotlinx-serialization, and coroutines. See
[requirements](/control-plane/sdk/requirements#android-dependencies) for versions.

## Types

```kotlin theme={null}
enum class PolicyVerdict { ALLOW, REWRITE, BLOCK, ESCALATE, PAUSE }

data class PolicyDecision(
    val verdict: PolicyVerdict,
    val stage: String,
    val policyId: String,
    val rule: String,
    val reason: String,
    val transformedContent: String? = null,
    val permitId: String? = null,
) {
    val mayContinue: Boolean
        get() = verdict == PolicyVerdict.ALLOW || verdict == PolicyVerdict.REWRITE
}
```

## Construct the PEP

```kotlin theme={null}
val pep = PolicyEnforcementPoint(
    AkharaPolicyClient(
        baseUrl = "https://api.akhara.dev",
        agentId = "support-ai",
    )
)
```

Always pass your workspace's `baseUrl` (`https://api.akhara.dev`) explicitly. The
client attaches `Telemetry.sessionId` as the `session` automatically, and the
call timeout is 8 seconds.

<Note>
  The in-repo demo build ships with a loopback default for the Android emulator so
  it can reach a developer's machine. Production builds must set `baseUrl` to the
  managed PDP over HTTPS.
</Note>

## Methods

```kotlin theme={null}
suspend fun checkInput(content: String): PolicyDecision
suspend fun checkContextEgress(content: String): PolicyDecision
suspend fun checkOutput(content: String): PolicyDecision
suspend fun checkDelivery(content: String): PolicyDecision
suspend fun authorizeAction(tool: String, args: JsonObject): PolicyDecision
```

All are `suspend` and run on `Dispatchers.IO`. Call them from a coroutine (e.g.
inside a `ViewModel`'s `viewModelScope`).

## End-to-end example

```kotlin theme={null}
suspend fun handleTurn(userText: String, accountCtx: String): String {
    // 1. input gate
    val input = pep.checkInput(userText)
    if (!input.mayContinue) return refusal(input)

    // 2. context egress: sensitive-data minimization
    val egress = pep.checkContextEgress(accountCtx)
    val modelInput = egress.transformedContent ?: accountCtx

    val draft = assistant.reply(modelInput, userText)

    // 3. output gate
    val output = pep.checkOutput(draft)
    if (!output.mayContinue) return escalateOrDrop(output)

    // 4. delivery gate
    val finalText = output.transformedContent ?: draft
    val delivery = pep.checkDelivery(finalText)
    if (!delivery.mayContinue) return escalateOrDrop(delivery)

    return finalText
}
```

## Permit-gated action

```kotlin theme={null}
suspend fun requestRefund(orderId: String, amount: Double) {
    val args = buildJsonObject {
        put("orderId", orderId)
        put("amount", amount)
    }
    val permit = pep.authorizeAction("refund_payment", args)

    when (permit.verdict) {
        PolicyVerdict.ALLOW -> {
            val receipt = paymentService.submit(orderId, amount, permit.permitId!!)
            // deliver the receipt to the customer
        }
        PolicyVerdict.ESCALATE -> queueForSupervisor(permit)
        else -> refusal(permit)   // BLOCK
    }
}
```

## Fail-closed behavior

Any error, non-`2xx`, or parse failure yields a synthesized `BLOCK`:

```kotlin theme={null}
PolicyDecision(
    verdict = PolicyVerdict.BLOCK,
    stage = stage,
    policyId = "latch-4",
    rule = "Fail-Closed Defaults",
    reason = "Akhara policy authorization is unavailable: …",
)
```

<Warning>
  Two known caveats in the shipped client, called out so you can align a production
  build with the [verdict taxonomy](/control-plane/concepts/verdicts):

  1. **`WARN` handling.** The PDP returns `WARN`, but the enum has no `WARN` member,
     so `PolicyVerdict.valueOf("WARN")` throws and trips fail-closed. Add a `WARN`
     member (or map `WARN → REWRITE`) if your attached policies can return it: for
     example context-egress rewrites from a privacy pack.
  2. **Fail-closed `policyId`.** The client cites `latch-4`, but the canonical
     Fail-Closed Defaults policy is `reliability-4`. Align if you rely on the id.
</Warning>
