The contract is tiny
The SDK keeps all of its state internal and private. Your app’s state management — Riverpod, Bloc, Provider, Redux, Zustand, Context, MobX — is never involved. There are only ever a handful of calls from your app into the SDK:init(apiKey)once at app start.identify({ externalId, email, traits })when the user logs in or changes.handlePushPayload(...)/openTicketFromPush(...)when a push fires.reset()on logout or user switch.
Identity in, push in, nothing else out. That’s the entire contract. Reads come back to you as live streams (Flutter) or subscriptions/hooks (React Native), not as state you have to wire.
Headless vs prebuilt
Headless
Public methods + live reads only. You build the UI in your own design
system. This is the primary integration mode.
Prebuilt screens
Inbox, ticket detail, new ticket, and bug report — presented in a
self-owned modal. Limited theming (colors + app bar). Minutes to integrate.
Routing — the sandbox model
The prebuilt UI never touches your router (GoRouter, Navigator 2.0, React Navigation, Expo Router). Your app pushes one route — a full-screen modal — and the SDK owns a private nested navigator inside it for all internal navigation (inbox → ticket detail → compose → image viewer). Control returns to your app when the user dismisses the modal.Data model
Three objects you’ll touch:| Object | What it is |
|---|---|
| Ticket | One support conversation. Has a status (new → open → resolved → closed), an unread indicator, and a last-message preview. |
| Message | One entry in a ticket thread. Carries body, an author type (end user / agent / system), a timestamp, and any attachments. |
| Attachment | A photo, video, or file bound to a message. Rendered via presigned URLs. |
Where to go next
Authentication
Why there are no JWTs and the publishable key is safe to ship.
Identity & lifecycle
The four host events that touch the SDK — login, launch, logout, switch.