SDK · Stable
Swift SDK
Official FluxChat SDK for Swift. iOS, macOS and server-side Swift, built on async/await and URLSession with Codable models.
Installation
dependencies: [
.package(url: "https://github.com/benflux-company/fluxchat-sdk.git", from: "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.
import FluxChat
let client = FluxChatClient(apiKey: ProcessInfo.processInfo.environment["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.
let response = try await client.ask(
message: "Quels sont vos horaires ?",
context: "Utilisateur : Alice, Plan : Pro",
sessionId: mySessionId
)
print(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.
let 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.
let ticket = try await 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.
let thread = try await support.thread(ticketId: ticket.ticketId, email: "jordan@example.com")
for m in thread.messages {
print(m.role, m.content) // .bot | .visitor | .agent
}
if thread.agentConnected { print("\(thread.agentName ?? "An agent") is answering") }
// 3. Let them reply into it.
try await support.reply(
ticketId: ticket.ticketId,
email: "jordan@example.com",
content: "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(403otherwise).
Knowledge base
Create, update and delete articles. Write operations need a key with the bot:write scope, or an admin JWT.
let kb = client.knowledge(jwtToken: jwt)
let article = try await kb.create(title: "Délais de livraison",
content: "Livraison sous 48 h en RDC.")
try await kb.delete(id: 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.
do {
_ = try await support.thread(ticketId: ticketId, email: "someone@else.com")
} catch let error as FluxChatError {
// 404 both when the ticket does not exist and when the e-mail does not
// own it — deliberately indistinguishable.
print(error)
}