clariodesk
In-app support and bug reporting for Flutter apps. The native half of the ClarioDesk SaaS — drop the SDK in, get a hardware-bound device identity and a live ticket UI. Anonymous/device identity needs no host backend; verified cross-device identity uses a host token endpoint or pinned OIDC.
Integrating with an AI agent? (Claude Code, Cursor, Codex) Paste the install prompt and it does this whole README for you — detect, ask, install, verify. Machine-readable docs: llms.txt · rules for AI agents.
What ships
ClarioDesk.init,ClarioDesk.identify,ClarioDesk.createTicket,ClarioDesk.sendMessage,ClarioDesk.ticketsStream,ClarioDesk.messagesStream,ClarioDesk.reset.- iOS Secure Enclave / Android Keystore-backed ECDSA P-256 keypair. Generated on first launch, never extractable.
- Challenge-response device registration, per-request signatures, signed SSE handshakes.
Quick start
import 'package:clariodesk/clariodesk.dart';
Future<void> main() async {
// First call generates the hardware key + registers with the backend.
// Subsequent launches reuse the existing key (no network).
await ClarioDesk.init(apiKey: 'pk_live_…');
runApp(const MyApp());
}
// Without a token/provider this is optional, unverified display metadata.
// externalId = your auth's stable user id (Firebase uid, Clerk user.id) —
// never an auth token, never an email.
// See "Lifecycle integration" below for the four host events you need
// to wire (especially case 1, which covers users who were already
// logged in before you installed the SDK).
await ClarioDesk.identify(
externalId: hostUser.id,
email: hostUser.email,
traits: {'plan': 'pro'},
);
// File a ticket.
final t = await ClarioDesk.createTicket(
subject: 'Upload broken',
body: 'Tapping upload does nothing.',
);
// Reactive inbox — auto-primes + live updates.
StreamBuilder<List<Ticket>>(
stream: ClarioDesk.ticketsStream(),
builder: (_, snap) => /* … */,
);
Lifecycle integration
Four host events touch the SDK. Wire each one and you're done.
1. App launch (every time, including for already-logged-in users)
The most common integration miss: existing users who installed your app before you added ClarioDesk never go through the login flow again, so an "identify on login" hook alone leaves them as unlabeled devices.
Hydrate from your own persisted session on launch and identify
unconditionally — identify() is idempotent (same values = no-op write)
and cheap (one signed POST, ~5–25 ms).
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await ClarioDesk.init(apiKey: 'pk_live_…');
final user = await yourAuth.currentUser(); // Firebase, Supabase,
// your own session store, …
if (user != null) {
await ClarioDesk.identify(
externalId: user.id,
email: user.email,
traits: {'plan': user.plan},
);
}
runApp(const MyApp());
}
2. Fresh login or signup
Right after your auth succeeds:
await yourAuth.signIn(email, password);
await ClarioDesk.identify(
externalId: user.id,
email: user.email,
traits: {'plan': user.plan},
);
This overwrites the label on the existing device row — no new device gets registered, no new hardware key gets generated. The same device now carries the new user's metadata.
3. Logout
await ClarioDesk.clearIdentity(); // when Verified identity is enabled
await yourAuth.signOut();
clearIdentity() preserves the key and advances the signed identity epoch.
reset() is acknowledged one-installation deprovisioning, not logout or
customer erase.
4. User switch (account A → account B without app restart)
await ClarioDesk.clearIdentity();
await yourAuth.switchTo(userB);
await ClarioDesk.identify(identityMode: IdentityMode.host);
Create and await the clear intent before activating user B. Invalid proofs never fall back to label-only identity.
What if I never call identify()?
Tickets still work. The device row exists, agents see a device id but
no email — the dashboard shows an "Unverified device" badge. Useful
for anonymous-feedback flows; otherwise call identify() from case
1 above and you're covered.
Auth model
You ship one publishable API key (pk_live_…) in your app binary. Its
only capability is letting a fresh install register a device with us —
it cannot read tickets or impersonate users. Every authenticated call
the SDK makes is signed by the device's hardware-bound private key.
If your key leaks, an attacker can register throwaway devices (rate- limited and bounded) but cannot touch any existing user's data. There is no separate "secure mode" to enable, no HMAC backend to build, no identity-verification setup. You ship the key, your users are secure by default.
Full documentation at docs.clariodesk.com.
Push notifications
The host owns firebase_messaging and passes a PushTokenProvider to
ClarioDesk.init; the SDK never imports Firebase. Route shared handlers with
ClarioDesk.isClarioMessage(message.data).
On a notification tap in 0.3.0+, await
ClarioDesk.resolvePushTarget(message.data) and route the returned
TicketPushTarget or InboxPushTarget with your Navigator/GoRouter. The raw
payload deliberately contains no ticket id: its opaque deliveryId is resolved
only by the signed API. With the prebuilt UI, call
ClarioDeskWidgets.openFromPush(context, message.data).
Custom screens mark only their focused support route:
await ClarioDesk.setVisibleSupportSurface(SupportSurface.inbox);
await ClarioDesk.setVisibleSupportSurface(SupportSurface.ticket(ticketId));
await ClarioDesk.clearVisibleSupportSurface(); // blur/pop
The prebuilt UI owns this automatically. A reply alerts immediately everywhere
else; a stale visible-route lease can wait at most two seconds for the specific
realtime render acknowledgement. Handle both
FirebaseMessaging.onMessageOpenedApp and killed-app getInitialMessage()
after ClarioDesk and your Navigator/GoRouter are ready.
See the push guide.
Platforms
- iOS — Secure Enclave when present (most iPhones since 5s), software-backed Keychain otherwise. Requires iOS 13+.
- Android — StrongBox when supported (Pixel 3+, recent Samsung), TEE-backed Keystore otherwise. Requires API 23+ (Marshmallow).
Example app
See example/. Runs against staging or local API:
cd example
flutter run --dart-define=CLARIODESK_API_KEY=pk_live_…
Libraries
- clariodesk
- ClarioDesk Flutter SDK — headless surface.
- widgets
- ClarioDesk pre-built UI — drop-in support chat + bug-report screens.