Android

Native Kotlin chat SDK with a full Jetpack Compose UI: AI chatbot, live-agent handoff, attachments, typing indicators and unread counts.

Installlink

mavenCentral() must be present in dependencyResolutionManagement; consumer R8/ProGuard rules ship in the AAR. minSdk 24, JDK 17.

implementation("cloud.newinstance:liveandaichat:0.3.0")

Quick startlink

val chat = LiveAndAiChat.Builder(context)
    .config(LiveAndAiChatConfig(apiKey = "sk_live_abc123"))
    .user(ChatUser(customerName = "Ada", customerEmail = "ada@example.com", customerId = "usr_123"))
    .build()

chat.openChat()

openChat() starts the bundled chat activity internally; Compose hosts can embed ChatScreen(sdk, onClose, onPickFile = {}, modifier) directly. ChatUser requires a non-blank customerName (it throws otherwise); switching to a different customer in setUser clears the saved conversation and starts fresh.

Authenticated customerslink

ChatUser.customerToken carries a chat identity token minted by your backend. When set it is the authoritative identity - the server derives name, email and customer id from its signed claims - so nothing else needs to be supplied:

val chat = LiveAndAiChat.Builder(context)
    .config(LiveAndAiChatConfig(apiKey = "sk_live_abc123"))
    .user(ChatUser.fromToken(tokenFromYourBackend))
    .build()

customerId without a token is recorded for agent context only and is not treated as identity. Refreshing a token for the same customer does not look like a user switch: the SDK compares the token's subject, not the token string.

Errorslink

LiveAndAiChat.state exposes a ChatSdkError with a stable code, and listeners receive onError(LiveAndAiChatError) carrying the same code. Codes are shared with every other chat SDK - see Authentication and identity -> Diagnosing problems. An auth failure while a token is in use is reported as INVALID_IDENTITY_TOKEN rather than INVALID_PUBLIC_KEY, which is the difference between rotating a key and minting a fresh token.

Configurationlink

FieldDefaultNotes
apiKeyrequiredPublishable widget key ID. A keyId:secret value is stripped to the ID with a warning
baseUrlhttps://service.newinstance.cloudRelease builds throw on any override (debug-only escape hatch)
gqlPath / ssePath / wsPath/service / /graphql/stream / /graphql/wsTransport paths
transportinheritedsse or ws override
initialMessage-Message sent automatically when the conversation opens
themedashboard themeChatThemeOverride(...) local theme, ranked below the dashboard theme and above the built-in one. See Theming
allowRemoteAttachmentUrlsfalseOpt-in: lets attach() download and upload http(s) sources

API surfacelink

initialize, openChat, closeChat, sendMessage, retryMessage, attachFile(bytes, name, mimeType, previewUri), attach(AttachmentRequest, previewUri?), removeAttachment (cancels an in-flight upload), clearAttachments, updateDraft, requestHandoff(reason), connectToAgent(reason), sendTypingStart / sendTypingStop, setUser, addListener / removeListener, destroy.

Observe state via StateFlows: lifecycle, connectionState, messages, conversation, assignment, agentTyping, unreadCount, orgConfig, agentsOnline, attachments, flowState, widgetOpen, draftText; or a listener with onMessageReceived, onMessageSent, onTyping, onConnectionChange, onUnreadCountChange, onError, onAttachmentUpdate, onChatClosed. Errors carry type (NETWORK, VALIDATION, AUTH, SYSTEM) and recoverable.

Programmatic attachmentslink

attach(AttachmentRequest(source, name, mimeType?, declaredSize?, metadata)) accepts any source: AttachmentSource.Base64Data, DataUri, FilePath, ContentUri, Bytes, or RemoteUrl (config-gated). AttachmentSource.detect(value) safely classifies strings. Validation runs before anything is queued: base64 must decode, URIs must be readable, files must be non-empty and at most 25 MB, MIME must be png/jpeg/webp/gif/pdf, a wrong declaredSize is rejected as corruption, and the extension must agree with the MIME type. Reads and uploads run off the main thread with real byte progress on the attachments flow and onAttachmentUpdate (statuses QUEUED, UPLOADING, UPLOADED, FAILED, CANCELLED). A message is never sent with an attachment that has not finished uploading, and attachment contents are never logged.

The chat close eventlink

onChatClosed fires exactly once per chat presentation, whichever way the screen goes away: the in-chat X (close_button), the system back button or gesture (back_navigation), closeChat() (programmatic - it now also finishes the chat activity), destroy() (session_ended), or host navigation (host_navigation). The event carries reason, initiator (user/host/sdk), timestamp, conversationId, assignmentId, channel, previousState, unreadCount, hasDraft, pendingAttachmentCount and SDK metadata; never message contents, credentials or attachment data.

Platform noteslink

  • The SDK manifest already declares INTERNET, ACCESS_NETWORK_STATE and legacy WRITE_EXTERNAL_STORAGE (maxSdkVersion 28) and merges the chat activity; add READ_MEDIA_IMAGES only if your own picker needs it.
  • Theming comes from the org configuration (the dashboard can force dark or light; otherwise the system theme applies); there is no local theme parameter.
  • All UI strings are Android string resources, so hosts can localise with values-xx overrides.
  • No push notifications: unread is in-app only via unreadCount.
  • destroy() cancels the SDK scope; any later call throws IllegalStateException, so build a new instance to start again.