Kotlin SDK for the put.io API
Domain-first, coroutine-friendly, and parsed at the boundary.
dependencies {
implementation("io.put:putio-sdk-kotlin:<version>")
}The latest version is on Maven Central and in GitHub releases. To build against a checkout instead, use a Gradle composite build with includeBuild("../putio-sdk-kotlin").
import io.putdotio.sdk.PutioClient
import io.putdotio.sdk.PutioConfig
suspend fun loadAccount() {
PutioClient(
PutioConfig(
accessToken = System.getenv("PUTIO_TOKEN"),
clientId = "android-app",
clientName = "put.io Android"
)
).use { sdk ->
val account = sdk.account.getInfo()
val rootFiles = sdk.files.list(parentId = 0)
println(account.username)
println(rootFiles.files.size)
}
}Stable since 1.0.0 and versioned with semver: breaking changes ship only in a major release. The public surface is deliberately smaller than putio-sdk-typescript; Architecture lists the namespaces and operations.
The surface grows with the put.io mobile and TV apps. The primary consumer is putio-android, which uses this SDK as its API boundary and pins the Maven Central release.
Only main and the latest io.put:putio-sdk-kotlin release receive fixes.
The SDK emits Java 8 bytecode and leaves the Android minimum SDK to the consumer. The first-party Android app's CI builds unsigned, R8-minified releases at minSdk 26 against the Maven Central release. The current SDK, OkHttp, coroutines, and serialization stack needs no SDK-specific consumer keep rules.
The SDK depends on kotlinx-coroutines-core and does not select
Dispatchers.Main. Android apps that run their own coroutines on the main
dispatcher must add the Android dispatcher:
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.11.0")
}Pass an OkHttpClient to add application interceptors, caching, or other
consumer policy:
val httpClient = OkHttpClient.Builder()
.addInterceptor(appInterceptor)
.cache(Cache(cacheDirectory, 50L * 1024 * 1024))
.build()
val sdk = PutioClient(
config = PutioConfig(accessToken = accessToken),
okHttpClient = httpClient,
)An injected client remains caller-owned: PutioClient.close() does not close
its cache, dispatcher, or connection pool. A client created internally by
PutioClient is closed with the SDK.
Download and stream URL builders take the account download token, never the access token, because their URLs leave the app for players, cast receivers, and caches:
val downloadToken = sdk.account.getInfo(AccountInfoQuery(downloadToken = true)).downloadToken
?: error("account returned no download token")
val url = sdk.files.buildHlsStreamUrl(fileId = file.id, downloadToken = downloadToken)Treat the returned URL as a credential: do not log it or attach it to errors.
sdk.deviceCodeAuth.link().collect { state ->
when (state) {
is DeviceCodeAuthState.AwaitingLink -> showCode(state.code, state.qrCodeUrl)
is DeviceCodeAuthState.Linked -> tokenStore.save(state.accessToken)
is DeviceCodeAuthState.Expired -> offerNewCode()
is DeviceCodeAuthState.Failed -> showError(PutioErrorLocalizer.localize(state.error))
DeviceCodeAuthState.Requesting, DeviceCodeAuthState.Validating -> showSpinner()
}
}One collection is one attempt; collect again for a new code. Polling, timeout, and token validation live in the SDK; see the device-code contract.
val sdk = PutioClient(
PutioConfig(
clientId = "android-app",
clientName = "put.io Android"
)
)
val loginUrl = sdk.auth.buildLoginUrl(
redirectUri = "myapp://oauth",
state = "login"
)./gradlew verify
./gradlew liveTestBoth need a Java 21 runtime, and verify also needs Node for its Markdown check. verify is the deterministic gate; liveTest is opt-in against the real put.io API and needs credentials. Testing covers both.
This project is available under the MIT License
