Kotlin SDK
Install the Kotlin SDK, create a client, handle errors, and find every type and method by area.
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 TransportTransport.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,
) : TransportSovaria.baseUrl
A property.
val baseUrl: StringSovaria.storage
A property.
val storage: SessionStorageSovaria.transport
How requests and realtime sockets are sent (HttpTransport.default unless given).
val transport: HttpTransportSovaria.api
A property.
val api: GeneratedApiSovaria.auth
A property.
val auth: SovariaAuthSovaria.db
A property.
val db: SovariaDatabaseSovaria.files
A property.
val files: SovariaStorageSovaria.functions
A property.
val functions: SovariaFunctionsSovaria.realtime
A property.
val realtime: SovariaRealtimeSovaria.offline
A property.
@Volatile var offline: SovariaOffline?Sovaria.crashes
A property.
val crashes: SovariaCrashesSovaria.simulate
Simulation (team members): who can do what in the live project, with nothing written.
val simulate: SovariaSimulationSovaria.push
Push notification tokens for the signed-in user.
val push: SovariaPushSovaria.collections
Document collections (Firestore-style).
val collections: SovariaCollectionsSovaria.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,
): SovariaOfflineReturns 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,
): SovariaSovariaException
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?): SovariaExceptionasMap
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 WebSocketListenerWebSocketListener.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 WebSocketConnectionWebSocketConnection.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 HttpTransportHttpTransport.execute
Send the request and read the whole answer.
suspend fun execute(request: TransportRequest): TransportResponseA 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): WebSocketConnectionThrows if the connection or handshake fails.
HttpTransport.isAndroid
True on Android (where java.net.http doesn't exist).
val isAndroid: Boolean by lazyHttpTransport.default
The platform's transport: UrlConnectionTransport on Android, JdkHttpTransport elsewhere (the system property sovaria.http.transport = urlconnection | jdk overrides it).
fun default(): HttpTransportThe 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(),
) : HttpTransportJdkHttpTransport.execute
override suspend fun execute(request: TransportRequest): TransportResponseJdkHttpTransport.openWebSocket
override suspend fun openWebSocket(url: String, headers: Map<String, String>, listener: WebSocketListener): WebSocketConnectionUrlConnectionTransport
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,
) : HttpTransportUrlConnectionTransport.execute
override suspend fun execute(request: TransportRequest): TransportResponseUrlConnectionTransport.openWebSocket
override suspend fun openWebSocket(url: String, headers: Map<String, String>, listener: WebSocketListener): WebSocketConnectionMAX_MESSAGE_BYTES
Largest message accepted; a bigger one closes the socket (1009) instead of exhausting memory.
const val MAX_MESSAGE_BYTESconnect
fun connect(uri: URI, headers: Map<String, String>, connectTimeoutMs: Int, tls: SSLSocketFactory,
listener: WebSocketListener): SocketWebSocket