Skip to main content
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

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

Create a client

import com.sfvoice.media.SfVoiceMediaClient

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

Ingest

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

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.
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

// list
val resp = client.listAssets(page = 1, limit = 20)

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

// delete
client.deleteAsset("call_001")

Errors

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}")
}