Installation and initialisation
KletsoConfig fields, anonymous versus signed-in users, context, app events and lifecycle.
Install
flutter pub add kletso_flutter
Requires Dart 3.9 / Flutter 3.35 or newer. Optional companions: url_launcher (open url actions), a notifications plugin such as flutter_local_notifications (OS tray for system notifications), firebase_messaging (push tokens).
Initialise once
final client = await Kletso.init(KletsoConfig(
publishableKey: 'kl_pub_dev_…',
agentId: 'agt_…',
environment: KletsoEnvironment.development, // default: production
));
Kletso.init returns the client and makes it available as Kletso.instance. Calling it again disposes the previous client (handy on hot restart). Kletso.isInitialized and Kletso.reset() exist for tests.
KletsoConfig
| Field | Default | Purpose |
|---|---|---|
publishableKey | required | Must start with kl_pub_; the constructor throws on a secret key. |
agentId | required | The agent to talk to. Change at runtime with client.useAgent(id). |
environment | production | development or production. Should match the key. |
baseUrl | https://api.kletso.ai | Point at http://127.0.0.1:8788 for a local runtime. |
transport | auto | auto (WebSocket, then SSE after 2 failures), webSocket, sse. |
heartbeatInterval / heartbeatTimeout | 25 s / 10 s | Ping cadence and how long to wait for pong before reconnecting. |
connectTimeout | 10 s | Handshake budget. |
backoffBase / backoffCap | 500 ms / 30 s | Reconnect backoff with full jitter. |
outboundQueueLimit | 100 | Frames kept while offline; the oldest is dropped beyond this. |
typingIndicatorTtl | 4 s | How long agentTyping stays true without new deltas. |
reportLocalActions | true | Tell the runtime when a local action ran (for the transcript). |
allowServerCommands | false | Let the agent run your local actions without a tap (app.command). |
debugValidateUi | false | Validate every surface in debug builds and log issues. |
logLevel | warning | debug, info, warning, error, none. |
Optional constructor arguments to Kletso.init: api and transport (inject the fake backend), httpClient, logger, device (KletsoDevice(platform:, sdk:, version:, locale:) sent with the session), tokenStore.
Identity
Exactly one of these after init:
await client.identifyAnonymous();
// or
await client.authenticate(
token: hostJwt,
onTokenExpired: () async => await myBackend.freshKletsoToken(),
);
- Anonymous: a visitor id is generated and stored under the key
kletso.anonymousIdin thetokenStore. The default store isKletsoMemoryTokenStore, so implementKletsoTokenStoreover shared preferences or secure storage if visitors should keep their history across launches. - Signed in: the token is a JWT your backend signs (HS256 shared secret, or RS256/ES256 published at a JWKS URL; configured in Settings → End-user auth). Required claim
sub; optionalname,email,plan,locale,tier,exp,nbf,iss,aud. When the runtime rejects an expired token the refresher is called and the connection resumes. client.logout()revokes the session server side and clears local state.client.isAuthenticatedtells you whether a session exists.
Context
client.setContext({'plan': 'pro', 'currency': 'INR', 'locale': 'en-IN'});
client.updateContext({'screen': 'checkout', 'cartValue': 3198});
setContext replaces, updateContext merges. Context is stored with the session and the end user, and reaches the prompt (public keys), trigger conditions and tool templates. Keep it small and flat.
App events
client.track('geofence_entered', {'store': 'Shibuya'});
client.screen('checkout');
Both are silent: nothing appears in the chat unless a trigger rule reacts. They are sent over the socket when connected and queued otherwise. The SDK also tracks kletso.chat_opened (constant KletsoEvents.chatOpened) every time open() shows the chat, with messages (total so far) and userMessages (sent by the user) so a rule can greet only a fresh chat.
Lifecycle
KletsoLauncher and KletsoChat handle the app lifecycle for you (since kletso_flutter 0.2.2): the socket is closed cleanly when the app goes to the background and reconnected, with the missed events replayed, when it returns. A dropped connection is not an error: the client reports KletsoConnectionChanged(reconnecting) and then open; an error event only follows when the reconnect itself fails.
If you drive KletsoClient without those widgets, bind the lifecycle yourself:
final lifecycle = client.bindAppLifecycle(); // pause() in background, resume() in foreground
await client.pause(); await client.resume(); await client.dispose();
bindAppLifecycle (from kletso_flutter) returns an AppLifecycleListener you can dispose with your app state.