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

# Kotlin

> Coroutine-native Kotlin SDK reference for our media API, built on Ktor for ingesting assets, polling tasks, and running scoped search.

`sh.sf-voice:sf-voice-media-kotlin` — coroutine-native Kotlin client built on Ktor. All methods are `suspend` functions.

Use this SDK in Kotlin services where cancellation should follow coroutine scope.

## Install

```kotlin theme={null}
// build.gradle.kts
dependencies {
    implementation("sh.sf-voice:sf-voice-media-kotlin:0.1.1")
}
```

## Create a client

```kotlin theme={null}
import com.sfvoice.media.SfVoiceMediaClient

val client = SfVoiceMediaClient(
    apiKey = System.getenv("SF_VOICE_API_KEY"),
    baseUrl = "https://api.sf-voice.com"
)
```

## Ingest

```kotlin theme={null}
import com.sfvoice.media.IngestRequest
import com.sfvoice.media.MediaType

val resp = client.ingest(
    IngestRequest(
        source = "url",
        url = "https://storage.example.com/calls/001.mp3",
        mediaType = MediaType.Audio
    )
)
val taskId = resp.taskId
```

## Poll until ready

```kotlin theme={null}
import com.sfvoice.media.SfVoiceMediaException
import com.sfvoice.media.TaskStatus

val task = client.pollTask(taskId, intervalMs = 2_000, timeoutMs = 120_000)

if (task.status == TaskStatus.Failed) {
    error("indexing failed: ${task.error}")
}
```

`pollTask` throws `SfVoiceMediaException` on API errors or timeout.

## Search

```kotlin theme={null}
import com.sfvoice.media.SearchMatchType
import com.sfvoice.media.SearchRequest

val resp = client.search(
    SearchRequest(
        query = "customer asks about pricing",
        types = listOf(SearchMatchType.Conversation),
        threshold = 0.7f,
        limit = 10
    )
)

resp.results.forEach { r ->
    println("${r.assetId} ${r.startMs}–${r.endMs}ms (${r.score})")
}
```

## Assets

```kotlin theme={null}
// list
val resp = client.listAssets(page = 1, limit = 20)

// get one
val asset = client.getAsset("call_001")

// delete
client.deleteAsset("call_001")
```

## Errors

```kotlin theme={null}
import com.sfvoice.media.SfVoiceMediaException
import com.sfvoice.media.SearchRequest

try {
    client.search(SearchRequest(query = "..."))
} catch (e: SfVoiceMediaException) {
    println("${e.code} ${e.status}: ${e.message}")
}
```
