Docs

Kotlin SDK

Install the Kotlin SDK, create a client, handle errors, and find every type and method by area.

Updated Oct 24, 2026

The Sovaria client for Android and the JVM: coroutines (suspend), java.net.http and kotlinx.serialization. It needs JDK 11+ (Android 8, API 26+) and has the same surface as the Dart, Swift, TypeScript and Python clients.

#Install

dependencies {
    implementation("in.sovaria:sovaria:1.0.0")
}

#Create a client

Use the project URL and the anon key from the console's Connect panel. The anon key is public and safe to ship in an app. Never put the service role key in a mobile app. For a full walkthrough see Android quickstart (Kotlin).

import `in`.sovaria.*

val sovaria = Sovaria.initialize(
    url = "https://<project-ref>.sovarias.in",
    anonKey = "<anon-key>",
    storage = KeystoreStore(context),   // your SessionStorage; the default keeps the session in memory
)
sovaria.auth.signInWithPassword("ada@example.com", "Correct-Horse-9!")

#Errors

Failed calls throw with the server's machine-readable code (for example PLAN_LIMIT_REACHED or INVALID_QUERY) and a readable message. See Troubleshooting.

try {
    sovaria.db.sql("select * from orders")
} catch (e: SovariaException) {
    println("${e.code}: ${e.message}")   // e.status is the HTTP status
}

#Reference by area

#Client

Transport

What the generated API needs from a client: send one request, and know the default scope.

interface Transport

Transport.call

suspend fun call(method: String, path: String, body: Any? = null, query: Map<String, Any?>? = null, auth: Boolean = true): Any?

Transport.scope

fun scope(name: String): String?

Sovaria

The Sovaria client for Kotlin (Android and the JVM). HTTP and WebSockets go through transport: HttpURLConnection + a built-in WebSocket client on Android, java.net.http on the JVM.

class Sovaria(
    url: String,
    val anonKey: String,
    orgId: String? = null,
    projectId: String? = null,
    envId: String? = null,
    storage: SessionStorage? = null,
    val device: SovariaDevice? = null,
    transport: HttpTransport? = null,
    val retries: Int = 3,
    val retryBaseDelayMs: Long = 200,
    val refreshMarginSeconds: Long = 60,
    val sessionKey: String = "",
    attestation: AttestationProvider? = null,
) : Transport

Sovaria.baseUrl

A property.

val baseUrl: String

Sovaria.storage

A property.

val storage: SessionStorage

Sovaria.transport

How requests and realtime sockets are sent (HttpTransport.default unless given).

val transport: HttpTransport

Sovaria.api

A property.

val api: GeneratedApi

Sovaria.auth

A property.

val auth: SovariaAuth

Sovaria.db

A property.

val db: SovariaDatabase

Sovaria.files

A property.

val files: SovariaStorage

Sovaria.functions

A property.

val functions: SovariaFunctions

Sovaria.realtime

A property.

val realtime: SovariaRealtime

Sovaria.offline

A property.

@Volatile var offline: SovariaOffline?

Sovaria.crashes

A property.

val crashes: SovariaCrashes

Sovaria.simulate

Simulation (team members): who can do what in the live project, with nothing written.

val simulate: SovariaSimulation

Sovaria.push

Push notification tokens for the signed-in user.

val push: SovariaPush

Sovaria.collections

Document collections (Firestore-style).

val collections: SovariaCollections

Sovaria.attestation

App attestation (X-Sovaria-Attestation), when the client was created with an AttestationProvider.

val attestation: AttestationManager?

Sovaria.orgId

A property.

val orgId get()

Sovaria.projectId

A property.

val projectId get()

Sovaria.envId

A property.

val envId get()

Sovaria.setScope

fun setScope(orgId: String? = null, projectId: String? = null, envId: String? = null)

Sovaria.enableOffline

Turn on offline-first for the data API: writes made with no network are queued and replayed in order on reconnect; db.select/reads fall back to the last cached result.

fun enableOffline(
    store: OfflineStore = FileOfflineStore(),
    namespace: String? = null,
    onSyncError: ((SovariaException, Map<String, Any?>) -> Unit)? = null,
    onSynced: ((Int) -> Unit)? = null,
): SovariaOffline

Returns the controller.

The queue and cache are kept in files in the app's private data folder by default (FileOfflineStore), so writes made offline still sync after the app is closed and reopened. Pass store = MemoryOfflineStore() to keep them for this run only.

Sovaria.scope

override fun scope(name: String): String?

Sovaria.resolveScope

Learn the org/project behind the project URL (), once.

suspend fun resolveScope()

Sovaria.call

override suspend fun call(method: String, path: String, body: Any?, query: Map<String, Any?>?, auth: Boolean): Any?

Sovaria.request

Send one request and decode the JSON answer.

suspend fun request(
    method: String,
    path: String,
    body: Any? = null,
    query: Map<String, Any?>? = null,
    auth: Boolean = true,
    headers: Map<String, String>? = null,
    bytes: ByteArray? = null,
    contentType: String? = null,
    raw: Boolean = false,
): Any?

Signs with the session (refreshed near expiry, and once more after a 401) or the anon key; retries transient failures with backoff. Returns parsed JSON (Map/ List/primitive), or the raw ByteArray when raw is true.

Sovaria.initialize

Create the client and restore the stored session (if any).

suspend fun initialize(
    url: String, anonKey: String, orgId: String? = null, projectId: String? = null, envId: String? = null,
    storage: SessionStorage? = null, device: SovariaDevice? = null, transport: HttpTransport? = null,
    attestation: AttestationProvider? = null,
): Sovaria

SovariaException

An error answer from Sovaria (or a client-side problem such as missing configuration). Mirrors the Dart/Swift/TypeScript/Python clients: a machine-readable code (PLAN_LIMIT_REACHED, PKCE_REQUIRED, APP_ATTESTATION_REQUIRED…) plus the HTTP status (0 for network/config errors).

class SovariaException(
    val status: Int,
    override val message: String,
    val code: String = "",
    val details: Any? = null,
    val traceId: String? = null,
) : Exception(message)

SovariaException.isUnauthorized

A property.

val isUnauthorized get()

SovariaException.isPlanLimit

A property.

val isPlanLimit get()

SovariaException.needsAttestation

A property.

val needsAttestation get()

SovariaException.toString

override fun toString()

SovariaException.config

A client-side configuration problem (no request was made).

fun config(message: String)

SovariaException.from

Build the error from a response body (FastAPI detail, gateway error/code).

fun from(status: Int, body: Any?): SovariaException

asMap

Convenience casts for the Map<String, Any?> results.

fun Any?.asMap(): Map<String, Any?>

asList

fun Any?.asList(): List<Any?>

asRows

fun Any?.asRows(): List<Map<String, Any?>>

TransportRequest

One HTTP request as the client sends it: an absolute URL, headers, an optional body.

class TransportRequest(
    val method: String,
    val url: String,
    val headers: Map<String, String>,
    val body: ByteArray?,
    val timeoutMs: Long,
)

TransportResponse

One HTTP answer. Header names are lower-case.

class TransportResponse(val status: Int, val headers: Map<String, List<String>>, val body: ByteArray)

TransportResponse.header

fun header(name: String): String?

WebSocketListener

Events from an open WebSocket. Called from the transport's own thread, in order.

interface WebSocketListener

WebSocketListener.onOpen

fun onOpen()

WebSocketListener.onText

fun onText(text: String)

WebSocketListener.onPong

fun onPong()

WebSocketListener.onClosed

The socket ended: closed by either side, or failed (error set).

fun onClosed(code: Int, reason: String, error: Throwable?)

Called once.

WebSocketConnection

An open WebSocket.

interface WebSocketConnection

WebSocketConnection.sendText

fun sendText(text: String)

WebSocketConnection.sendPing

fun sendPing()

WebSocketConnection.close

Close politely (a close frame), then drop the connection.

fun close(code: Int = 1000, reason: String = "")

WebSocketConnection.abort

Drop the connection at once (a dead socket).

fun abort()

HttpTransport

How the client talks HTTP and WebSockets. The default picks per platform: UrlConnectionTransport on Android (HttpURLConnection + a built-in WebSocket client — Android has no java.net.http), JdkHttpTransport (java.net.http) on the JVM. Pass your own to use OkHttp, a proxy or certificate pinning.

interface HttpTransport

HttpTransport.execute

Send the request and read the whole answer.

suspend fun execute(request: TransportRequest): TransportResponse

A network failure throws (any exception); an HTTP error status is returned, not thrown.

HttpTransport.openWebSocket

Open a WebSocket to url (ws:// / wss://); listener gets onOpen when the handshake succeeds.

suspend fun openWebSocket(url: String, headers: Map<String, String>, listener: WebSocketListener): WebSocketConnection

Throws if the connection or handshake fails.

HttpTransport.isAndroid

True on Android (where java.net.http doesn't exist).

val isAndroid: Boolean by lazy

HttpTransport.default

The platform's transport: UrlConnectionTransport on Android, JdkHttpTransport elsewhere (the system property sovaria.http.transport = urlconnection | jdk overrides it).

fun default(): HttpTransport

The JDK one is loaded by name so Android never resolves java.net.http classes.

JdkHttpTransport

The JVM transport: java.net.http (Java 11+). Not available on Android — see UrlConnectionTransport.

class JdkHttpTransport @JvmOverloads constructor(
    /** The client to use (its timeouts, proxy, executor, TLS): one per app. */
    val client: HttpClient = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(15)).build(),
) : HttpTransport

JdkHttpTransport.execute

override suspend fun execute(request: TransportRequest): TransportResponse

JdkHttpTransport.openWebSocket

override suspend fun openWebSocket(url: String, headers: Map<String, String>, listener: WebSocketListener): WebSocketConnection

UrlConnectionTransport

The Android transport (works on any JVM too): HttpURLConnection for requests and a small RFC 6455 WebSocket client over a plain or TLS socket for realtime. No dependencies, nothing from java.net.http. TLS uses the platform's trust store and checks the certificate's host name, like HttpsURLConnection.

class UrlConnectionTransport @JvmOverloads constructor(
    private val connectTimeoutMs: Int = 15_000,
    private val sslSocketFactory: SSLSocketFactory = SSLSocketFactory.getDefault() as SSLSocketFactory,
) : HttpTransport

UrlConnectionTransport.execute

override suspend fun execute(request: TransportRequest): TransportResponse

UrlConnectionTransport.openWebSocket

override suspend fun openWebSocket(url: String, headers: Map<String, String>, listener: WebSocketListener): WebSocketConnection

MAX_MESSAGE_BYTES

Largest message accepted; a bigger one closes the socket (1009) instead of exhausting memory.

const val MAX_MESSAGE_BYTES

connect

fun connect(uri: URI, headers: Map<String, String>, connectTimeoutMs: Int, tls: SSLSocketFactory,
            listener: WebSocketListener): SocketWebSocket