uchara_sdk 1.0.2
uchara_sdk: ^1.0.2 copied to clipboard
Official Flutter SDK for the Uchara Chat Platform. Headless customer/visitor SDK for embedding realtime chat into Flutter apps: session init, conversations, messages, file uploads, transcript download [...]
Uchara SDK for Flutter #
The official headless Flutter SDK for the Uchara Chat Platform. It lets you embed realtime customer/visitor chat into any Flutter application — Android, iOS, web, macOS, Windows, and Linux — without shipping a pre-built widget.
The SDK is fully headless: it exposes a small, typed API for session initialisation, conversations, messages, file uploads, transcript download, and a resilient WebSocket client. You bring your own UI.
Features #
- Session initialisation — exchange a public widget token for a visitor JWT and contact identity.
- Conversations — fetch the active conversation, start a new one, and close one.
- Messages — list messages with offset/limit pagination and send new ones.
- Files — upload attachments via
uploadFileand download the full conversation transcript. - Realtime — a resilient WebSocket client with typed events, keepalive
pings, and bounded exponential-backoff reconnection. The connection
automatically switches to the active conversation's room after
startConversationorgetActiveConversation. - Token persistence and refresh — pluggable
TokenStore(in-memory by default),restoreSession, and automatic visitor JWT renewal for long-lived applications. - Structured errors — every failure is a typed exception you can catch and react to.
Installation #
Add uchara_sdk to your pubspec.yaml:
dependencies:
uchara_sdk: ^1.0.0
Then run:
flutter pub get
Quick start #
import 'package:uchara_sdk/uchara_sdk.dart';
final sdk = VisitorSDK(VisitorConfig(
apiUrl: 'https://api.uchara.com',
widgetToken: 'YOUR_PUBLIC_WIDGET_TOKEN',
identity: VisitorIdentity(
externalId: 'user-123',
name: 'Ada Lovelace',
email: 'ada@example.com',
),
));
// 1. Initialise the session (opens the realtime connection when
// `autoConnect` is enabled, which is the default).
final session = await sdk.init();
// 2. Find or start a conversation.
var conversation = await sdk.getActiveConversation();
conversation ??= await sdk.startConversation(message: 'Hello!');
// 3. Send a message.
final sent = await sdk.sendMessage(
conversation.id,
content: 'Is anyone there?',
);
// 4. Read the transcript (paginated).
final page = await sdk.getMessages(conversation.id, limit: 50);
// 5. Listen to realtime events.
final sub = sdk.events?.listen((event) {
if (event is MessageNewEvent) {
print('New message: ${event.message.content}');
}
});
// 6. Release all resources when done.
sub?.cancel();
sdk.dispose();
Never hardcode a real widget token in source control. Widget tokens are public by design, but treat them like any other credential and load them from a secure configuration source at runtime. See SECURITY.md.
Configuration #
VisitorConfig accepts the following options:
| Option | Default | Description |
|---|---|---|
apiUrl |
— | Base URL of the Uchara API, e.g. https://api.uchara.com. |
widgetToken |
— | The public widget token for the channel. |
identity |
— | Optional VisitorIdentity (external id, name, email, phone, metadata). |
autoConnect |
true |
Open the realtime connection automatically after init(). |
timeout |
30s |
Per-request HTTP timeout. |
tokenStore |
InMemoryTokenStore |
Pluggable token persistence. |
autoReconnect |
true |
Reconnect the WebSocket after an unexpected drop. |
maxReconnectAttempts |
10 |
Maximum WebSocket reconnect attempts. |
initialReconnectDelay |
500ms |
Delay before the first reconnect attempt. |
maxReconnectDelay |
30s |
Upper bound for the reconnect delay. |
autoPresenceHeartbeat |
true |
Automatically refresh visitor online presence while connected. |
presenceHeartbeatInterval |
20s |
Interval for the presence heartbeat; the backend presence TTL is 35 seconds. |
autoRefreshSession |
true |
Automatically renew the visitor JWT before expiry. |
sessionRefreshBeforeExpiry |
5m |
How early to renew the visitor JWT. |
sessionRefreshInterval |
12h |
Fallback refresh interval when token expiry cannot be decoded. |
autoPresenceHeartbeat |
true |
Automatically refresh visitor online presence while connected. |
presenceHeartbeatInterval |
20s |
Interval for the presence heartbeat; the backend presence TTL is 35 seconds. |
Realtime events #
The events stream yields typed WSEvent subclasses:
| Event | Meaning |
|---|---|
MessageNewEvent |
A new message was created. |
MessageDeltaEvent |
A streaming bot response chunk. |
TypingEvent |
A typing indicator (or its stop signal). |
PresenceEvent |
An online/offline presence update. |
ConversationResolvedEvent |
A conversation was resolved. |
ConversationEvent |
A generic conversation.* event. |
UnknownEvent |
A forward-compatible event the SDK does not yet model. |
Token persistence #
By default the visitor token lives only for the lifetime of the process. To
persist it across launches, provide your own TokenStore backed by e.g.
shared_preferences or flutter_secure_storage:
class PrefsTokenStore implements TokenStore {
@override
Future<String?> read() async => prefs.getString('uchara_visitor_token');
@override
Future<void> write(String token) async =>
prefs.setString('uchara_visitor_token', token);
@override
Future<void> clear() async => prefs.remove('uchara_visitor_token');
}
Restoring a session #
On a subsequent launch, restore the persisted session without re-authenticating.
restoreSession reads the stored token, re-establishes the in-memory session,
and (by default) opens the realtime connection:
final restored = await sdk.restoreSession(conversationId: 'c1');
if (restored) {
// The SDK is ready: getConfig, getActiveConversation, startConversation and
// connect all work without calling init() again.
}
Pass connect: false to restore the session without opening the WebSocket, and
conversationId to subscribe to a specific conversation's room. restoreSession
returns false when no token is stored.
Sending messages and attachments #
sendMessage sends plain text messages. To attach files, upload them first with
uploadFile — the backend creates the message carrying the attachment:
final msg = await sdk.uploadFile(
conversation.id,
filename: 'receipt.pdf',
bytes: pdfBytes,
contentType: 'application/pdf',
);
The legacy attachmentIds / attachments parameters on sendMessage are
deprecated and ignored; sending attachments inline via sendMessage is not
supported by the backend.
Metadata caveat #
VisitorIdentity.metadata, Conversation.metadata and Message.metadata are
free-form maps passed through to the backend and echoed back. The SDK does not
validate their contents or schema — treat them as untrusted input in your UI,
and keep them JSON-serialisable.
Realtime connection #
The WebSocket connects to /ws/visitor derived from apiUrl (which must use
http or https; the scheme is upgraded to ws/wss). The visitor token is
always sent as the token query parameter, which the backend requires — see
SECURITY.md. When an active conversation is known, its id is sent
as the conv query parameter so the connection subscribes to that room. After
startConversation or getActiveConversation, the connection automatically
switches to the new conversation's room.
Error handling #
All errors extend UcharaException:
| Type | When |
|---|---|
ServerEnvelopeException |
The server returned a structured error envelope. |
ApiException |
An HTTP error without a structured envelope. |
NetworkException |
A transport-level failure (DNS, refused, reset). |
UcharaTimeoutException |
A request exceeded its configured timeout. |
ProtocolException |
A WebSocket protocol violation or malformed payload. |
try {
await sdk.init();
} on ServerEnvelopeException catch (e) {
// e.code, e.statusCode, e.message
} on NetworkException catch (e) {
// offline / unreachable
}
Example #
A runnable example app is included under example/. It demonstrates
init, conversations, messages, realtime events, and dispose end to end:
cd example
flutter run
Documentation #
Contributing #
See CONTRIBUTING for guidelines. Please report security issues privately — see SECURITY.md.
License #
MIT