Skip to content

Kotlin (Android, JVM) API

Generated from the source and its doc comments (sdk/client-kotlin/src/main/kotlin/com/agentim/client/AgentIMClient.kt).

// build.gradle.kts (the Maven Central group is not final yet)
implementation("com.agentim:client:0.1.0")
class ClientOptions(

How the client starts: the server address, this device’s id, the cache, the token provider and how it names itself.

val url: String,

https://chat.example.com.

val deviceId: String,

This device’s id, the same every run (1..64 of A-Z a-z 0-9 _ -).

val tokenProvider: suspend () -> String,

A client token from your backend; throw [TokenRefused] to stop.

val storeDir: String? = null,

Where the cache lives; null keeps it in memory.

val deviceName: String? = null,

A name for this device in the user’s device list, like “Maya’s iPhone”.

val platform: String? = null,

The platform reported to the server (ios, android, windows…), for the device list and push.

val clientName: String = "agentim-kotlin",

This app’s name, sent to the server with each connection (for diagnostics and minimum-version rules).

val clientVersion: String = "0.1.0",

This app’s version, sent with clientName.

val commandTimeoutMs: Long = 60_000,

How long a command waits for its answer before failing with timeout. In milliseconds.

class TokenRefused(message: String) : Exception(message)

Thrown by a token provider when the backend refuses the user.

class AgentIMException(

The server’s code (forbidden, not_found, conflict, bad_request, blocked, rate_limited…) or the client’s (disconnected, timeout, closed…); unknown codes pass through as they are.

val code: String,

The error code: the server’s (forbidden, not_found, conflict, bad_request, …) or the client’s (disconnected, timeout, closed, unauthenticated).

val retryable: Boolean = false,

Whether the same request may work later.

val retryAfterMs: Long? = null,

How long the server asked to wait before trying again.

data class Connection(

The connection state and why: state is closed, connecting, connected, disconnected, suspended or failed.

val state: String,

closed, connecting, connected, disconnected, suspended or failed.

val reason: String? = null,

Why it is not connected (an error code), when it is not.

val retryAt: Long? = null,

When the next reconnect attempt is due (Unix ms), while waiting to reconnect.

class Messages internal constructor(private val client: AgentIMClient, val convId: String)

One conversation’s messages as shown, oldest first; each has key, content, status (sent, sending, failed)…

val list: StateFlow<List<JSONObject>> = flow.asStateFlow()

The messages as shown, oldest first, kept up to date.

suspend fun loadOlder(): Boolean

The page before the oldest shown: the cache first, then the server. False at the start.

suspend fun jumpTo(seq: Long) = client.command(JSONObject().put("cmd", "jump_to").put("conv_id", convId).put("seq", seq))

A page around a message (it stays loaded).

class AgentIMClient private constructor(private val o: ClientOptions) : AutoCloseable

The AgentIM client for one account on one device: keeps the connection, syncs, sends the offline queue in order, keeps the local cache, and turns everything into observable state.

val connection: StateFlow<Connection> = connectionFlow.asStateFlow()

The connection state (closed, connecting, connected, disconnected, suspended, failed) and why; subscribe to follow it.

val conversations: StateFlow<List<JSONObject>> = conversationsFlow.asStateFlow()

The conversation list, newest first, kept up to date.

suspend fun command(cmd: JSONObject): Any? = command(cmd, null)

Any core command ({"cmd": …}); returns its result (a JSONObject, or null).

fun on(type: String, fn: (JSONObject) -> Unit): AutoCloseable

Events: operation, side, signal, reset, storage, event, read, mute (and the ones above).

fun setNetwork(online: Boolean) = synchronized(gate)

The platform’s network: call on every change (Android: ConnectivityManager.NetworkCallback).

fun setForeground(foreground: Boolean) = synchronized(gate)

The app came to the foreground or went to the background (Android: ProcessLifecycleOwner).

fun messages(convId: String): Messages

The conversation’s messages, kept up to date.

suspend fun connect() = cmd("connect")

Connects and keeps connecting: reconnects with backoff when the network or the server goes, syncs what it missed, sends the offline queue.

suspend fun disconnect() = cmd("close")

Closes the connection (the client stays usable; [close] frees it).

suspend fun logout() = cmd("logout")

Logs out here: closes and wipes the cache and the queue (GDPR).

suspend fun send(

Returns {client_msg_id, key} once the message is queued and shown.

suspend fun sendFile(

Sends a file or a photo from a path: shown at once as uploading, sent when it is up. A photo loses its EXIF and GPS (unless [keepMetadata]) and gets a 320-pixel thumbnail (docs/en/design/messaging.md).

suspend fun fileUrl(convId: String, fileId: String, variant: String = "original")

A link (valid a few minutes) to read a file: original or thumb.

suspend fun edit(convId: String, msgId: String, text: String) = cmd("edit", "conv_id" to convId, "msg_id" to msgId, "text" to text)

Edits one of my messages (by msgId, or by clientMsgId while it is still queued); the change shows at once.

suspend fun recall(convId: String, msgId: String) = cmd("recall", "conv_id" to convId, "msg_id" to msgId)

Recalls one of my messages for everyone; a message still in the queue is simply not sent.

suspend fun read(convId: String, seq: Long? = null) = cmd("read", "conv_id" to convId, "seq" to seq)

Marks the conversation read up to seq (default: the newest) on all my devices.

suspend fun retry(convId: String, clientMsgId: String) = cmd("retry", "conv_id" to convId, "client_msg_id" to clientMsgId)

Sends a failed message again (the same client_msg_id, so it is never doubled).

suspend fun discard(convId: String, clientMsgId: String) = cmd("discard", "conv_id" to convId, "client_msg_id" to clientMsgId)

Drops a failed or waiting message from the queue.

suspend fun react(convId: String, msgId: String, key: String) = call("react", args("conv_id" to convId, "msg_id" to msgId, "key" to key))

Adds a reaction (one emoji, or one of the app’s :names:); adding it twice changes nothing.

suspend fun unreact(convId: String, msgId: String, key: String) = call("unreact", args("conv_id" to convId, "msg_id" to msgId, "key" to key))

Removes my reaction; removing one that is not there changes nothing.

suspend fun pin(convId: String, msgId: String) = call("pin", args("conv_id" to convId, "msg_id" to msgId))

Pins a message to the conversation; pinning it again changes nothing.

suspend fun unpin(convId: String, msgId: String) = call("unpin", args("conv_id" to convId, "msg_id" to msgId))

Unpins a message.

suspend fun listPins(convId: String) = call("list_pins", args("conv_id" to convId))

The conversation’s pinned messages, latest pin first.

suspend fun pullThread(convId: String, threadId: String, afterSeq: Long = 0)

A thread’s root and its replies after [afterSeq].

suspend fun listThreads(convId: String? = null) = call("list_threads", args("conv_id" to convId))

The threads I follow (in one conversation, or all), latest reply first, with unread counts.

suspend fun readThread(convId: String, threadId: String, seq: Long)

Marks a thread read up to [seq] on all my devices.

suspend fun followThread(convId: String, threadId: String, follow: Boolean = true)

Follows a thread (its replies notify me) or stops following it.

suspend fun msgSummary(convId: String, msgIds: List<String>)

The thread summary, reactions and pin of up to 100 messages (for messages loaded from history).

suspend fun call(op: String, args: JSONObject = JSONObject()) = cmd("call", "op" to op, "args" to args)

A request that needs the server now (approvals, decisions, queries).

suspend fun debugState() = cmd("debug_state")

Everything that helps report a problem: the connection, positions, the queue and recent failures.

override fun close()

Stops the client; no event arrives after it returns. Waiting commands fail with closed. Not from an event.

fun interface EventSink

The native side calls this with each event (UTF-8 JSON).

fun onEvent(json: ByteArray)

Receives one event from the native core as UTF-8 JSON.