fluxchat/sdk

SDK · Stable

Kotlin / JVM SDK

v0.3.0 · Stable

Official FluxChat SDK for Kotlin and the JVM. Android, Spring Boot and Ktor servers — coroutines throughout, Ktor client, kotlinx.serialization models.

SourceContributed by @benjaminmugangu — Kotlin SDK

Installation

build.gradle.kts
dependencies {
    implementation("com.fluxchat:sdk:0.3.0")
}

Create a client

The base URL defaults to https://api.fluxchat-corp.com/api/v2 — production. Pass it explicitly to target dev.

Client.kt
import com.fluxchat.FluxChatClient

val client = FluxChatClient(apiKey = System.getenv("FLUXCHAT_API_KEY"))

ask()

Send a message and get the assistant's reply. Pass context to inject real-time data the bot treats as a priority source of truth, above the knowledge base. A sessionId keeps multi-turn history without persisting a conversation.

Ask.kt
val response = client.ask(
    message = "Quels sont vos horaires ?",
    context = "Utilisateur : Alice, Plan : Pro",
    sessionId = mySessionId,
)

println(response.reply)

Human handoff (support thread)

When the bot can't help, hand the visitor to a human. This creates a real support ticket in the organization's dashboard — and unlike a contact form, the conversation stays open in the same thread: the visitor polls it, the agent answers into it.

Handoff.kt
val support = client.support()

// 1. Hand off — with the same sessionId you pass to ask, the bot's side of
//    the conversation is copied into the ticket.
val ticket = support.escalate(
    name = "Jordan Mbeki",
    email = "jordan@example.com",
    message = "Mon virement est bloqué depuis hier.",
    sessionId = mySessionId,
)

// 2. Follow the thread: the e-mail proves the ticket is theirs.
val thread = support.thread(ticket.ticketId, "jordan@example.com")
thread.messages.forEach { println("${it.role}: ${it.content}") }  // bot | visitor | agent
if (thread.agentConnected) println("${thread.agentName} is answering")

// 3. Let them reply into it.
support.reply(ticket.ticketId, "jordan@example.com", "La référence est 4821.")
  • Ownership is the e-mail. A wrong e-mail and a non-existent ticket both return 404, deliberately — a guessed ticket id reveals nothing. The lookup is also scoped to the organization behind your API key.
  • Polled, not pushed. There is no socket on the public surface. 5s is the cadence the official widget uses and what the route's limits are sized for: 60 reads/min, 30 replies/min, 5 escalations/min.
  • Statuses: open → in_progress → resolved → closed. A reply to a resolved ticket reopens it; a closed one refuses the write (403) rather than swallowing a message nobody will read.
  • Requires the organization's plan to include supportTickets (403 otherwise).

Knowledge base

Create, update and delete articles. Write operations need a key with the bot:write scope, or an admin JWT.

Knowledge.kt
val kb = client.knowledge(jwtToken)

val article = kb.create(title = "Délais de livraison", content = "Livraison sous 48 h en RDC.")
kb.delete(article.id)

Errors

Every failure is typed: an API error carries the HTTP status and the server's message, a network error wraps the transport failure, and a config error is raised before any request leaves.

Errors.kt
try {
    support.thread(ticketId, "someone@else.com")
} catch (e: FluxChatApiException) {
    // 404 both when the ticket does not exist and when the e-mail does not
    // own it — deliberately indistinguishable.
    println("${e.statusCode} ${e.message}")
} catch (e: FluxChatNetworkException) {
    println("transport failure: ${e.message}")
}