Kotlin Multiplatform SDK reference
Every public class, function, property, event and error of ai.graine:inapp-kmp 0.8.0 and ai.graine:inapp-kmp-compose 0.8.0, grouped by what you use it for: signatures, parameters, what each returns, the errors it raises, and what to expect.
Every public declaration of the Kotlin Multiplatform SDK — ai.graine:inapp-kmp 0.8.0, one API in shared code over Graine's Android and iOS SDKs, and ai.graine:inapp-kmp-compose 0.8.0, the SDK's own screens for Compose Multiplatform — grouped by what you use it for. Each entry says what it is for, then gives its signature, its parameters, what it returns, the errors it can raise and an example; each type lists its fields with what to expect. The same API across every platform, topic by topic, is the SDK reference; installing and setting up is Kotlin Multiplatform.
Requirements, install, permissions and initialisation
| Artifacts | ai.graine:inapp-kmp:0.10.3 (the shared API) and ai.graine:inapp-kmp-compose:0.10.3 (the chat, the call screen and the voice bar for Compose Multiplatform), on Maven Central |
|---|---|
| Requires | Kotlin 2.1.20 or newer; targets androidTarget, iosArm64, iosSimulatorArm64 and iosX64; Android 7.0 (API 24) and up; iOS 15 and up. Depends on kotlinx-coroutines and kotlinx-datetime only (and Compose, for the compose artifact) |
| How it works | One API in shared code over the native SDKs: the Android SDK (ai.graine:inapp-android 0.8.0) comes with it; on iOS your app links the iOS SDK (GraineInApp 0.8.0) itself, so it is never in the app twice |
| In the dashboard | Add app:// to the agent's Allowed domains (Agent → Embed & Widgets); without it every request is refused |
Gradle (Kotlin DSL) — shared/build.gradle.kts
- Android. The Android SDK's manifest comes with it — the microphone and notification permissions, the call screen, the call service — so there is nothing to copy (what it adds, and how to remove the calls entries from an app without calls, is on the Android reference). The library finds your application by itself, so
Graine.configure(config)works from shared code. Ask forPOST_NOTIFICATIONSon Android 13 and up during onboarding — without it the phone cannot ring. An activity that shows the voice bar or places calls forwards its permission answers toGraine.onRequestPermissionsResult(requestCode, grantResults). For calls Graine rings, add Firebase Cloud Messaging to the Android app (google-services.json, its Gradle plugin andcom.google.firebase:firebase-messaging): the SDK registers for pushes itself. An app with aFirebaseMessagingServiceof its own, which outranks the SDK's, callsGraine.calls.onNewToken(token)andGraine.calls.handlePush(message.data)from it. - iOS. Add the iOS SDK to the app: with Swift Package Manager (
https://github.com/GraineAI/inapp-ios, from 0.10.3) and your shared framework built static (isStatic = true, the Compose Multiplatform default), or with CocoaPods (pod 'GraineInApp'; with Kotlin's CocoaPods plugin and a dynamic framework,pod("GraineInApp") { version = "0.10.3"; linkOnly = true }). AddNSMicrophoneUsageDescriptiontoInfo.plist, and for calls Push Notifications and Background Modes → Audio and Voice over IP. CallGraine.calls.enableVoIPPushes()at every launch. LinkGraineInApp0.10.3, the version of this library: with an older one (0.7.0), booking is not available on iOS —bookingAvailableisfalseandpresentBookingthrowsUnavailable— and everything else works.
Kotlin Multiplatform · Kotlin — commonMain
Every function may be called from any thread; the suspending ones never block the main thread; the states are flows that always hold the latest value.
WebRTC calls (optional)
Native calls carry their audio over a WebSocket — in both directions: the person calling the agent (startCall, your “Call us” button) and the person answering a call Graine rang (a booked call, Ring now). Add each platform's WebRTC module to the app and they carry it over WebRTC instead. Nothing in shared code changes: the same ring, the same call screen, the same “Connecting…”, the same outcomes.
Gradle (Kotlin DSL) — shared/build.gradle.kts, the Android side
- Android.
ai.graine:inapp-android-rtc, at the same version as the Android SDK this library brings (ai.graine:inapp-android0.8.0) — inandroidMainas above, or in the Android app's own dependencies. Its manifest addsACCESS_NETWORK_STATE, a normal permission granted at install: nothing is asked. Its R8 rules ship inside it. Size per phone (an app bundle delivers one ABI): about 7.4 MB installed on 64-bit ARM (about 2.5–3.3 MB to download), 4.3 MB on 32-bit ARM, 8.2–8.4 MB on x86, and about 0.5 MB of classes. - iOS. The
GraineInAppRTCpackage, added to the iOS app target besideGraineInApp, the same way and at the same version: with Swift Package Managerhttps://github.com/GraineAI/inapp-ios-rtc(its libraryGraineInAppRTC), or with CocoaPodspod 'GraineInAppRTC', :git => 'https://github.com/GraineAI/inapp-ios-rtc.git', :tag => '0.10.3'. No code, noInfo.plistentry, no capability: the calls find the module by themselves. A build that strips code nothing references can callGraineRTC.install()at launch from the app's Swift code (import GraineInAppRTC);GraineRTCVersion.nameis the module's version. About 12 MB more installed on an arm64 iPhone, about 6 MB more to download. If another package in the app pins the same WebRTC build at a different version, the two cannot be resolved together: leave the module out (calls stay on the WebSocket) or align the versions. - Choosing.
GraineCallsConfig(callTransport = …), aGraineCallTransport:AUTO, the default — WebRTC when the module is in the app and the dashboard allows it, else the WebSocket;WEBSOCKET— always the WebSocket;WEBRTC— WebRTC wherever it can carry the call (the WebSocket only when the module is missing or WebRTC cannot set up this call). - The dashboard's switch. The agent's In-app call audio setting (agent → Calls & scheduling): WebRTC (recommended) or Standard. Standard moves every install's calls back to the WebSocket, whatever the app chose, from each app's next start — no app release.
- What the person notices. On a weak or changing network the agent's voice holds up instead of stalling and catching up: a jitter buffer, loss concealment and the platform's echo canceller. The call is prepared at the tap, or while a ring is showing, so it connects as fast as before. Over WebRTC the agent's voice arrives at telephone bandwidth today: steadier through a weak network, narrower than the WebSocket's on a perfect one.
- When WebRTC cannot set up a call before any audio flows — a network that blocks it, a server that turns it down, no answer within the time allowed (about 12 seconds in all) — the call continues on the WebSocket inside the same call: no second ring, no new call screen, no error; “Connecting…” shows a moment longer. A call the agent cannot take at all (no credit left, the agent switched off) ends with that reason, exactly as without the module. Once audio flows, a call never moves: a network drop is retried once, then the call ends as dropped.
Graine.calls.statesays which one carried a call —transportandtransportFallbackReason(GraineCallInfo). - Licences. A prebuilt WebRTC library (Google's WebRTC, BSD 3-Clause, its symbols renamed so it never clashes with another copy in the app): list it in your app's open-source notices. Android:
graine-inapp-rtc/THIRD-PARTY-NOTICES.md; iOS: the package'sTHIRD-PARTY-NOTICES.md.
Kotlin Multiplatform · Kotlin — commonMain
Install and the first answer
Configure the SDK once at launch, then read the agent's settings when they arrive. Calling anything on Graine before configure throws IllegalStateException (“Graine.configure(GraineConfig(…)) has not been called. Call it once, at launch.”).
Graine.configure
Call it once at launch — in Application.onCreate on Android, the app's init on iOS — to start the agent.
Without a name it starts the default agent, which everything on Graine belongs to. An app with several agents configures each under its own name and key (Several agents). Asking again for a name already set up gives you that same agent; the configuration passed the second time is ignored. On Android the SDK finds the application by itself; on iOS the agent is the iOS SDK's agent under that name — one the app already configured from Swift is used as it is.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
config | GraineConfig | required | The agent's publishable key, the base URL and the options (GraineConfig). |
name | String | DEFAULT_AGENT | The name to configure the agent under — leave it out for the default agent, which is "default"; blank is the default too. |
Returns — GraineAgent — the agent (GraineAgent).
Errors — IllegalStateException on Android when the application cannot be found (“Graine could not find the application. Keep ai.graine.inapp.kmp.GraineInitProvider in the manifest, or call Graine.configure(context, config) on Android.”), and on iOS when the iOS SDK cannot configure the agent.
Graine.configure (Android, with a context)
Use it on Android only when your app removed GraineInitProvider from its manifest, so the SDK cannot find the application itself.
The same as Graine.configure(config, name), with the application taken from context. In androidMain only.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
context | Context | required | Any Android context of your app — the Application or an Activity. |
config | GraineConfig | required | The agent's key, base URL and options. |
name | String | Graine.DEFAULT_AGENT | The name to configure the agent under; the default agent when left out. |
Returns — GraineAgent.
Errors — none.
Graine
Use it for everything: it is the SDK, and the default agent.
object Graine
Configure it once at launch. Who the user is, this install, the memory choice, the screen and the phone's calls belong to the app and are shared by every agent; everything else on Graine is the default agent's.
What to expect — Every member may be called from any thread; calling anything but configure before it throws IllegalStateException. The flows always hold the latest value; events is not replayed.
| Property | Type | Meaning |
|---|---|---|
const val DEFAULT_AGENT = "default" | String | Use it to name the default agent explicitly — Graine.agent(Graine.DEFAULT_AGENT). |
val agents: Map<String, GraineAgent> | Map<String, GraineAgent> | Read it to list every configured agent, by name, in the order they were configured. |
val isConfigured: Boolean | Boolean | Read it to know whether the default agent has been configured — before calling anything else from code that may run first. |
val session: StateFlow<GraineSession?> | StateFlow<GraineSession?> | Collect it to know when the default agent's settings have loaded (Graine.session); null before. |
val theme: StateFlow<GraineThemeTokens?> | StateFlow<GraineThemeTokens?> | Collect it to draw UI of your own in the brand's design: the default agent's design tokens for the phone's light or dark appearance (Graine.theme); null before the settings load. |
val events: SharedFlow<GraineEvent> | SharedFlow<GraineEvent> | Collect it, from app start, to hear what the default agent reports that is not a state (GraineEvent). Not replayed. |
val chat: GraineChat | GraineChat | Use it to drive the default agent's chat (GraineChat). |
val voice: GraineVoice | GraineVoice | Use it to drive the default agent's voice-bar call (GraineVoice). |
val calls: GraineCalls | GraineCalls | Use it for native calls — the app's, whichever agent a call is with (GraineCalls). |
val schedulingRules: GraineSchedulingRules | GraineSchedulingRules | Read it to know what the default agent lets users book (GraineSchedulingRules); the defaults, with booking off, before the settings load. |
val bookingAvailable: Boolean | Boolean | Read it to decide whether to offer booking for the default agent (Graine.bookingAvailable). |
val installId: String | String | Read it for this install's ID: a random UUID made on first launch and kept for as long as the app is installed; the same one the native SDKs use, for every agent. |
val memoryEnabled: Boolean | Boolean | Read it to show the user's memory choice: their last choice on this install, else true. |
Its functions: configure, agent, identify, setMemory, memories, forgetMemory, setVariables, currentVariables, registerAction, setScreen, updateField, removeField, clearScreen, track, tap, noteInteraction, presentBooking, scheduleCall, scheduledCalls, cancelScheduledCall, onRequestPermissionsResult — each with its parameters, what it returns, its errors and an example.
Graine.session (the agent's settings)
Collect it to know when the agent's settings have loaded — before offering what depends on them, such as booking.
null until the default agent's settings arrive from Graine (once per launch), then the GraineSession. Each agent has its own: GraineAgent.session. The same moment is also an event, GraineEvent.SessionReady.
Parameters — none.
Returns — StateFlow<GraineSession?>. Example value: GraineSession(agentId="a7c1…", memoryEnabled=true, identityField="user_id", devicesPerUser=2, schedulingRules=GraineSchedulingRules(usersCanBook=true, …), declaredActions=[open_screen]).
Errors — none.
GraineConfig
Pass it to Graine.configure to say which agent to start and how.
data class GraineConfig(…)
Everything the SDK needs to know up front — the native SDKs' configuration, field for field.
What to expect — Made once, at launch. Optional fields have the defaults shown; nothing in it is sent to Graine except what the descriptions say.
| Field | Type | Default | Meaning |
|---|---|---|---|
baseUrl | String | required | Where Graine is: "https://www.graine.ai" unless your workspace is self-hosted. |
publishableKey | String | required | Selects the agent: its publishable key from Agent → Embed & Widgets. Not a secret, and it cannot place a call on its own. |
variables | Map<String, Any?> | emptyMap() | Prompt variables your app knows at build time. Values from identify() and setVariables() take precedence over these. |
appVersion | String? | null | Use it to tell builds apart: it lands on the call record. |
endOnBackground | Boolean | true | Ends a live voice-bar conversation when the app goes to the background. Keep it on. |
debug | Boolean | false | Turn it on in debug builds to log the SDK's traffic with Graine — keys only, never values — to logcat (tag Graine) and the iOS console. |
captureTaps | Boolean | true | Lets the SDK notice taps on labelled controls (what a screen reader reads, a view's ID, a button's words) by itself, at most two a second and only while a screen is reported, so the agent knows what the user just pressed. It only watches. |
transport | GraineTransport? | null | Use it to force how the voice bar's audio travels (GraineTransport); null lets the dashboard's Call audio setting decide. |
chatSize | GraineChatSize? | null | How big the chat window opens in this app, over the dashboard's Window size (GraineChatSize); a size passed to chat.open(size) wins for that open. |
GraineSession
Read it to know what the agent's settings say once they have loaded — who the user is, the booking rules, the declared actions.
data class GraineSession(…)
From Graine.session (and GraineAgent.session, GraineEvent.SessionReady).
What to expect — Loaded once per launch; null in session until then. A setting an older server does not send means its default: the user_id trait, 2 devices per user, booking off.
| Field | Type | Default | Meaning |
|---|---|---|---|
agentId | String | required | Use it in logs and support requests: the agent's ID in Graine. |
memoryEnabled | Boolean? | required | Read it to know whether the agent remembers its users; null when Graine does not say. |
identityField | String | "user_id" | Read it to know which trait names the user (Agent → Memory → Who is the user?): the one identify must carry for memory and booking to work. |
devicesPerUser | Int | 2 | How many devices one user may use the agent on (1–5); beyond it, device_limit. |
schedulingRules | GraineSchedulingRules | GraineSchedulingRules() | Read it for the booking rules and how Graine rings (GraineSchedulingRules). |
declaredActions | Set<String> | emptySet() | Use it to check your handlers against the actions declared on the agent: one declared and not registered is refused when the agent asks for it. |
GraineInitProvider
Leave it in your merged manifest on Android: it is how Graine.configure(config) finds the application from shared code.
class GraineInitProvider : ContentProvider()
Declared by the library and started by Android at process start, before Application.onCreate; it records the application and, from then on, the activity in front. It serves no data. An app that removes it calls Graine.configure(context, config) instead.
What to expect — Nothing to call. Android only.
Identity, memory and conversations that continue
Tell the agent who the user is, so it can remember them, ring them, and continue their conversation. Where the agent remembers its users, a signed-in user who comes back finds the chat where they left it: it opens with the earlier messages (restored, oldest first; an earlier call's as one “Voice call” row) and the agent continues from them. chat.newConversation() starts afresh. Who the user is belongs to the app: Graine.identify tells every agent.
Graine.identify
Call it after the user signs in, and with null when they sign out.
The trait the agent's Who is the user? setting names (user_id unless the dashboard chose email, phone or a custom trait) is how Graine tells your users apart: it lets the agent remember a user and ring them. Its value goes only to Graine and never becomes a prompt variable; every other trait is masked on the device and reaches the agent as a prompt variable. A placeholder ("guest", a masked value) identifies nobody.
It applies to the next conversation, for every agent. null signs out everywhere: this device stops being one of the user's, the chat on screen is cleared, and later conversations are anonymous. Identifying a different user does the same for the previous one.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
traits | Map<String, Any?>? | required | Who the user is, by trait name — mapOf("user_id" to user.id, "name" to user.firstName). null signs out. |
memory | Boolean? | null | false keeps this user's conversations out of memory — nothing is remembered or recalled — and the choice is saved on this install for that user. null keeps their last choice — on until they make one. |
Returns — nothing.
Errors — IllegalStateException before configure.
Graine.setMemory
Call it when the user changes their memory choice in your privacy settings.
The same as identify with the same traits and memory = enabled, for every agent. Kept on this install for this user.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
enabled | Boolean | required | true to remember this user's conversations, false to stop. |
Returns — nothing.
Errors — IllegalStateException before configure.
Graine.memories
Call it to show the user what the agent remembers about them, on a privacy screen.
What the default agent remembers about the signed-in user, newest first, at most 100, already redacted. Only from a device the user is using. GraineAgent.memories() asks another agent.
Parameters — none.
Returns — List<GraineMemory> (GraineMemory). Example value: [GraineMemory(id="m_31", text="Prefers a call after 6 PM", createdAt=2026-10-12T09:15:00Z, updatedAt=null)].
Errors — throws GraineException: IdentityRequired with nobody signed in, DeviceLimit from a device that is not one of the user's, RateLimited, Network, Unavailable, InvalidKey, OriginRefused.
Graine.forgetMemory
Call it when the user asks the agent to forget them.
Deletes everything the default agent remembers about the signed-in user — only from a device that user is using. Once it succeeds the chat on screen is cleared too; a refusal leaves it as it was.
Parameters — none.
Returns — Int — how many memories were deleted. Example value: 3.
Errors — throws GraineException: IdentityRequired, DeviceLimit, RateLimited, Network, Unavailable, InvalidKey, OriginRefused.
GraineAgent.memories
Call it to show what one of your other agents remembers about the signed-in user.
Graine.memories() for this agent: newest first, at most 100, already redacted.
Parameters — none.
Returns — List<GraineMemory>.
Errors — throws GraineException as Graine.memories(); IllegalStateException when this agent is not configured.
GraineAgent.forgetMemory
Call it to make one of your other agents forget the signed-in user.
Graine.forgetMemory() for this agent.
Parameters — none.
Returns — Int — how many memories were deleted.
Errors — throws GraineException as Graine.forgetMemory().
GraineChat.newConversation
Call it to give the user a fresh conversation instead of continuing the last one.
The chat hangs up, Graine lets go of the conversation it kept for this agent and user (so the next chat does not continue it), and the messages on screen and any waiting to go are cleared; when the user was in the chat, a fresh chat opens with the welcome and the starters. Memories the agent keeps about the user stay as they are. With nobody signed in there is nothing kept to let go of, so only the screen is cleared. The SDK's chat window calls it from the New conversation button in its header, which it shows only while a conversation continued from before is on screen.
Parameters — none.
Returns — nothing, once the new conversation has started.
Errors — throws GraineException when Graine refused (DeviceLimit, Network, …); the chat is then as it was.
GraineMemory
Read it to show one thing the agent remembers about the user.
data class GraineMemory(val id: String, val text: String, val createdAt: Instant? = null, val updatedAt: Instant? = null)
What to expect — From memories(): newest first, at most 100, already redacted. The times are moments; show them in the device's time zone.
| Field | Type | Default | Meaning |
|---|---|---|---|
id | String | required | Use it as a stable key when you list memories. |
text | String | required | Show it: what is remembered, in a sentence, already redacted. |
createdAt | Instant? | null | When it was first remembered; null when Graine did not say. |
updatedAt | Instant? | null | When it last changed; null when it never did or Graine did not say. |
Chat
The typed conversation with an agent: Graine.chat for the default agent, Graine.agent("support")?.chat for another. Show the SDK's own chat screen — GraineChat() in Compose, GraineViews.chat() in UIKit — or draw your own from turns, status, typing and upcomingCalls. Everything works as on a call: the agent sees the screen you report, runs your actions and draws its components. Only one conversation runs at a time — opening the chat ends a voice-bar call, and during a native call the chat waits until the call is over.
GraineChat
Use it to open, send to and follow one agent's chat.
class GraineChat
Graine.chat, or GraineAgent.chat for another agent. A message typed while the chat is not connected is shown at once and sent when it is; a dropped connection is made again on its own while the user is in the chat.
What to expect — The four flows always hold the latest value. turns empties on sign-out, when another user signs in, after a successful forgetMemory(), on newConversation() and on clear().
| Property | Type | Meaning |
|---|---|---|
val turns: StateFlow<List<GraineChatTurn>> | StateFlow<List<GraineChatTurn>> | Collect it to draw the conversation, oldest first (GraineChatTurn). |
val status: StateFlow<GraineChatStatus> | StateFlow<GraineChatStatus> | Collect it to show the connection — Online, Connecting… (GraineChatStatus). |
val typing: StateFlow<Boolean> | StateFlow<Boolean> | Collect it to draw a typing indicator: true from the user's message until the agent's first words. |
val upcomingCalls: StateFlow<List<GraineScheduledCall>> | StateFlow<List<GraineScheduledCall>> | Collect it to draw the SDK window's Upcoming calls card in a chat of your own: the user's calls still to come, soonest first, at most three (GraineScheduledCall). Fetched when the chat opens and after the agent finishes a reply (it may have just booked one), only where Graine rings this app; empty otherwise. On iOS it needs GraineInApp of this version; with an older one it stays empty. |
Its functions: open, close, send, retry, clear, newConversation, refreshUpcomingCalls, cancelUpcomingCall, answerWidget — each with its parameters, what it returns, its errors and an example.
GraineChat.open
Call it when the user goes into the chat, so it connects and stays connected.
Ends a call the voice bar holds. A voice bar on screen moves to its chat and draws it at size — this open's own, until close(); null follows GraineConfig.chatSize, then the dashboard's Window size.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
size | GraineChatSize? | null | How big the chat opens this time: COMPACT, LARGE or FULL. |
Returns — nothing.
Errors — IllegalStateException when the agent is not configured.
GraineChat.close
Call it when the user leaves the chat.
The chat hangs up and stays down; what was said stays.
Parameters — none.
Returns — nothing.
Errors — IllegalStateException when the agent is not configured.
GraineChat.send
Call it to send the user's message from a chat screen of your own.
Shown at once, and sent now or as soon as the chat is connected. Blank messages are ignored.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
text | String | required | What the user typed. |
Returns — nothing.
Errors — IllegalStateException when the agent is not configured.
GraineChat.retry
Call it from a “Try again” control when the chat is offline or a message failed.
Connects now and sends what could not be sent.
Parameters — none.
Returns — nothing.
Errors — IllegalStateException when the agent is not configured.
GraineChat.clear
Call it to empty the chat on screen — a new topic, a tidy screen.
Only the screen: the conversation goes on — connected stays connected — a message still waiting for the connection is still sent, and nothing Graine keeps is touched: the conversation that continues comes back with the next history, and what the agent remembers stays. newConversation() starts over; forgetMemory() forgets. On iOS it needs GraineInApp of this version; an older one ignores it.
Parameters — none.
Returns — nothing; turns is empty.
Errors — IllegalStateException when the agent is not configured.
GraineChat.refreshUpcomingCalls
Call it after the user booked a call outside the chat, so the Upcoming calls card of your own chat shows it.
Fetches upcomingCalls again. The chat refreshes them by itself when it opens and after each of the agent's replies, so most apps never call it. On iOS it needs GraineInApp of this version; an older one does nothing.
Parameters — none.
Returns — nothing; upcomingCalls changes when Graine answers.
Errors — IllegalStateException when the agent is not configured.
GraineChat.cancelUpcomingCall
Call it from the Cancel control on a row of your own Upcoming calls card.
The call leaves upcomingCalls once Graine has called it off; if Graine refuses, it stays in the list. A blank id is ignored. To learn the outcome yourself, call Graine.cancelScheduledCall(id) instead (Graine.cancelScheduledCall). On iOS it needs GraineInApp of this version; an older one does nothing.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
id | String | required | The call's id, from upcomingCalls. |
Returns — nothing.
Errors — IllegalStateException when the agent is not configured.
GraineChatTurn
Read it to draw one message of the chat.
data class GraineChatTurn(…)
What to expect — One per message, oldest first. The agent's message updates in place (same id) while live, then stops. A user's message typed while the chat is not connected is SENDING and goes as soon as it is; FAILED means it could not reconnect — retry() tries again. Restored turns arrive once, when the chat opens, for a signed-in user on an agent with memory on: at most 50, oldest first, delivered, never live, with their original times; the welcome is not sent then.
| Field | Type | Default | Meaning |
|---|---|---|---|
id | String | required | Use it as a stable key: it stays the same while the message grows. |
role | GraineRole | required | Who said it: USER or AGENT (GraineRole). |
text | String | required | What was said; an agent's message may be only a component. |
at | Instant | required | When the message began; show it in the device's time zone. |
delivery | GraineDelivery? | null | Where a user's message is (GraineDelivery); null for the agent's. |
proactive | Boolean | false | Use it to badge a closed chat: the agent spoke up without being asked. |
component | GraineComponent? | null | The component the agent drew with this message, when the platform ties one to it (GraineComponent). |
live | Boolean | false | Draw the words as provisional while true: the agent's message is still arriving. |
restored | Boolean | false | true for a message from an earlier conversation, restored when the chat opened: draw it as history, not as said just now. |
channel | GraineChatChannel | GraineChatChannel.CHAT | Where it was said (GraineChatChannel): typed, or on a call — only a restored message can be from a call. |
GraineDelivery
Read it to show where one of the user's messages is.
enum class GraineDelivery { SENDING, SENT, FAILED }
What to expect — Only on the user's messages; the agent's are null.
| Case | Meaning |
|---|---|
SENDING | Waiting for the connection: typed while the chat was not connected, sent as soon as it is. Draw it as pending. |
SENT | On its way to the agent. Draw it as sent. |
FAILED | The chat could not reconnect to send it. Offer a retry: GraineChat.retry(). |
GraineChatChannel
Read it to tell a typed message from one said on a call.
enum class GraineChatChannel(val wire: String)
What to expect — A chat window draws a run of VOICE messages as one “Voice call” row, never their words.
| Property | Type | Meaning |
|---|---|---|
wire | String | The word Graine uses: chat or voice — for logs. |
| Case | Meaning |
|---|---|
CHAT | Typed in the chat. |
VOICE | Said on a call with the agent — only a restored message is. |
GraineChatStatus
Read it to show the chat's connection.
enum class GraineChatStatus { IDLE, CONNECTING, ONLINE, RECONNECTING, OFFLINE }
What to expect — IDLE → CONNECTING → ONLINE; a drop — or a first try that failed — goes to RECONNECTING and back; OFFLINE when it could not reconnect or the conversation ended.
| Case | Meaning |
|---|---|
IDLE | The chat is not in use — never opened, left by the user, or the app is in the background. Show nothing. |
CONNECTING | Being connected. Show “Connecting…”. |
ONLINE | Connected: messages go out as they are typed. Show “Online”. |
RECONNECTING | The chat is being connected again — after a drop, or after a first try that failed. Messages wait. |
OFFLINE | Reconnecting failed, or the conversation was ended from the agent's side. A message, or retry(), connects again. |
GraineChatSize
Pass it to choose how big the chat window opens.
enum class GraineChatSize(val wire: String)
What to expect — Which size wins: the size passed to chat.open(size), then GraineConfig.chatSize, then the agent's Window size in the dashboard, else compact. On a phone, large is a sheet over most of the screen and full the whole screen; on a tablet both open a panel in the corner.
| Property | Type | Meaning |
|---|---|---|
wire | String | The design's word for it — compact, large or full — for logs. |
| Case | Meaning |
|---|---|
COMPACT | The window above the voice bar. |
LARGE | On a phone, a sheet covering most of the screen; on a tablet, a panel in the corner. |
FULL | The whole screen. |
GraineRole
Read it to know who said a line.
enum class GraineRole { USER, AGENT }
| Case | Meaning |
|---|---|
USER | The person using your app. |
AGENT | The agent. |
GraineChat (Compose)
Use it to show the SDK's own chat screen in a Compose Multiplatform UI.
In ai.graine:inapp-kmp-compose. The chat as a whole screen, drawn in the agent's design: the header, one bubble per reply, the components the agent draws, the composer — and, where Graine rings this phone, the user's booked calls with Book a call (Booking with the SDK's sheet). It connects when it appears and hangs up shortly after it leaves the screen. A conversation that continues opens on its earlier messages — under day separators, an earlier call as one “Voice call” row, no starters — with New conversation in the header. On Android it hosts the Android SDK's chat view; on iOS the iOS SDK's chat screen, inside Compose's accessibility tree so VoiceOver reaches it. Nothing is drawn before Graine.configure, or for an agent that is not configured.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
modifier | Modifier | Modifier | Where and how big the chat is drawn. |
onClose | (() -> Unit)? | null | Called when the user taps the header's close control. null: on iOS no close control is drawn and your navigation closes it. |
agent | String | Graine.DEFAULT_AGENT | Which configured agent's chat to show. |
Returns — nothing (a composable).
Errors — none.
GraineViews
Use it on iOS to present the SDK's own screens as UIKit view controllers.
object GraineViews
In iosMain. For an app that presents UIKit itself; Compose Multiplatform apps use GraineChat(), GraineCallScreen() and GraineAgentBar(), which host these.
What to expect — Each function throws IllegalStateException before Graine.configure (or for an agent that is not configured).
Its functions: chat, voiceBar, callScreen — each with its parameters, what it returns, its errors and an example.
GraineViews.chat
Call it on iOS to get an agent's chat as a view controller to present.
The iOS SDK's chat screen, drawn in the agent's design, with the user's booked calls and Book a call where Graine rings this phone.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
onClose | (() -> Unit)? | null | Called when the user taps the header's close control; null draws none — your navigation closes it. |
agent | String | Graine.DEFAULT_AGENT | Which configured agent's chat. |
Returns — UIViewController.
Errors — IllegalStateException when the agent is not configured.
GraineChatRows
Use it to lay out a chat screen of your own the way the SDK's chat window does.
object GraineChatRows
The rule every Graine SDK draws its own chat window by: a bubble per typed message; one “Voice call · 6:05 PM” row for a run of voice messages on one day; and a day separator — Today, Yesterday, a date — before the first row when it is not from today, and wherever the device's day changes between rows. A conversation all from today draws exactly as before.
Its functions: build, dayLabel, callLabel, offersStarters, offersNewConversation — each with its parameters, what it returns, its errors and an example.
GraineChatRows.build
Call it to turn the chat's messages into the rows to draw, in order.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
turns | List<GraineChatTurn> | required | The messages, oldest first — Graine.chat.turns.value. |
now | Instant | Clock.System.now() | The moment “today” is counted from; now when left out. |
zone | TimeZone | TimeZone.currentSystemDefault() | The time zone whose days separate the rows; the device's when left out. |
Returns — List<GraineChatRow> (GraineChatRow). Example value: [Day(TODAY, 2026-10-15, …), Turn(turn, 0), VoiceCall("voice-t2", …, 1, 3)].
Errors — none.
GraineChatRows.dayLabel
Call it to write a day separator's words.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
row | GraineChatRow.Day | required | The separator to label. |
now | Instant | Clock.System.now() | The moment “today” is counted from. |
zone | TimeZone | TimeZone.currentSystemDefault() | The time zone of the days. |
Returns — String — “Today”, “Yesterday”, else the date, “Sat 10 Oct”, with the year when it is not this one (“Wed 31 Dec 2025”). Example value: "Yesterday".
Errors — none.
GraineChatRows.callLabel
Call it to write an earlier call's row.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
clock | String | required | When the call's first message was said, in the device's short time format — “6:05 PM”. |
Returns — String — “Voice call · 6:05 PM”. Example value: "Voice call · 6:05 PM".
Errors — none.
GraineChatRows.offersStarters
Call it to decide whether to show the brand's conversation starters.
Starters appear only in a fresh chat: they go once the user says something, and a continued conversation never shows them.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
turns | List<GraineChatTurn> | required | The messages, oldest first. |
Returns — Boolean. Example value: true.
Errors — none.
GraineChatRows.offersNewConversation
Call it to decide whether to offer “New conversation” in your chat's header.
Offered while restored messages are on screen — a conversation that continues.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
turns | List<GraineChatTurn> | required | The messages, oldest first. |
Returns — Boolean. Example value: false.
Errors — none.
GraineChatRow
Read it to draw one row of a chat laid out by GraineChatRows.build.
sealed class GraineChatRow
What to expect — Draw Day as a separator, VoiceCall as one row that a call happened (never its words) and Turn as a bubble.
| Property | Type | Meaning |
|---|---|---|
abstract val key: String | String | Use it as the row's stable key while the conversation grows: a separator by its date, a call by its first message, a bubble by its message. |
| Case | Meaning |
|---|---|
Day(day: GraineChatDay, date: LocalDate, at: Instant) | A day separator: which day (GraineChatDay), the device's date, and the first message's time on it. Label it with GraineChatRows.dayLabel. |
VoiceCall(id: String, at: Instant, first: Int, last: Int) | An earlier call as one row: when its first message was said, and which messages (first to last, positions in the conversation) it stands for. Label it with GraineChatRows.callLabel. |
Turn(turn: GraineChatTurn, index: Int) | A message bubble: the message, and its position in the conversation. |
GraineChatDay
Read it to know which day a separator names.
enum class GraineChatDay { TODAY, YESTERDAY, DATE }
| Case | Meaning |
|---|---|
TODAY | Today, in the device's time zone. |
YESTERDAY | Yesterday. |
DATE | Any earlier day: show the date. |
The voice bar
The agent talking inside your app, beside the screen it talks about. Show the bar with GraineAgentBar() (Compose) or GraineViews.voiceBar() (UIKit); it places and ends the call itself, draws captions, the connection's state and the chat beside the call, all in the agent's design. Graine.voice is for an app that draws its own controls. The device asks the user for the microphone when the first call starts.
GraineVoice
Use it to start and end the voice bar's call from controls of your own.
class GraineVoice
Graine.voice, or GraineAgent.voice for another agent.
| Property | Type | Meaning |
|---|---|---|
val state: StateFlow<GraineVoiceInfo> | StateFlow<GraineVoiceInfo> | Collect it to draw the call's state, what has been said and the captions switch (GraineVoiceInfo). |
Its functions: start, end, setMuted, setCaptionsOn, clearCaptions — each with its parameters, what it returns, its errors and an example.
GraineVoice.start
Call it from your own “Talk” control to place the voice bar's call.
Mounts the voice engine first if nothing has (on Android, in the activity in front). One conversation at a time: it ends the chat, and it cannot start while a native call is live.
Parameters — none.
Returns — nothing.
Errors — IllegalStateException when the agent is not configured. A refused microphone is the event GraineEvent.MicrophoneDenied.
GraineVoice.end
Call it from your own hang-up control.
Parameters — none.
Returns — nothing.
Errors — IllegalStateException when the agent is not configured.
GraineVoice.setMuted
Call it from your own mute control.
Stops sending the user's voice without ending the call.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
muted | Boolean | required | true to stop sending the user's voice, false to send it again. |
Returns — nothing.
Errors — IllegalStateException when the agent is not configured.
GraineVoice.setCaptionsOn
Call it from your own captions (CC) control.
Moves the captions switch, GraineVoiceInfo.captionsOn. While it is off the SDK's voice bar hides the line being said; the call goes on, and what was said is still kept in captions. On by default. On iOS it needs GraineInApp of this version; with an older one the captions stay on.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
on | Boolean | required | true to show the captions, false to hide them. |
Returns — nothing.
Errors — IllegalStateException when the agent is not configured.
GraineVoice.clearCaptions
Call it to forget what has been said on the call — a fresh transcript for your own captions view.
Empties GraineVoiceInfo.captions without ending the call; the next line starts the list again. On iOS it needs GraineInApp of this version; an older one does nothing.
Parameters — none.
Returns — nothing.
Errors — IllegalStateException when the agent is not configured.
GraineVoiceInfo
Read it to draw the voice bar's call in controls of your own.
data class GraineVoiceInfo(val connected: Boolean = false, val connecting: Boolean = false, val muted: Boolean = false, val captionsOn: Boolean = true, val captions: List<GraineCaption> = emptyList())
| Field | Type | Default | Meaning |
|---|---|---|---|
connected | Boolean | false | Show the call as live: the agent and the user can hear each other. |
connecting | Boolean | false | Show “Connecting…”: the call is being placed. |
muted | Boolean | false | Show the mute control as on: nothing the user says is sent. |
captionsOn | Boolean | true | Show your captions (CC) control as on: the SDK's bar shows the line being said only while it is. On by default — a caption nobody asked to hide is an accessibility default. setCaptionsOn moves it. |
captions | List<GraineCaption> | emptyList() | What has been said this call, oldest first: the last 12 lines, a live one replaced as its words arrive (GraineCaption). A new call starts empty. Kept whether the captions are on or off; clearCaptions() forgets them. |
GraineCaption
Read it to show the line being said on a call.
data class GraineCaption(val role: GraineRole, val text: String, val live: Boolean)
The last line of a native call (GraineCallInfo.caption). The voice bar's lines arrive as the event GraineEvent.Caption and are kept, the last 12, in GraineVoiceInfo.captions; the captions switch (captionsOn) hides the bar's caption, never the list.
| Field | Type | Default | Meaning |
|---|---|---|---|
role | GraineRole | required | Who is speaking: the agent's words as they are spoken, the user's as they are heard. |
text | String | required | The words so far. |
live | Boolean | required | Draw it as provisional while true: the line is still arriving. |
GraineTransport
Pass it in GraineConfig.transport only to force how the voice bar's audio travels.
enum class GraineTransport(val wire: String)
What to expect — Leave transport null and the dashboard's Call audio setting decides.
| Property | Type | Meaning |
|---|---|---|
wire | String | The setting's word: ws or rtc. |
| Case | Meaning |
|---|---|
WEBSOCKET | The standard path: the audio on the conversation's own connection. Works everywhere. |
WEBRTC | High-quality audio, with a jitter buffer and the device's echo canceller; falls back to WEBSOCKET. |
GraineAgentBar
Use it to show the voice bar in a Compose Multiplatform UI.
In ai.graine:inapp-kmp-compose. It takes up the whole space you give it and draws the bar along the bottom edge, so place it in a strip at the bottom of your layout. It mounts the voice engine itself; on iOS VoiceOver reaches into it. The chat opens at the size passed to Graine.chat.open(size); failing that, at GraineConfig.chatSize; failing that, at the dashboard's setting — large and full open over the app, as a sheet or the whole screen. Nothing is drawn before Graine.configure.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
modifier | Modifier | Modifier | The area the bar is drawn in, at its bottom. |
name | String? | null | The name beside the status; null shows the dashboard's heading. |
onClose | (() -> Unit)? | null | Called when the user hung up, or closed the bar with no call — hide the bar there. |
agent | String | Graine.DEFAULT_AGENT | Which configured agent the bar talks to. |
Returns — nothing (a composable).
Errors — none.
GraineViews.voiceBar
Call it on iOS to get an agent's voice bar as a view controller.
The bar is drawn at the bottom of the view it fills.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
name | String? | null | The name beside the status; null shows the dashboard's heading. |
onClose | (() -> Unit)? | null | Called when the user hung up, or closed the bar with no call. |
agent | String | Graine.DEFAULT_AGENT | Which configured agent the bar talks to. |
Returns — UIViewController.
Errors — IllegalStateException when the agent is not configured.
Graine.onRequestPermissionsResult
Call it from an Android activity's onRequestPermissionsResult when that activity shows the voice bar or places calls.
The SDK asks for the microphone from the activity in front; forwarding the answer lets the call go ahead once it is granted. In androidMain only.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
requestCode | Int | required | As Android passes it to your activity. |
grantResults | IntArray | required | As Android passes them to your activity. |
Returns — Boolean — true when the request was the SDK's (and handled). Example value: true.
Errors — none.
Several agents in one app
A support agent in Help, another agent elsewhere — each with its own publishable key, configured under its own name. Each agent has its own settings, chat, voice bar, actions, and scheduling and memory requests; who the user is, the install ID, the memory choice, the screen and the phone's calls are the app's and shared. Across all agents, only one conversation runs at a time. Graine itself is the default agent; the views take an agent name (GraineChat(agent = "support"), GraineAgentBar(agent = …), GraineViews.chat(agent = …)), and Graine.calls.startCall(agent = …) calls a named one.
Graine.agent (by name)
Call it to reach an agent you configured under a name.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
name | String | DEFAULT_AGENT | The name it was configured under; the default agent when left out. |
Returns — GraineAgent? — the agent, or null when nothing is configured under that name. Example value: GraineAgent(name="support").
Errors — none.
GraineAgent
Use it to work with one particular agent — its chat, voice bar, actions, bookings and memory.
class GraineAgent
Returned by Graine.configure and Graine.agent. Graine is the default agent's; this is any agent's.
What to expect — Calling a function before this agent is configured throws IllegalStateException (“The agent "support" is not configured: call Graine.configure(config, name = "support") first.”).
| Property | Type | Meaning |
|---|---|---|
val name: String | String | Read it to know which agent this is: the name it was configured under, "default" for the default agent. |
val session: StateFlow<GraineSession?> | StateFlow<GraineSession?> | Collect it to know when this agent's settings have loaded; null before. |
val theme: StateFlow<GraineThemeTokens?> | StateFlow<GraineThemeTokens?> | Collect it to draw UI of your own in this agent's design (GraineThemeTokens), for the phone's light or dark appearance — or the brand's fixed scheme — changing when the phone switches. null before its settings load, and when they carry no design. On iOS it needs GraineInApp of this version. |
val events: SharedFlow<GraineEvent> | SharedFlow<GraineEvent> | Collect it to hear what this agent reports (GraineEvent). Not replayed. |
val chat: GraineChat | GraineChat | Use it to drive this agent's chat. |
val voice: GraineVoice | GraineVoice | Use it to drive this agent's voice-bar call. |
val schedulingRules: GraineSchedulingRules | GraineSchedulingRules | Read it to know what this agent lets users book; the defaults (no booking) before its settings load. |
val isConfigured: Boolean | Boolean | Read it to know whether this agent has been configured. |
val bookingAvailable: Boolean | Boolean | Read it to decide whether to offer booking with this agent (GraineAgent.bookingAvailable). |
Its functions: setVariables, currentVariables, registerAction, memories, forgetMemory, presentBooking, scheduleCall, scheduledCalls, cancelScheduledCall, startCall — each with its parameters, what it returns, its errors and an example.
Screen context, actions and components
Tell the agent what the user is looking at, and let it act in your app through handlers you register. The screen is the app's: every agent hears it while it has a live conversation. Values are masked on the device before anything leaves it; report real values. The agent's components (product lists, choices, time slots) are drawn by the SDK's views; an app that draws its own hears them as GraineEvent.Component.
Graine.setScreen
Call it on every meaningful change of what the user can see, so the agent talks about the right thing.
An unchanged screen is not sent again; the first change goes at once and later ones at most every 400 ms. After a pause, the agent names a field that is INVALID or EMPTY instead of asking “need a hand?”. null clears it.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
screen | GraineScreen? | required | What the user can see now (GraineScreen), or null. |
Returns — nothing.
Errors — IllegalStateException before configure.
Graine.updateField
Call it when one field changes, so a screen described once need not be described again on every keystroke.
Merged by name into the current screen (added when it is new) and sent like any screen change. Nothing happens before a screen is set.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
field | GraineField | required | The field as it is now (GraineField). |
Returns — nothing.
Errors — IllegalStateException before configure.
Graine.removeField
Call it when a field leaves the screen.
Nothing happens when it was never reported.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
name | String | required | The field's name. |
Returns — nothing.
Errors — IllegalStateException before configure.
Graine.clearScreen
Call it when the user leaves a screen — in the screen's teardown.
With screenId, only if that screen is still the one described — a screen closing late must not erase the one that replaced it. null clears whatever is there.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
screenId | String? | null | The screen's screen ID, to clear only that one. |
Returns — nothing.
Errors — IllegalStateException before configure.
Graine.track
Call it to tell the agent about something the user did that informs without interrupting — a coupon applied, a filter changed.
It rides along with the next screen update, among the user's recent events (the last ten are kept). Blank names are ignored. Masked on the device.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
name | String | required | The event's name, such as "coupon_applied". |
data | Map<String, Any?> | emptyMap() | Details of the event, by name. |
Returns — nothing.
Errors — IllegalStateException before configure.
Graine.tap
Call it to tell the agent the user tapped a control the SDK cannot name by itself — a custom-drawn button, say.
Reported as tap:<name> among the screen's recent events, at most twice a second, and only while a screen is set. Labelled controls are noticed by themselves (GraineConfig.captureTaps).
| Parameter | Type | Default | Meaning |
|---|---|---|---|
name | String | required | The control's name as the agent should read it; at most 40 characters are kept. |
Returns — nothing.
Errors — IllegalStateException before configure.
Graine.noteInteraction
Call it when the user does something that is not a field edit — scrolls a list, opens a picker — so the SDK does not think they are stuck.
Restarts the clock that notices a user stuck on a step. Field edits do not count: someone typing one character every forty seconds is exactly who is stuck.
Parameters — none.
Returns — nothing.
Errors — IllegalStateException before configure.
Graine.setVariables
Call it when your app learns something the default agent's prompt uses — a plan, a cart total.
Merged over what is there: null under a key removes it, null for the whole map clears them all. Masked on the device; reaches the next conversation. GraineAgent.setVariables sets another agent's.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
values | Map<String, Any?>? | required | Prompt variables by name; numbers and booleans are written out as text, nested maps and lists are dropped. |
Returns — nothing.
Errors — IllegalStateException before configure.
GraineAgent.setVariables
Call it to set prompt variables for one of your other agents.
Graine.setVariables for this agent.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
values | Map<String, Any?>? | required | Prompt variables by name; null under a key removes it, null clears all. |
Returns — nothing.
Errors — IllegalStateException when this agent is not configured.
Graine.currentVariables
Call it to see exactly which prompt variables the default agent's next conversation will carry — on a debug screen, or before a support request.
Every variable the agent will receive, merged in precedence order: the dashboard's defaults, GraineConfig.variables, setVariables, then who is signed in (identify's traits, the identity trait itself withheld). What the next conversation carries, as text; values are masked as they leave the device. A copy: changing it changes nothing. GraineAgent.currentVariables() reads another agent's. On iOS it needs GraineInApp of this version; an older one answers an empty map.
Parameters — none.
Returns — Map<String, String>. Example value: {plan=gold, cart_total=42, name=Asha}.
Errors — IllegalStateException before configure.
GraineAgent.currentVariables
Call it to see the prompt variables one of your other agents will receive.
Graine.currentVariables() for this agent: its dashboard defaults, its configuration's variables, its setVariables, then who is signed in.
Parameters — none.
Returns — Map<String, String> — a copy.
Errors — IllegalStateException when this agent is not configured.
Graine.registerAction
Call it to let the default agent do one thing in your app — open a screen, apply a filter, change a setting.
Declare the action on the agent in the dashboard too: a handler with no declaration is never offered, and a declared action with no handler is refused when the agent asks for it. The handler starts on the main thread (it will usually touch your UI) and may suspend — a network call, a confirmation the user taps — continuing on the main thread. It must answer within six seconds, or the agent is told it was not done. A handler that throws answers error with its message; one cancelled answers error with “The action was cancelled.”. Defining an action again under the same name replaces its handler. GraineAgent.registerAction registers with another agent.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
name | String | required | The action's name, exactly as it is declared on the agent. |
handler | suspend (args: Map<String, Any?>) -> GraineActionResult | required | Runs the action with the agent's arguments and says how it went (GraineActionResult). Tell the truth: the agent speaks about exactly this. |
Returns — () -> Unit — call it to unregister this handler.
Errors — IllegalStateException before configure.
GraineAgent.registerAction
Call it to give one of your other agents an action.
Graine.registerAction for this agent: the same six seconds, the same rules.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
name | String | required | The action's name, as declared on this agent. |
handler | suspend (args: Map<String, Any?>) -> GraineActionResult | required | Runs the action and says how it went. |
Returns — () -> Unit — call it to unregister.
Errors — IllegalStateException when this agent is not configured.
GraineScreen
Pass it to Graine.setScreen to describe what the user can see.
data class GraineScreen(…)
What to expect — Values are masked on the device before they leave it — report real ones; a screen that pre-redacts only blinds the agent.
| Field | Type | Default | Meaning |
|---|---|---|---|
screen | String | required | A stable ID for the screen, such as "checkout_address"; keep it constant, since friction rules and clearScreen match on it. |
title | String? | null | The screen's title as the user sees it — what the agent calls the screen. |
journey | Journey? | null | Where the screen sits in a flow (GraineScreen.Journey), so the agent can say “step 2 of 3”. |
fields | List<GraineField> | emptyList() | The inputs on the screen (GraineField): what lets the agent name the field that is wrong. |
error | String? | null | A screen-level error banner, as shown — distinct from a field's own. |
data | Map<String, Any?> | emptyMap() | Anything else the agent should know about the screen: strings, numbers, booleans, and lists and maps of them. |
availableActions | List<String>? | null | Which registered actions work on this screen: null allows every action this build registered, an empty list allows none. |
GraineScreen.Journey
Pass it in GraineScreen.journey to say where a screen sits in a flow.
data class Journey(val name: String, val step: Int? = null, val of: Int? = null, val next: String? = null)
| Field | Type | Default | Meaning |
|---|---|---|---|
name | String | required | The flow's name, such as "Checkout". |
step | Int? | null | Which step this screen is, counting from 1. |
of | Int? | null | How many steps the flow has. |
next | String? | null | What comes next, such as "Payment" — so the agent can say what is left. |
GraineField
Pass it in GraineScreen.fields (or to updateField) to describe one input.
data class GraineField(…)
What to expect — Its status is what lets the agent name the field that is wrong.
| Field | Type | Default | Meaning |
|---|---|---|---|
name | String | required | A stable ID for the field, by which updateField and removeField find it. |
label | String? | null | The label the user sees — what the agent calls the field. |
value | String? | null | The real value; it is masked on the device on its way out. Never pre-redact it. |
status | GraineFieldStatus? | null | Where the field stands (GraineFieldStatus). |
error | String? | null | The validation message the user sees, so the agent can help them fix it. |
optional | Boolean | false | true when leaving it blank is fine, so the agent does not count it as work left. |
GraineFieldStatus
Use it to say where a field stands.
enum class GraineFieldStatus(val wire: String)
What to expect — INVALID and EMPTY are what make the agent speak up about a field.
| Property | Type | Meaning |
|---|---|---|
wire | String | The word sent to the agent: empty, filled, invalid, pending or locked. |
| Case | Meaning |
|---|---|
EMPTY | Nothing entered yet. |
FILLED | Entered and accepted. |
INVALID | Entered and refused — set error to the message shown. |
PENDING | Being checked (a code being verified, an upload in progress). |
LOCKED | Not the user's to change — the agent does not ask them to. |
GraineActionResult
Return it from an action handler to tell the agent what happened.
data class GraineActionResult(val status: Status, val message: String = "", val data: Map<String, Any?> = emptyMap())
What to expect — The agent speaks about exactly this, so tell the truth: ok only when it really happened.
| Field | Type | Default | Meaning |
|---|---|---|---|
status | Status | required | How it went (GraineActionResult.Status). |
message | String | "" | What the agent is told — not shown to the user; the agent decides how to say it. |
data | Map<String, Any?> | emptyMap() | Structured details the agent can use in its reply. |
Its functions: ok, refused, error — each with its parameters, what it returns, its errors and an example.
GraineActionResult.Status
Read it to know how an action went.
enum class Status(val wire: String) { OK("ok"), REFUSED("refused"), ERROR("error") }
| Property | Type | Meaning |
|---|---|---|
wire | String | The word sent to the agent: ok, refused or error. |
| Case | Meaning |
|---|---|
OK | It happened; the agent tells the user it is done. |
REFUSED | The action will not be done, with the reason — often the most helpful thing to tell the agent. |
ERROR | It was tried and failed. |
GraineActionResult.ok
Return it when the action happened.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
message | String | "" | What the agent is told happened. |
data | Map<String, Any?> | emptyMap() | A structured result for the agent. |
Returns — GraineActionResult with status OK.
Errors — none.
GraineActionResult.refused
Return it when the action will not happen, and say why.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
message | String | required | Why not, for the agent to explain. |
Returns — GraineActionResult with status REFUSED.
Errors — none.
GraineActionResult.error
Return it when the action was tried and failed.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
message | String | required | What went wrong, for the agent. |
Returns — GraineActionResult with status ERROR.
Errors — none.
GraineComponent
Read it to draw a component the agent sent, in an interface of your own.
data class GraineComponent(val id: String, val kind: String?, val title: String?, val payload: Map<String, Any?>)
Carried by GraineChatTurn.component and the event GraineEvent.Component. The SDK's own views draw components themselves; one you draw yourself is answered with Graine.chat.answerWidget (GraineChat.answerWidget).
| Field | Type | Default | Meaning |
|---|---|---|---|
id | String | required | The component's ID: what an answer to it refers to. |
kind | String? | required | What it is — product_list, plan_comparison, choices, quick_actions, calendar_slots, info_card — or a newer kind; draw a fallback for one you do not know. |
title | String? | required | Its title, when it has one. |
payload | Map<String, Any?> | required | The whole component as it arrived — items, choices, slots — to draw from. |
GraineChat.answerWidget
Call it when the user answers a component the agent drew that you draw yourself — a choice tapped, a slot picked.
Sends the answer on the conversation that is live with that agent — the chat, else the voice bar's call. data is the structured answer, for your webhook; summary is the sentence that becomes the user's turn (“The blue one, please”) — without it the conversation does not move. The SDK's own views answer for themselves. On iOS it needs GraineInApp of this version; an older one does nothing.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
id | String | required | The component's id (GraineComponent). |
data | Map<String, Any?> | required | The answer as data — strings, numbers, booleans, and lists and maps of them. |
summary | String | required | The answer in the user's words, said as their turn. |
Returns — nothing.
Errors — IllegalStateException when the agent is not configured.
Calls the user places
Calls like WhatsApp, on the phone's own call screen and over the lock screen, going on with the screen off: the user calls the agent with startCall; the agent can also ring the user (Calls Graine rings). Only one call can be live: a ring that arrives during a call is reported as missed, reason busy.
What a call does on each platform. Both platforms start a call at once on the agent's settings already kept on the device, and refresh them behind the call.
- Android (the Android SDK's engine): opening the call has 15 seconds — “The call could not connect within 15 seconds.” Once connected, nothing at all arriving for 50 seconds (two 25-second keep-alive periods) ends the call: it ends
ENDEDwith reasonfailed, and the SDK's call screen says “Call failed”. Sending never holds a call up on a slow network: at most 300 ms of the user's audio waits, the oldest dropped first. Hanging up lets go of the microphone at once, and a close the network cannot carry is forced within about a second. - iOS (the iOS SDK's engine, under CallKit): everything before the conversation opens — loading the agent, connecting — has one 20-second deadline: “The call could not connect within 20 seconds.” (the connection itself gives up after 15 seconds of trying to open: “The conversation did not open within 15 seconds.”). Once the conversation is open, the call's audio comes from CallKit; if it does not arrive, the SDK waits 3 seconds, starts the audio itself once, and waits 3 seconds more — then the call fails: “The call's audio could not start.”
A call that never connected reaches Graine.calls.outcomes as FAILED with these sentences in reason; the call screen says “Call failed”.
WebRTC. With the platform's WebRTC module in the app (WebRTC calls), calls in both directions carry their audio over WebRTC, as GraineCallsConfig.callTransport and the agent's In-app call audio setting allow. A call WebRTC cannot set up within about 12 seconds continues on the WebSocket inside the same call — “Connecting…” shows a moment longer — and Graine.calls.state says which one carried it (transport, transportFallbackReason, GraineCallInfo).
GraineCalls
Use it for native calls: configure them, place one, control it, and follow its state and outcomes.
class GraineCalls
Graine.calls — the app's one call engine, whichever agent a call is with. Its functions may be called from any thread.
What to expect — state always holds the latest value; outcomes and incoming are not replayed, so collect them from app start.
| Property | Type | Meaning |
|---|---|---|
val state: StateFlow<GraineCallInfo> | StateFlow<GraineCallInfo> | Collect it to draw a call screen of your own, or to know when to show GraineCallScreen() (GraineCallInfo). |
val outcomes: SharedFlow<GraineCallOutcome> | SharedFlow<GraineCallOutcome> | Collect it from app start to send each call's outcome to your server (GraineCallOutcome). |
val incoming: SharedFlow<GraineIncomingCall> | SharedFlow<GraineIncomingCall> | Collect it to know when an agent's call starts ringing on this device (GraineIncomingCall). |
val callPermissions: GraineCallPermissions | GraineCallPermissions | Read it for the call permissions the phone holds now (GraineCalls.callPermissions). |
val callSetupProblems: List<GraineCallSetupProblem> | List<GraineCallSetupProblem> | Read it for what the app's setup still lacks for calls, each with its fix (GraineCalls.callSetupProblems). |
Its functions: configure, startCall, end, setMuted, setSpeaker, answer, decline, callBack, dismissMissedCall, enableVoIPPushes, onNewToken, handlePush, requestCallPermissions, openCallSettings — each with its parameters, what it returns, its errors and an example.
GraineCalls.configure
Call it once at launch to set how calls ring and what they show.
On iOS, call it before enableVoIPPushes(). On Android it also creates the notification channels for rings, ongoing calls and missed calls.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
config | GraineCallsConfig | required | The calls' name, ring time, ringtone and icons (GraineCallsConfig). |
Returns — nothing.
Errors — IllegalStateException before Graine.configure.
GraineCallsConfig
Pass it to Graine.calls.configure to set how calls ring and what they show.
data class GraineCallsConfig(…)
What to expect — Set once at launch. Each field applies on the platforms its description names.
| Field | Type | Default | Meaning |
|---|---|---|---|
displayName | String | "" | The name a call rings with and shows when the ring names nobody; empty uses the agent's call name, else the app's own name. |
ringTimeoutSeconds | Int | 30 | How long a call from your own server rings before it is missed (on Android, held to 5–300 seconds). A call Graine rang rings for the agent's own ring time. |
ringtone | String? | null | The ring: on Android a content URI, on iOS a sound file in the app's bundle; null uses the phone's ringtone. |
smallIcon | Int | 0 | Android: a monochrome drawable resource for the status-bar icon; 0 uses the platform's call icon. |
includeInRecents | Boolean | false | On iOS, show these calls in the Phone app's Recents list. |
callTransport | GraineCallTransport | GraineCallTransport.AUTO | How calls carry their audio, in both directions — calls the user places and calls Graine rings (GraineCallTransport): AUTO, WebRTC when the app includes the platform's WebRTC module, else the WebSocket; WEBSOCKET; or WEBRTC. The agent's In-app call audio setting can still move every call to the WebSocket. |
GraineCallTransport
Pass it in GraineCallsConfig.callTransport to choose how native calls carry their audio; read it back in GraineCallInfo.transport.
enum class GraineCallTransport(val wire: String) { AUTO("auto"), WEBSOCKET("websocket"), WEBRTC("webrtc") }
It applies to calls in both directions: one the user places (startCall, callBack) and one Graine rang (a booked call, Ring now). WebRTC needs the platform's WebRTC module in the app (WebRTC calls). Mapped to each platform SDK's own setting.
What to expect — The agent's In-app call audio setting wins over the app: Standard keeps every call on the WebSocket, whatever callTransport says, from each app's next start. A call that tried WebRTC and could not use it before any audio flowed continues on the WebSocket inside the same call, and GraineCallInfo.transport says WEBSOCKET, with transportFallbackReason saying why. AUTO is only something you choose; a call's transport is WEBRTC or WEBSOCKET.
| Property | Type | Meaning |
|---|---|---|
wire | String | The platform SDKs' word: auto, websocket or webrtc — for logs. |
| Case | Meaning |
|---|---|
AUTO | The default: WebRTC when the app includes the WebRTC module and the dashboard allows it; else the WebSocket. |
WEBSOCKET | Always the WebSocket — as a call's transport, the call's audio runs on the WebSocket. |
WEBRTC | WebRTC wherever it can carry the call; the WebSocket only when the module is missing or WebRTC cannot set up this call — as a call's transport, the call's audio runs on WebRTC. |
Its functions: ofCall — each with its parameters, what it returns, its errors and an example.
GraineCalls.startCall
Call it from a “Call us” button to let the user call the agent.
The call screen opens and the call connects. reason becomes the prompt's call_reason; variables are this call's own prompt variables, over the SDK's (masked on the device). It answers on the spot.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
reason | String? | null | Why the user is calling — the agent hears it as call_reason. Blank is none. |
variables | Map<String, Any?> | emptyMap() | Prompt variables for this call only. |
agent | String? | null | Which configured agent to call, by name; null for the default agent. |
Returns — String? — the call's ID, which every outcome carries; null when another call is live or, on Android, when no screen of the app is in front to start it from. Example value: "call_6f1c2a9e-7d1b-4c47-9a6e-2f0d8b1c5e44".
Errors — IllegalStateException when the agent is not configured. A call that cannot connect is a FAILED outcome, not an exception.
GraineAgent.startCall
Call it to let the user call one of your other agents.
A shortcut for Graine.calls.startCall(reason, variables, agent = name), with this agent's name filled in.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
reason | String? | null | Why the user is calling — the agent's call_reason. |
variables | Map<String, Any?> | emptyMap() | Prompt variables for this call only. |
Returns — String? — the call's ID, or null when another call is live.
Errors — IllegalStateException when this agent is not configured.
GraineCalls.end
Call it from your own hang-up control.
Hangs up — or, while the call rings, declines it.
Parameters — none.
Returns — nothing.
Errors — none.
GraineCalls.setMuted
Call it from your own mute control.
Stops sending the user's voice without ending the call.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
muted | Boolean | required | true to stop sending the user's voice, false to send it again. |
Returns — nothing.
Errors — none.
GraineCalls.setSpeaker
Call it from your own loudspeaker control.
Turning it off sends the audio back to a connected headset, or to the earpiece when there is none.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
on | Boolean | required | true for the loudspeaker. |
Returns — nothing.
Errors — none.
GraineCallInfo
Read it to draw a call screen: the call's state, who is calling, the controls and the last line said.
data class GraineCallInfo(…)
Graine.calls.state.
What to expect — Every field has a value at every moment; with no call, state is IDLE and the rest are empty — except missedCall, kept until it is called back or dismissed.
| Field | Type | Default | Meaning |
|---|---|---|---|
state | GraineCallState | GraineCallState.IDLE | Read it to know which moment to draw: ringing, connecting, on the call (GraineCallState). |
callId | String? | null | The call's ID — the one its outcomes carry; null with no call. |
callerName | String? | null | The name to show: the ring's caller name, else the agent's call name or the app's display name. |
incoming | Boolean | false | true when the agent rang (a push); false for a call the user placed. |
hosted | Boolean | false | true when Graine rang it — a call booked by the agent, the user or your team; the ring says “Scheduled call”. |
muted | Boolean | false | Show the mute control as on. |
speakerOn | Boolean | false | Show the loudspeaker control as on (otherwise a headset, if one is connected, or the earpiece). |
agentSpeaking | Boolean | false | Animate the agent's avatar while true: its voice is being heard. |
caption | GraineCaption? | null | The last line said, while a call is live (GraineCaption). |
missedCall | GraineMissedCall? | null | The last ring nobody answered, until it is called back or dismissed (GraineMissedCall) — show “Missed call” with Call back. |
agent | String? | null | Which agent the call is with, by the name it was configured under ("default" for the default agent); null with no call. |
transport | GraineCallTransport? | null | What carries the call's audio, in either direction: WEBRTC or WEBSOCKET (GraineCallTransport). null with no call, before a transport is decided, and from a platform SDK older than WebRTC. Read it once the call is ACTIVE. |
transportFallbackReason | String? | null | Why a call that tried WebRTC runs on the WebSocket — one of the values below; null for a call that did not fall back. Read it once the call is ACTIVE. |
| Property | Type | Meaning |
|---|---|---|
val showsCallScreen: Boolean | Boolean | Read it to decide whether to show a call screen: there is a call, or a missed ring to call back. |
Why a call is on the WebSocket — the values of transportFallbackReason. Log them; the person sees nothing different, only “Connecting…” a moment longer.
| Value | Means |
|---|---|
mint_failed | The call's WebRTC session could not be set up: refused, rate-limited or unreachable. |
register_failed | Graine's media service did not accept the connection, or never answered it. |
no_response | The call got no reply at all. |
no_answer | Replies came; the answer did not. |
ice_timeout | Answered, but the audio path never connected — usually a network that blocks WebRTC. |
error | Anything else before the audio flowed. |
dashboard | Android only, for a call that would have used WebRTC: the agent's In-app call audio is Standard. |
unavailable | Android only, when the app chose WEBRTC: this build has no WebRTC module, or the device cannot run it. |
GraineCallState
Read it to know where the one native call is.
enum class GraineCallState { IDLE, RINGING, CONNECTING, ACTIVE, ENDED }
What to expect — IDLE → RINGING (the agent rings) or CONNECTING (the user called) → ACTIVE → ENDED, then IDLE again once reported.
| Case | Meaning |
|---|---|
IDLE | No call. |
RINGING | The agent is calling the user; show Answer and Decline. |
CONNECTING | The call was answered, or placed by the user, and is connecting. |
ACTIVE | The agent and the user can hear each other. |
ENDED | Over — reported once, then IDLE. |
GraineCallOutcome
Read it to send what became of a call to your server.
data class GraineCallOutcome(…)
From Graine.calls.outcomes, as it happens.
What to expect — One call reports RINGING (an agent's call), then ANSWERED followed by ENDED or FAILED; or DECLINED, MISSED or FAILED alone. hosted means Graine rang it, and the SDK has already told Graine. Do not rely on its order relative to state.
| Field | Type | Default | Meaning |
|---|---|---|---|
callId | String | required | Use it to match the outcome to the call: the push's call_id, Graine's for a ring it sent, or the ID startCall returned. |
outcome | Kind | required | What became of it (GraineCallOutcome.Kind). |
reason | String? | null | Why: for MISSED, no_answer or busy (another call was live); for ENDED, local (this app hung up), remote (the agent did) or failed (the connection was lost); for FAILED, the sentence saying why (at most 200 characters). |
durationSeconds | Int? | null | How long a call that connected lasted, in whole seconds — with ENDED. |
hosted | Boolean | false | true when Graine rang it — a call booked by the agent, the user or your team. |
agent | String? | null | Which agent the call was with, by the name it was configured under; null when the platform does not know (a ring refused as busy). |
GraineCallOutcome.Kind
Read it to branch on what became of a call.
enum class Kind(val wire: String)
| Property | Type | Meaning |
|---|---|---|
wire | String | One word for your server: ringing, answered, declined, missed, failed or ended. |
| Case | Meaning |
|---|---|
RINGING | An agent's call is ringing on this device. |
ANSWERED | The user picked up; ENDED or FAILED follows. |
DECLINED | The user declined it. |
MISSED | The ring went unanswered (no_answer), or the user was already on another call (busy). |
FAILED | It never connected; reason says why. |
ENDED | A call that connected is over; reason says who ended it and durationSeconds how long it lasted. |
GraineCallScreen
Use it to show the SDK's call screen in a Compose Multiplatform UI while there is a call.
In ai.graine:inapp-kmp-compose. The ring (Answer, Decline — “Scheduled call” for a call Graine rang), connecting, the call with its controls, and a missed ring with Call back, in the agent's design. Show it whenever Graine.calls.state.value.showsCallScreen is true. On iOS it is drawn in place, and VoiceOver reaches into it. On Android the SDK's call screen is a full-screen activity that also covers the lock screen and opens by itself when a call rings or is placed; composing this brings it to the front.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
modifier | Modifier | Modifier | Where the call screen is drawn (iOS). |
Returns — nothing (a composable).
Errors — none.
GraineViews.callScreen
Call it on iOS to get the call screen as a view controller, for while the app is open.
The ring, the call, and a missed ring with Call back — the app's, whichever agent calls.
Parameters — none.
Returns — UIViewController.
Errors — IllegalStateException before Graine.configure.
Calls Graine rings
Graine rings your users itself — a call the agent booked in a conversation, one the user booked, or one your team booked — once your push credentials are uploaded (Workspace settings → In-app calling). On iOS, call enableVoIPPushes() at every launch; on Android, add Firebase Cloud Messaging to the app and the SDK registers for pushes itself (an app with a messaging service of its own forwards its token and data messages to onNewToken and handlePush). The SDK registers the device, rings each call on the phone's own call screen (“Scheduled call”), and reports what became of it. Your own server can ring the same way, with a push of type agent_call.
A call the person answers carries its audio exactly as one they place: over WebRTC when the app includes the platform's WebRTC module and GraineCallsConfig.callTransport and the agent's In-app call audio setting allow it (WebRTC calls) — prepared while the ring shows, so answering connects as fast as before — and Graine.calls.state reports transport and transportFallbackReason for it too.
The push your server sends (data only, nothing personal): type: "agent_call", call_id (required — every outcome carries it back), reason (the prompt's call_reason), caller_name (what the ring shows), variables (a JSON object as a string: more prompt variables), and agent_id (which of the app's agents the call is with).
GraineCalls.enableVoIPPushes
Call it at every launch on iOS, so the agent's calls can ring a closed app.
iOS wakes a closed app for a VoIP push and hands it over once this has run; the SDK registers the token with Graine when Graine rings this app. Call configure first. Android: does nothing — with Firebase in the app the SDK registers for pushes itself (see onNewToken and handlePush for an app with its own messaging service).
Parameters — none.
Returns — nothing.
Errors — IllegalStateException before Graine.configure.
GraineCalls.onNewToken
Call it on Android from a FirebaseMessagingService of your own, in onNewToken. Without one, the SDK's own messaging service receives each new token, and the SDK reads the current one when it starts.
The token is kept and registered with Graine when Graine rings this app. It works before Graine.configure, as a messaging service may start first. iOS: does nothing — the VoIP token comes through enableVoIPPushes().
| Parameter | Type | Default | Meaning |
|---|---|---|---|
token | String | required | The new Firebase Cloud Messaging token. |
Returns — nothing.
Errors — none.
GraineCalls.handlePush
Call it on Android first thing in the onMessageReceived of a FirebaseMessagingService of your own. Without one, the SDK's own messaging service calls it for every message.
When the message is a call — Graine's own ring, or one your server sent — the SDK rings it and the answer is true; your own messages answer false. iOS: always false — a VoIP push reaches the SDK by itself.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
data | Map<String, String> | required | The message's data, exactly as the messaging service received it. |
Returns — Boolean — true when it was a call and you should do nothing more with it. Example value: true.
Errors — none.
GraineCalls.answer
Call it from a call screen of your own to pick up the ringing call.
The SDK's own call screen does this itself.
Parameters — none.
Returns — nothing.
Errors — none.
GraineCalls.decline
Call it from a call screen of your own to decline the ringing call.
Reported as DECLINED. For a call Graine rang, nothing rings that user again for 6 hours.
Parameters — none.
Returns — nothing.
Errors — none.
GraineCalls.callBack
Call it from a missed-call screen's “Call back”.
Returns the last missed call: the user calls the agent back, with the missed call's reason and its agent. On Android it starts from the screen in front.
Parameters — none.
Returns — String? — the new call's ID, or null when it could not start (another call is live, or no screen is in front). Example value: "call_8a20d3c1-…".
Errors — none.
GraineCalls.dismissMissedCall
Call it when the user has seen the missed call, so a call screen can go.
Clears GraineCallInfo.missedCall.
Parameters — none.
Returns — nothing.
Errors — none.
GraineIncomingCall
Read it to know an agent's call started ringing on this device — for your own UI or analytics.
data class GraineIncomingCall(…)
From Graine.calls.incoming. The SDK shows the ring itself.
What to expect — It carries only what the ring shows: nothing personal.
| Field | Type | Default | Meaning |
|---|---|---|---|
callId | String | required | The call's ID, which its outcomes carry. |
callerName | String? | required | Read it for the name on the ring: the push's caller name when it has one, otherwise the agent's call name or the app's display name. |
reason | String? | required | Why the agent is calling, when the push said; a call Graine rang keeps its reason with Graine. |
hosted | Boolean | required | true when Graine rang it. |
agent | String? | null | Which configured agent is ringing; null when the platform does not say. |
GraineMissedCall
Read it to show a missed call with “Call back”.
data class GraineMissedCall(…)
GraineCallInfo.missedCall: the last ring nobody answered, kept until callBack() or dismissMissedCall(). A ring refused because another call was live is not kept.
| Field | Type | Default | Meaning |
|---|---|---|---|
callId | String | required | The missed call's ID. |
callerName | String? | required | Who called, as the ring showed it. |
reason | String? | required | Why, when the ring said. |
hosted | Boolean | required | true when Graine rang it. |
at | Instant | required | When it was missed; show it in the device's time zone. |
Permissions and the setup check
What a call needs from the phone, and from your app's setup, in one place — read from the platform SDKs, the same on both. The SDK never asks on its own at launch: it asks for the microphone when a call starts without it, and requestCallPermissions() asks earlier, when your app chooses (on Android from a transparent screen of the SDK's own, over the app's screen in front). callSetupProblems says what your setup still lacks; the platform SDK logs each problem once from configure on. A problem is never an exception: calls still work where they can. On iOS these need GraineInApp of this version.
GraineCalls.callPermissions
Read it to see which call permissions the phone holds now.
The microphone, and on Android the ring's notifications (13+ asks) and the full-screen ring (14+, Settings only); on iOS those two are NOT_NEEDED — CallKit rings and draws its own screen.
Returns — a GraineCallPermissions. Read it again when the app returns to the foreground: the user may have changed Settings.
Errors — IllegalStateException before Graine.configure.
GraineCalls.requestCallPermissions
Call it at a moment you choose — onboarding, a "Call me" screen — to ask for what a call needs.
Asks for the call permissions the system's dialog can still ask for — the microphone, and notifications on Android 13+ — and returns them as they stand after; suspends until the user answers (at once when there is nothing to ask). The full-screen ring has no dialog: openCallSettings(GraineCallPermission.FULL_SCREEN) opens its page.
Returns — a GraineCallPermissions.
Errors — IllegalStateException before Graine.configure.
GraineCalls.openCallSettings
Call it to send the user to the Settings page that changes a call permission.
Opens, on Android, the full-screen ring's own page (14+) or the app's notification settings, from the screen in front; otherwise the app's page, where the microphone is.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
item | GraineCallPermission | required | The permission the user should change. |
Returns — Boolean: true when a page opened.
Errors — IllegalStateException before Graine.configure.
GraineCalls.callSetupProblems
Read it to learn what your app's setup still lacks for calls, in plain words.
What the app's setup still lacks for calls, read now — each a stable code and a sentence with the exact fix; empty when nothing is missing. The codes: iOS microphone_usage_missing, background_audio_missing, background_voip_missing (Info.plist), push_entitlement_missing, voip_pushes_not_enabled (no enableVoIPPushes() 15 seconds after configure), no_voip_token; Android manifest_entry_missing, no_push_token (no push token 15 seconds after configure: Firebase missing or not set up in the app, or no onNewToken from an app with its own messaging service), notifications_not_allowed, full_screen_not_allowed; both microphone_blocked and hosted_ringing_off (no push credentials uploaded for this platform: Graine can't ring this phone).
Returns — List<GraineCallSetupProblem>.
Errors — IllegalStateException before Graine.configure.
GraineCallPermission
Use it to name a call permission.
enum class GraineCallPermission(val wire: String) { MICROPHONE("microphone"), NOTIFICATIONS("notifications"), FULL_SCREEN("fullScreen") }
What to expect — Three permissions, the same on every SDK. On iOS only the microphone is ever asked for.
| Property | Type | Meaning |
|---|---|---|
wire | String | The word every Graine SDK uses for it. |
| Case | Meaning |
|---|---|
MICROPHONE | The user's voice. |
NOTIFICATIONS | A call that rings while the app is closed (Android; 13+ asks). iOS rings through CallKit, which needs none. |
FULL_SCREEN | A ring that fills a locked screen (Android 14+, allowed in Settings). iOS: CallKit's own screen. |
GraineCallPermission.of(word) reads one by its word — microphone, notifications, fullScreen (full_screen and any case too); null for any other.
GraineCallPermissionStatus
Read it to tell whether a call permission is allowed, can still be asked for, or only Settings can change it.
enum class GraineCallPermissionStatus(val wire: String) { GRANTED("granted"), DENIED("denied"), BLOCKED("blocked"), NOT_NEEDED("notNeeded") }
What to expect — GRANTED and NOT_NEEDED need nothing; DENIED means requestCallPermissions() can ask; BLOCKED means only Settings can change it — offer openCallSettings.
| Property | Type | Meaning |
|---|---|---|
wire | String | The word every Graine SDK uses for it. |
isSatisfied | Boolean | Granted, or nothing to grant. |
| Case | Meaning |
|---|---|
GRANTED | Allowed. |
DENIED | Not allowed yet: requestCallPermissions() can ask for it. |
BLOCKED | Refused for good, or only allowed in Settings. |
NOT_NEEDED | This phone needs no such permission. |
GraineCallPermissionStatus.of(word) reads one from a platform's word; anything it does not know is DENIED, never granted.
GraineCallPermissions
Read it to see every call permission at once.
data class GraineCallPermissions(val microphone: GraineCallPermissionStatus, val notifications: GraineCallPermissionStatus, val fullScreen: GraineCallPermissionStatus)
What to expect — A snapshot: read callPermissions again for the latest. allGranted is the one check before offering calls; permissions[GraineCallPermission.MICROPHONE] reads one by name.
| Property | Type | Meaning |
|---|---|---|
microphone | GraineCallPermissionStatus | The microphone. |
notifications | GraineCallPermissionStatus | The ring's notifications — NOT_NEEDED on iOS. |
fullScreen | GraineCallPermissionStatus | The full-screen ring — NOT_NEEDED on iOS. |
allGranted | Boolean | Every permission is granted or not needed: calls can ring and connect. |
GraineCallSetupProblem
Read it to show or log what your app's setup still lacks.
data class GraineCallSetupProblem(val code: String, val message: String)
What to expect — The code is stable and the same on every SDK (the list is under GraineCalls.callSetupProblems); the message is a sentence for the developer, with the exact fix.
| Property | Type | Meaning |
|---|---|---|
code | String | Stable: act on it, or count it. |
message | String | What is missing and how to fix it, in plain words. |
Booking with the SDK's sheet
“Call me at…” with no screen of your own: presentBooking shows the native SDK's booking sheet in the agent's design, and Graine rings this phone at the time the user picks.
What the sheet shows. A header, “Book a call”, with “{call name} will call you” under it — the agent's call name, else its chat title, else its heading. Day: a chip for each day from today to the furthest day the agent allows — “Today”, “Tomorrow”, then the weekday and date, such as “Thu 16 Oct”. Time: Android's own time picker in spinner mode (inside the SDK's own dialog) or iOS's time wheel, both in 5-minute steps, held to the times the chosen day offers between earliest(now) (rounded up to the next 5 minutes) and latest(now). The phone's time zone and the chosen time, such as “Asia/Kolkata · Tue 14 Oct, 6:00 PM”. What's it about? (optional): one line, at most 120 characters. Book call, with a spinner while it books; then “Booked — {call name} will call you on {day}, {time}” and Done. A sheet up from the bottom on a phone, a centred panel on a tablet.
A refusal stays in the sheet as one line under the button:
| Code | The line |
|---|---|
too_soon | Pick a time at least N minutes from now |
too_far | Pick a time within N days |
outside_window | That time is outside the hours we can call — pick another |
too_many_pending | You already have N calls booked — cancel one first |
scheduling_off | Booking a call isn't available right now |
identity_required | Sign in to book a call |
| anything else | Couldn't book just now — try again |
N is the agent's own number, singular for one (“1 minute”). An agent that cannot be booked now opens a sheet that says why (the scheduling_off or identity_required line) and books nothing.
In the chat. The SDK's chat windows (GraineChat(), GraineViews.chat()) show the user's booked calls in their Upcoming calls card, with Book a call under them where booking is available — or alone in the card's place when none are booked; never when the design hides the booked-calls card. A booking refreshes the card.
Graine.bookingAvailable
Read it to decide whether to show your own “Call me” button.
true when Graine rings this phone (the workspace's In-app calling is on, for this platform), the default agent lets users book, and the user is identified with the trait the agent's Who is the user? setting names. The native SDK's own rule, read as it is now; false before configure, and on iOS with a GraineInApp older than this library (0.7.0). Check it again after identify and after the agent's settings have loaded. GraineAgent.bookingAvailable answers for another agent.
Parameters — none.
Returns — Boolean. Example value: true.
Errors — none.
Graine.presentBooking
Call it from your own “Call me” button to let the user book a call in the SDK's sheet.
Shows the booking sheet (above) for the default agent and returns once it closes. On Android it opens over the activity in front; on iOS over the key window's top view controller. One sheet at a time.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
reason | String? | null | Fills What's it about?; the user may change it. Kept with the call for the agent and never put in the push that rings it. |
Returns — GraineScheduledCall? — the booked call (GraineScheduledCall), or null when the user closed the sheet without booking, a sheet is already open, or (Android) no activity is in front. Example value: GraineScheduledCall(id="6f1c2a9e-…", at=2026-10-16T12:30:00Z, status=Scheduled, source=App, reason="Talk through my order").
Errors — IllegalStateException before configure. On iOS with a GraineInApp older than this library: GraineException Unavailable — “This app links a GraineInApp without the booking sheet. Link GraineInApp 0.8.0, the version of this library.”. Refusals stay in the sheet: it does not throw them.
GraineAgent.bookingAvailable
Read it to decide whether to offer booking with one of your other agents.
Graine.bookingAvailable for this agent: Graine rings this phone, this agent lets users book, and the user is identified with this agent's identity trait. false before the agent is configured.
Parameters — none.
Returns — Boolean.
Errors — none.
GraineAgent.presentBooking
Call it to show the booking sheet for one of your other agents.
Graine.presentBooking for this agent, in its design and with its rules.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
reason | String? | null | Fills What's it about?. |
Returns — GraineScheduledCall? — the booked call, or null when nothing was booked.
Errors — IllegalStateException when this agent is not configured; on iOS with an older GraineInApp, GraineException Unavailable, as Graine.presentBooking.
The scheduled-calls list
Book calls from your own interface, list the user's upcoming calls and cancel one — the same calls the sheet and the chat's card show. Graine rings this device at the booked time.
GraineSchedulingRules
Read it to build your own booking screen — which times the agent accepts, and whether users may book at all.
data class GraineSchedulingRules(…)
Graine.schedulingRules (or GraineAgent.schedulingRules, GraineSession.schedulingRules).
What to expect — From the agent's settings once they load; before that the defaults hold, with booking off. scheduleCall refuses on the device first — SchedulingOff, TooSoon (before earliest(now)), TooFar (after latest(now)) — and Graine then checks the calling hours and the calls already waiting.
| Field | Type | Default | Meaning |
|---|---|---|---|
usersCanBook | Boolean | false | Read it to know whether users may book calls from the app at all; false when the settings say nothing. |
minLeadMinutes | Int | 5 | The least notice a booking needs, in minutes — use it with earliest(). |
maxDaysAhead | Int | 14 | How far ahead a booking may be, in days — use it with latest(). |
maxPendingPerUser | Int | 3 | How many upcoming calls a user may have at once; beyond it, TooManyPending. |
ringTimeoutSeconds | Int | 45 | How long a call Graine rings keeps ringing before it is missed, 20–60 seconds. |
callName | String? | null | The name the agent's calls ring with; null uses the app's display name. |
hostedRinging | Boolean | false | Read it to know whether Graine rings this workspace's users itself (its push credentials are uploaded). |
platforms | List<GrainePlatform> | emptyList() | The phones Graine rings (GrainePlatform); empty means every platform. |
Its functions: earliest, latest — each with its parameters, what it returns, its errors and an example.
GraineSchedulingRules.earliest
Call it for the soonest time a booking is accepted, to start your time picker there.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
now | Instant | Clock.System.now() | The moment to count from; now when left out. |
Returns — Instant — now plus minLeadMinutes. Example value: 2026-10-15T12:05:00Z.
Errors — none.
GraineSchedulingRules.latest
Call it for the latest time a booking is accepted, to end your time picker there.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
now | Instant | Clock.System.now() | The moment to count from; now when left out. |
Returns — Instant — now plus maxDaysAhead days. Example value: 2026-10-29T12:00:00Z.
Errors — none.
GrainePlatform
Read it in GraineSchedulingRules.platforms to know which phones Graine rings.
enum class GrainePlatform(val wire: String) { IOS("ios"), ANDROID("android") }
| Property | Type | Meaning |
|---|---|---|
wire | String | The platform's word: ios or android. |
| Case | Meaning |
|---|---|
IOS | iPhones and iPads. |
ANDROID | Android phones and tablets. |
Graine.scheduleCall
Call it to book a call from your own booking screen.
Books a call from the default agent for the signed-in user (or, with nobody signed in, this install) at at — “call me tomorrow at 6”; Graine rings this device then. It waits for the agent's settings (at most 20 seconds), then refuses on the device — before anything is sent — what the rules already settle, in the booking sheet's words: SchedulingOff, “Booking a call isn't available right now”; TooSoon, “Pick a time at least N minutes from now”; TooFar, “Pick a time within N days” (N singular for one: “1 minute”, “1 day”). Graine then checks its own: the calling hours, in this device's time zone, and the calls already waiting. reason is trimmed to 120 characters, kept with the call for the agent, and never put in the push that rings it. An open chat's Upcoming calls card shows the booking at once. GraineAgent.scheduleCall books with another agent.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
at | Instant | required | When the call should ring: between schedulingRules.earliest() and latest(). |
reason | String? | null | What the call is about, at most 120 characters — the agent hears it when it rings. |
Returns — GraineScheduledCall — the booked call (GraineScheduledCall), status Scheduled. Example value: GraineScheduledCall(id="6f1c2a9e-…", at=2026-10-16T12:30:00Z, status=Scheduled, source=App, reason="Talk through my order").
Errors — throws GraineException — on the device: SchedulingOff, TooSoon, TooFar; from Graine: OutsideWindow, TooManyPending, IdentityRequired, DeviceLimit, InvalidRequest, RateLimited, Network, Unavailable (“The booking was accepted but not described.” when Graine booked it without describing it).
GraineAgent.scheduleCall
Call it to book a call with one of your other agents.
Graine.scheduleCall for this agent, checked against this agent's rules.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
at | Instant | required | When the call should ring. |
reason | String? | null | What it is about, at most 120 characters. |
Returns — GraineScheduledCall.
Errors — throws GraineException as Graine.scheduleCall.
Graine.scheduledCalls
Call it to list the user's upcoming calls in your own interface.
The calls the default agent has booked for the signed-in user (or this install) that are still to come, soonest first.
Parameters — none.
Returns — List<GraineScheduledCall>. Example value: [GraineScheduledCall(id="6f1c2a9e-…", at=2026-10-16T12:30:00Z, status=Scheduled, source=Agent, reason=null)].
Errors — throws GraineException: Network, RateLimited, Unavailable, InvalidKey, OriginRefused.
GraineAgent.scheduledCalls
Call it to list the calls one of your other agents has booked for the user.
Parameters — none.
Returns — List<GraineScheduledCall>, soonest first.
Errors — throws GraineException as Graine.scheduledCalls.
Graine.cancelScheduledCall
Call it when the user calls off one of their booked calls.
Only a call booked for this user or install; any other is NotFound.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
id | String | required | The call's id, from scheduleCall, scheduledCalls or presentBooking. |
Returns — GraineCallStatus — its status afterwards, Cancelled. Example value: GraineCallStatus.Cancelled.
Errors — throws GraineException: InvalidRequest (“No call id.”) for a blank id, NotFound, Network, RateLimited, Unavailable.
GraineAgent.cancelScheduledCall
Call it to call off a call one of your other agents booked.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
id | String | required | The call's id. |
Returns — GraineCallStatus — Cancelled.
Errors — throws GraineException as Graine.cancelScheduledCall.
GraineScheduledCall
Read it to show a booked call: when it rings, where it is, and whether it can still be cancelled.
data class GraineScheduledCall(…)
What to expect — From scheduleCall (status Scheduled), scheduledCalls and presentBooking. at is a moment: show it in the device's time zone. A status or source newer than the SDK arrives as Unknown, carrying its word.
| Field | Type | Default | Meaning |
|---|---|---|---|
id | String | required | Use it to cancel the call — Graine's ID for it, a UUID. |
at | Instant? | required | When it rings; null only when Graine's time could not be read. |
status | GraineCallStatus | required | Where it is (GraineCallStatus). |
source | GraineCallSource? | null | Who booked it (GraineCallSource); null when Graine does not say. |
reason | String? | null | What it is about, when Graine shares it — at most 120 characters. |
| Property | Type | Meaning |
|---|---|---|
val isUpcoming: Boolean | Boolean | Use it to keep a call in an upcoming list: true while it is Scheduled or Ringing. |
val canCancel: Boolean | Boolean | Use it to show a Cancel control: true while it is Scheduled, before it rings. |
GraineCallStatus
Read it to show where a booked call is.
sealed class GraineCallStatus(val wire: String)
What to expect — Scheduled → Ringing → Answered → Completed; or Declined, Missed, Failed, Cancelled, Expired, NoDevice. A status newer than this SDK arrives as Unknown(raw): give every when an else.
| Property | Type | Meaning |
|---|---|---|
wire | String | Graine's word for the status — scheduled, no_device, … — for logs and your server. |
| Case | Meaning |
|---|---|
Scheduled | Booked, waiting for its time; it can still be cancelled. |
Ringing | Ringing the user now. |
Answered | The user picked up. |
Completed | The call took place and is over. |
Declined | The user declined it; nothing rings them again for 6 hours. |
Missed | It rang out unanswered. |
Failed | It could not be placed. |
Cancelled | Called off before it rang. |
Expired | Its time passed while it could not ring — more than 10 minutes late, or no opening left in the calling hours. |
NoDevice | Nothing could be rung: the user has no device registered for calls in the last 30 days. |
Unknown(raw) | A status newer than this SDK, carrying Graine's word in raw. |
Its functions: from — each with its parameters, what it returns, its errors and an example.
GraineCallSource
Read it to show who booked a call.
sealed class GraineCallSource(val wire: String)
What to expect — A source newer than this SDK arrives as Unknown(raw).
| Property | Type | Meaning |
|---|---|---|
wire | String | Graine's word for the source — sdk for App. |
| Case | Meaning |
|---|---|
Agent | The agent booked it in a conversation. |
App | Your app booked it (scheduleCall, the booking sheet). |
Dashboard | Your team booked it in the dashboard. |
Api | Your server booked it through the API. |
Unknown(raw) | A source newer than this SDK, carrying Graine's word. |
Its functions: from — each with its parameters, what it returns, its errors and an example.
Design
The SDK's own screens — the chat, the voice bar, the call screen and the booking sheet — draw the design the brand chose in the Garage: colours, typefaces, corners, the launcher's look, light and dark. The last design is kept on the device, so a call that wakes a closed app is drawn in it too. An app that draws UI of its own in the brand's design reads the same tokens from shared code: Graine.theme (each agent's theme), a GraineThemeTokens.
Graine.theme
Collect it to draw UI of your own — a card, a button, a screen around the agent — in the brand's design.
The default agent's design tokens: the colours, type, corners, depth, motion and icons the SDK's own screens draw with, and the chat window's and the call screen's tokens, for the phone's current light or dark appearance — or the brand's fixed scheme. It changes when the phone switches between light and dark. null before the agent's settings load, and when they carry no design. Each agent has its own: GraineAgent.theme. On iOS it needs GraineInApp of this version; with an older one it stays null.
Parameters — none.
Returns — StateFlow<GraineThemeTokens?> (GraineThemeTokens). Example value: GraineThemeTokens(v=1, preset="graine-classic", scheme="light", colors=Colors(background="#F7F7F5", …), …).
Errors — none.
GraineThemeTokens
Read it to draw UI of your own in the brand's design.
data class GraineThemeTokens(…)
One scheme's design tokens — the design contract every Graine SDK draws from, the same values as React Native's useGraineTheme(), iOS theme, Android themeTokens() and Flutter tokens — as plain numbers and #RRGGBB strings. Lengths are dp (points on iOS), type sizes sp. From Graine.theme and each agent's theme.
What to expect — Resolved by the native SDK from the agent's design when the settings load, for the phone's light or dark appearance (or the brand's fixed scheme); reading them never throws. A design without a complete set of colours is no design: theme is null. The words — "outline", "spring", "window" — are the design's own: compare against them, and keep a default branch, since a newer design can send a word this build does not know.
| Field | Type | Meaning |
|---|---|---|
v | Int | The token format's version, for logs. |
preset | String | The design style the brand started from — informational: the tokens are already resolved. |
scheme | String | Which of the design's two sets this is: light or dark. |
colors | Colors | Every colour a surface is drawn in (GraineThemeTokens.Colors). |
font | Font | The typefaces, sizes, weights, letter spacing and line heights (GraineThemeTokens.Font). |
shape | Shape | Corner radii, outline width and the launcher's outline (GraineThemeTokens.Shape). |
spacing | Spacing | The spacing unit, card padding and gap (GraineThemeTokens.Spacing). |
elevation | Elevation | How depth is drawn: soft shadows, or a hard offset (GraineThemeTokens.Elevation). |
motion | Motion | How long and how things move (GraineThemeTokens.Motion). |
icons | Icons | The icons' style and stroke, and the launcher's glyph (GraineThemeTokens.Icons). |
glass | Glass? | Translucent surfaces (GraineThemeTokens.Glass); null for opaque ones. |
bubbles | String | How chat messages sit on the page: filled, outline or minimal. |
chrome | String | none, or window: the chat, the caption and components open as desktop windows. |
chat | Chat? | The chat window's own tokens (GraineThemeTokens.Chat); null from a server that predates them — draw from colors. |
call | Call? | The call screen's tokens (GraineThemeTokens.Call); null from a server that predates them. |
| Property | Type | Meaning |
|---|---|---|
val isDark: Boolean | Boolean | Read it to know these are the dark tokens (scheme == "dark"). |
GraineThemeTokens.Colors
Read it to colour your own UI exactly as the agent's design does. data class Colors(…), as GraineThemeTokens.colors: seventeen colours, each #RRGGBB.
| Field | Type | Meaning |
|---|---|---|
background | String | The page behind everything in the chat window — your own full-screen backgrounds. |
surface | String | The voice bar, the chat header and sheets — your own bars and panels. |
surfaceRaised | String | Cards, the message field and unselected chips — anything that sits on a surface. |
text | String | Main text. |
textMuted | String | Secondary text: captions, labels, times. |
border | String | Hairlines and outlines. |
accent | String | The brand colour: primary buttons, the launcher, the send button, anything selected. |
onAccent | String | Text and icons drawn on the accent. |
accentSoft | String | The accent at low strength: chips, selected rows, the focus halo. |
success | String | Good news: online dots, confirmations. |
warning | String | Cautions. |
danger | String | Errors, the end-call button, a refusal line. |
userBubble | String | The user's chat bubbles. |
userBubbleText | String | Text in the user's bubbles. |
agentBubble | String | The agent's chat bubbles. |
agentBubbleText | String | Text in the agent's bubbles. |
scrim | String | What dims the app behind a sheet or dialog, drawn at 40 %. |
GraineThemeTokens.Font
Read it to set your own text in the design's typefaces, sizes and weights. data class Font(…), as GraineThemeTokens.font.
| Field | Type | Meaning |
|---|---|---|
body | String | The body text's family: system is the platform's own UI font; any other name is a family your app may bundle. |
heading | String | The titles' family, read the same way. |
scale | Scale | The type sizes, in sp (GraineThemeTokens.Font.Scale). |
weights | Weights | The weights (GraineThemeTokens.Font.Weights). |
letterSpacing | Pair | Letter spacing for body text and titles, in em (GraineThemeTokens.Font.Pair). |
lineHeight | Pair | Line height for body text and titles, as a multiple of the size. |
GraineThemeTokens.Font.Scale
Read it to size your own text on the design's scale. data class Scale(val caption: Double, val body: Double, val title: Double, val display: Double), in sp.
| Field | Type | Meaning |
|---|---|---|
caption | Double | Small text: captions, labels and times. |
body | Double | Body text and chat messages. |
title | Double | Titles, such as a header's. |
display | Double | The largest text, such as the call screen's name. |
GraineThemeTokens.Font.Weights
Read it to weight your own text the way the design does. data class Weights(val body: Int, val strong: Int, val title: Int): CSS weights, 100–900 — 400 is regular, 600 semibold.
| Field | Type | Meaning |
|---|---|---|
body | Int | Body text. |
strong | Int | Emphasis: labels and buttons. |
title | Int | Titles. |
GraineThemeTokens.Font.Pair
Read it for a value that differs between body text and titles. data class Pair(val body: Double, val title: Double) — letter spacing in em, or line height as a multiple of the size, as its field says.
| Field | Type | Meaning |
|---|---|---|
body | Double | The value for body text. |
title | Double | The value for titles. |
GraineThemeTokens.Shape
Read it for the design's corners, outline width and launcher shape. data class Shape(val radius: Radius, val borderWidth: Double, val launcher: String), as GraineThemeTokens.shape.
| Field | Type | Meaning |
|---|---|---|
radius | Radius | The corner radii (GraineThemeTokens.Shape.Radius). |
borderWidth | Double | The width of every outline, in dp. |
launcher | String | The launcher's outline: circle, squircle, rounded-square, square or pill. |
GraineThemeTokens.Shape.Radius
Read it to round your own corners the way the design does. data class Radius(val sm: Double, val md: Double, val lg: Double, val xl: Double), in dp.
| Field | Type | Meaning |
|---|---|---|
sm | Double | Small corners: chips, a square launcher. |
md | Double | Buttons and text fields. |
lg | Double | Cards, and a rounded-square launcher. |
xl | Double | The roundest corners: pill-shaped chips, sheets, the voice bar. |
GraineThemeTokens.Spacing
Read it to space your own UI on the design's rhythm. data class Spacing(val unit: Double, val cardPadding: Double, val gap: Double), in dp.
| Field | Type | Meaning |
|---|---|---|
unit | Double | The base step: other spacing is a multiple of it. |
cardPadding | Double | The padding inside a card. |
gap | Double | The gap between items in a list or a row. |
GraineThemeTokens.Elevation
Read it to give your own cards and floating views the design's depth. data class Elevation(val card: Shadow?, val floating: Shadow?, val hardOffset: Offset?), as GraineThemeTokens.elevation.
| Field | Type | Meaning |
|---|---|---|
card | Shadow? | The shadow under cards (GraineThemeTokens.Shadow); null for none. |
floating | Shadow? | The shadow under what floats — the voice bar, the launcher, sheets; null for none. |
hardOffset | Offset? | A solid offset drawn instead of soft shadows (GraineThemeTokens.Offset); null unless the design draws depth that way. |
GraineThemeTokens.Shadow
Read it to draw one of the design's soft shadows. data class Shadow(val y: Double, val blur: Double, val spread: Double, val opacity: Double, val color: String), used by GraineThemeTokens.Elevation.
| Field | Type | Meaning |
|---|---|---|
y | Double | How far down the shadow falls, in dp. |
blur | Double | How soft it is: the blur radius, in dp. |
spread | Double | How far it grows past the shape, in dp. |
opacity | Double | How dark it is, 0–1, applied to color. |
color | String | The shadow's colour, #RRGGBB. |
GraineThemeTokens.Offset
Read it to draw the solid, offset shadow some designs use instead of a soft one. data class Offset(val x: Double, val y: Double): the shape repeated behind itself, drawn in colors.text, as GraineThemeTokens.elevation.hardOffset.
| Field | Type | Meaning |
|---|---|---|
x | Double | How far right it sits, in dp (negative: left). |
y | Double | How far down it sits, in dp (negative: up). |
GraineThemeTokens.Motion
Read it to move your own UI at the design's pace — or not at all. data class Motion(val durationMs: Int, val easing: String, val bounce: Double), as GraineThemeTokens.motion. The phone's own reduce-motion setting wins over any number here.
| Field | Type | Meaning |
|---|---|---|
durationMs | Int | How long a transition runs, in milliseconds; 0 means no transition at all. |
easing | String | The curve: standard, emphasized, spring, linear or none (no movement). |
bounce | Double | How much a spring overshoots, 0–1. |
GraineThemeTokens.Icons
Read it to match the design's icon style and the launcher's glyph. data class Icons(val style: String, val stroke: Double, val launcher: Glyph?), as GraineThemeTokens.icons.
| Field | Type | Meaning |
|---|---|---|
style | String | The style of the SDK's icons: outline, filled, duotone or sharp. |
stroke | Double | The line width of icons and stroked glyphs, in dp. |
launcher | Glyph? | The launcher's glyph (GraineThemeTokens.Glyph); null when the launcher shows the brand's logo. |
GraineThemeTokens.Glyph
Read it to draw the brand's launcher glyph yourself, from its path. data class Glyph(val id: String, val path: String, val stroke: Boolean), as GraineThemeTokens.icons.launcher.
| Field | Type | Meaning |
|---|---|---|
id | String | The glyph's name, such as waveform. |
path | String | The drawing: SVG path data on a 24 × 24 grid. |
stroke | Boolean | true: draw it as a line icons.stroke wide; false: fill it. |
GraineThemeTokens.Glass
Read it to draw translucent surfaces the way a glass design does. data class Glass(val blur: Double, val surfaceOpacity: Double), as GraineThemeTokens.glass — null for an opaque design.
| Field | Type | Meaning |
|---|---|---|
blur | Double | How much what is behind a translucent surface is blurred, in dp, where the platform can. |
surfaceOpacity | Double | The opacity colors.surface is drawn at over that blur, 0–1. |
GraineThemeTokens.Chat
Read it to draw a chat window of your own the way the agent's design does. data class Chat(…), as GraineThemeTokens.chat: the Garage's chat window, resolved. Colours are #RRGGBB; an empty word means the SDK's own default.
| Field | Type | Meaning |
|---|---|---|
title | String | The header's title; empty: the agent's heading. |
subtitle | String | The header's second line while online; empty: “Online”. |
avatar | String | The header's mark: logo, glyph or none. |
placeholder | String | The message field's placeholder, such as “Message…”. |
showTimestamps | Boolean | Whether each message shows its time. |
suggestions | List<String> | Up to four replies offered before the user has said anything; a tap sends one. |
background | String | Behind the messages. |
pattern | String | none, or dots over the messages in patternColor. |
patternColor | String | The dots' colour. |
headerBackground | String | The header's fill. |
headerText | String | The header's title colour. |
headerSubtext | String | The header's second line's colour. |
composerBackground | String | The message field's fill. |
composerText | String | The message field's text colour. |
composerPlaceholder | String | The placeholder's colour. |
composerBorder | String | The message field's outline. |
size | GraineChatSize? | How big the window opens — the dashboard's Window size (GraineChatSize); null for a word this build does not know: draw it compact. |
showUpcomingCalls | Boolean | Whether the Upcoming calls card — and Book a call — shows at the top of the chat (Booked calls in the dashboard). |
upcomingTitle | String | That card's title; empty: “Upcoming call” or “Upcoming calls”. |
showHistory | Boolean | Whether the conversation kept from before is shown (Earlier messages); off, the agent still has it. |
newConversation | Boolean | Whether the header offers New conversation; off, chat.newConversation() still works. |
GraineThemeTokens.Call
Read it to draw a call screen of your own the way the agent's design does. data class Call(…), as GraineThemeTokens.call: the Garage's call screen, resolved. Colours are #RRGGBB.
| Field | Type | Meaning |
|---|---|---|
background | String | The screen's fill — the gradient's top when backgroundTo is set. |
backgroundTo | String? | The gradient's bottom; null for a solid background. |
text | String | The name and the agent's caption. |
textMuted | String | The status line, button labels and the user's caption. |
avatar | String | What the avatar shows: logo, glyph or initials. |
avatarShape | String | circle, squircle or rounded-square. |
avatarFill | String | Behind a glyph or initial. |
avatarText | String | The glyph or initial. |
buttonFill | String | A control's fill (mute, loudspeaker). |
buttonIcon | String | A control's icon. |
buttonActiveFill | String | A control's fill while it is on (muted, the loudspeaker). |
buttonActiveIcon | String | A control's icon while it is on. |
endFill | String | The end-call button's fill. |
endIcon | String | The end-call button's icon. |
endShape | String | The end-call button: round or pill. |
acceptFill | String | The Answer button's fill. |
acceptIcon | String | The Answer button's icon. |
declineFill | String | The Decline button's fill. |
declineIcon | String | The Decline button's icon. |
callbackFill | String | The Call back button's fill, on a missed call. |
callbackIcon | String | The Call back button's icon. |
captions | Boolean | Whether the screen shows the line being said. |
timer | Boolean | Whether it shows how long the call has lasted. |
controls | List<String> | The in-call controls, in order, from mute, speaker, chat and end. |
ringtone | String | The ring: system (the phone's), graine or soft. |
statusText | StatusText | The screen's words (GraineThemeTokens.Call.StatusText). |
GraineThemeTokens.Call.StatusText
Read it for the brand's words on the call screen. data class StatusText(val ringing: String, val connecting: String, val ended: String, val scheduled: String?, val missed: String?).
| Field | Type | Meaning |
|---|---|---|
ringing | String | While an agent's call rings, such as “Incoming call”. |
connecting | String | While the call connects, such as “Connecting…”. |
ended | String | Once a call is over, such as “Call ended”. |
scheduled | String? | While a call Graine rang at a booked time rings; null where the design leaves the SDK's own, “Scheduled call”. |
missed | String? | For a ring nobody answered; null where the design leaves the SDK's own, “Missed call”. |
Going live
- Debug logging.
GraineConfig(debug = true)in debug builds logs the SDK's traffic with Graine — keys only, never values — to logcat (tagGraine) and the iOS console. Leave it off in release builds. - Android, Google Play. On Android 14 and up, Play asks apps that declare
USE_FULL_SCREEN_INTENTor the microphone foreground service to justify them; the call screen needs both. An app that never uses calls removes them from its manifest (see the Android reference). - iOS. Calls need the Voice over IP and Audio background modes and push notifications; Graine rings only once your VoIP credentials are uploaded in Workspace settings → In-app calling.
- The version.
GraineKmpVersion.NAMEsays which build of the library the app runs; include it when you ask for support.
GraineKmpVersion
Read it to know which version of the library your app runs — include it when you ask for support.
object GraineKmpVersion
What to expect — The same on Android and iOS.
| Property | Type | Meaning |
|---|---|---|
const val NAME: String | String | The library's version: "0.10.3" in this release. |
Events and listeners
The states are flows that always hold the latest value — Graine.session, Graine.theme, Graine.chat.turns, Graine.chat.status, Graine.chat.upcomingCalls, Graine.voice.state, Graine.calls.state — and the moments are shared flows that are not replayed: Graine.events (each agent's own events), Graine.calls.outcomes and Graine.calls.incoming. Collect the shared flows from app start for what you log or count.
GraineEvent
Collect it from Graine.events to hear what the agent reports that is not a state.
sealed class GraineEvent
What to expect — Not replayed. A when over it needs an else, so an event added later does not break your build.
| Case | Meaning |
|---|---|
SessionReady(session: GraineSession) | The agent's settings arrived (also in Graine.session) — offer what depends on them. |
Caption(role: GraineRole, text: String, live: Boolean) | A line of the voice bar's call: the agent's as it is spoken, the user's as it is heard; live while it is still arriving. |
VoiceEnded | The voice bar's call is over, however it ended. |
Component(component: GraineComponent) | The agent drew a component, in the chat or on a call — for an app that draws components itself. |
ComponentPending(hint: Map<String, Any?>) | A component is on its way and its content is loading — draw a loader. |
ChatMessage(turn: GraineChatTurn) | The agent finished a message in the chat; turn.proactive means it spoke up unasked — badge your chat if it is closed. |
ActionRequested(name: String) | The agent asked for one of your actions. |
ActionCompleted(name: String, result: GraineActionResult) | What one of your actions answered the agent — your handler's result, or the SDK's when it was late or unknown. |
Friction(name: String, screen: String?, detail: String) | The user seems stuck (stalled_on_step): during a live conversation they have stayed on one screen for a while with a field still to fill. The agent has already been told; badge a control or count it. |
MicrophoneDenied(reason: String) | The microphone was refused, so the agent cannot hear the user — explain how to allow it. |
Error(message: String) | Something is misconfigured (a key, an allowed domain). Log it for your team; it is never the user's problem and is not shown to them. |
Errors
A suspending function that asks Graine on the user's behalf throws GraineException carrying a GraineErrorCode. Branch on the code; show the message, which is written for the user and never carries the identifier, traits or a push token. Calling anything before Graine.configure throws IllegalStateException instead — a mistake in the app, not a refusal.
GraineException
Catch it around a request to Graine to decide what to do about a refusal.
class GraineException(val code: GraineErrorCode, message: String, val retryAfterSeconds: Int? = null) : Exception(message)
What to expect — code is the stable part — branch on it, never on the message. A refusal made on the device before anything was sent carries the same codes.
| Property | Type | Meaning |
|---|---|---|
val code: GraineErrorCode | GraineErrorCode | Branch on it: the reason in one stable word. |
message | String | Show it: Graine's sentence when it sent one, written for the user. |
val retryAfterSeconds: Int? | Int? | Use it to wait before trying again: how long Graine asked (Retry-After), with RateLimited; null when it did not say. |
GraineErrorCode
Read it to branch on why a request was refused.
sealed class GraineErrorCode(val wire: String)
What to expect — A code newer than this SDK arrives as Unknown(raw), with Graine's word in it.
| Property | Type | Meaning |
|---|---|---|
wire | String | Graine's word for the code — too_soon, network, … — for logs. |
| Case | Meaning |
|---|---|
InvalidKey | The publishable key was refused. Check it against Embed & Widgets. |
OriginRefused | The agent does not allow apps yet: add app:// to its Allowed domains. |
RateLimited | Too many requests; retryAfterSeconds says when to try again, when Graine said. |
InvalidRequest | A field was missing or malformed — a blank call ID, say. |
IdentityRequired | Nobody is signed in: identify() the user with the trait the agent's Who is the user? setting names. |
DeviceLimit | The user has already reached the agent's limit of devices. |
SchedulingOff | Booking is switched off for this agent (Calls & scheduling). |
TooSoon | Sooner than the agent's minimum notice. |
TooFar | Further ahead than the agent allows. |
OutsideWindow | The time falls outside the agent's calling hours, in the user's time zone. |
TooManyPending | The user has already booked the most calls the agent allows. |
NotFound | No such booking for this user or install. |
Unavailable | Graine could not answer just now. Worth trying again later. |
Network | Nothing came back: the phone is offline, or Graine could not be reached. Worth trying again. |
Unknown(raw) | A code newer than this SDK, or none at all ("unknown"), carrying the word. |
Its functions: from — each with its parameters, what it returns, its errors and an example.
| Code | wire | HTTP | Means | Raised by | Fix |
|---|---|---|---|---|---|
InvalidKey | invalid_key | 401 | The publishable key is not one, or belongs to no agent | Every request to Graine | Copy the key again from Agent → Embed & Widgets → Install |
OriginRefused | origin_refused | 403 | app:// is missing from the agent's Allowed domains | Every request | Add app:// |
RateLimited | rate_limited | 429 | Too many requests from this install, key or address | Every request | Wait retryAfterSeconds, then retry |
InvalidRequest | invalid_request | 400 | A field is missing or malformed | scheduleCall, cancelScheduledCall | Fix the request |
IdentityRequired | identity_required | 400 | Nobody is identified, or the identifier is a placeholder | memories, forgetMemory, bookings that need a user | identify() with the identifier first |
DeviceLimit | device_limit | 403 | This install is not one of the user's allowed devices | memories, forgetMemory, newConversation, scheduleCall | Sign out on another device, raise Devices per user, or Reset devices for the user |
SchedulingOff | scheduling_off | 403 (on the device first) | Users may not book from the app | scheduleCall | Turn it on, or hide booking when bookingAvailable is false |
TooSoon | too_soon | 400 (on the device first) | Earlier than earliest(now) | scheduleCall | Offer times from earliest(now) |
TooFar | too_far | 400 (on the device first) | Later than latest(now) | scheduleCall | Offer times up to latest(now) |
OutsideWindow | outside_window | 400 | The time falls outside the agent's calling hours, in the user's time zone | scheduleCall | Show the message (it names a time that works); offer another time |
TooManyPending | too_many_pending | 409 | The user already has the most calls waiting | scheduleCall | Cancel one first |
NotFound | not_found | 404 | No such booking for this user or install | cancelScheduledCall | Read scheduledCalls() again |
Unavailable | unavailable | 503 (504 on a timeout) | Graine could not answer just now — including a booking refused because the agent is switched off or out of credit | Every request | Retry with backoff; check the agent is on and the workspace has credit |
Network | network | — | No answer came back: offline, or the connection dropped | Every request | Retry when online |
Unknown(raw) | the word | other | A code newer than the SDK | Every request | Log the code and message; show the message |
When the bar or chat cannot start, the reason arrives as GraineEvent.Error, not as an exception: key_missing, key_malformed (publishable keys start with pk_live_), key_unknown (copy the key again), no_origin (add app://), no_domains_configured (the list is empty), origin_not_allowed, and not_configured (high-quality audio is not enabled for the workspace; calls use the standard path). A refused microphone arrives as GraineEvent.MicrophoneDenied.
Rate limits and quotas
| Limit | Value | Where it applies |
|---|---|---|
| Bookings from one install | 10 an hour | scheduleCall, the booking sheet, and the agent's bookings in that install's conversations |
| Bookings in a workspace | 1,000 an hour | All in-app bookings |
| Calls waiting per user | 3 by default (1–10) | At most this many upcoming calls per user |
| Devices per user | 2 by default (1–5) | Devices per user; idle devices free their place after 30 days |
| Rings per user per day | 3 by default (1–10) | Ring each user at most, in the user's own day |
| Quiet period after a decline | 6 hours | No ring for that user |
| Notice and horizon | 5 minutes (2–1440) · 14 days (1–30) | With at least this much notice · Booked at most this far ahead |
| Ring time · re-ring | 45 s (20–60) · once after 10 min (0–120) | Ring for · After a missed call, ring once more after |
| Lateness | 10 minutes | A booked call more than 10 minutes late is not rung |
| Booking reason · variables | 120 characters · 12 variables, about 1 KB | Bookings and calls |
| Memories listed | At most 100, newest first | memories() |
| Conversation kept | 50 messages, 7 days after the last | Conversations that continue |
| Action handlers | 6 seconds | After that, the agent is told the action was not done |
Lower-level building blocks
Reading Graine's own words into the SDK's types — for a server or a bridge that passes them around. An integration does not need them.
GraineCallStatus.from
Call it to turn a status word from Graine (or your server) into a GraineCallStatus.
Case and surrounding spaces do not matter. No word at all reads as Scheduled, as the native SDKs read it.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
wire | String? | required | Graine's word, such as "no_device". |
Returns — GraineCallStatus — a known status, or Unknown(raw). Example value: GraineCallStatus.NoDevice.
Errors — none.
GraineCallSource.from
Call it to turn a source word from Graine into a GraineCallSource.
sdk and app are App; case does not matter.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
wire | String? | required | Graine's word, such as "sdk". |
Returns — GraineCallSource? — null for no word at all. Example value: GraineCallSource.App.
Errors — none.
GraineErrorCode.from
Call it to turn an error word from Graine (or a native SDK) into a GraineErrorCode.
Case does not matter; a blank word is Unknown("unknown").
| Parameter | Type | Default | Meaning |
|---|---|---|---|
wire | String? | required | The word, such as "too_soon". |
Returns — GraineErrorCode — a known code, or Unknown(raw). Example value: GraineErrorCode.TooSoon.
Errors — none.
GraineCallTransport.ofCall
Call it to turn a platform SDK's word for what carried a call into a GraineCallTransport — the way GraineCallInfo.transport is read.
Case does not matter. Only what a call can run on is read: auto is something an app chooses, never what carried a call.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
wire | String? | required | The word, such as "webrtc". |
Returns — GraineCallTransport? — WEBRTC or WEBSOCKET; null for any other word, or none. Example value: GraineCallTransport.WEBRTC.
Errors — none.
What's new in 0.9.0
- WebRTC calls. Add each platform's WebRTC module to the app and native calls — the ones the person places and the ones Graine rings — carry their audio over WebRTC: steadier on a weak network, with the same ring, call screen and outcomes.
GraineCallsConfig(callTransport = …)chooses (GraineCallTransport);GraineCallInfo.transportandtransportFallbackReasonsay which one carried a call (WebRTC calls). - The brand's design tokens in shared code:
Graine.theme(and each agent'stheme), aGraineThemeTokens, following the phone between light and dark. Graine.currentVariables()(and each agent's): every prompt variable the next conversation carries, merged in precedence order.- The chat:
clear()empties the screen only;upcomingCalls,refreshUpcomingCalls()andcancelUpcomingCall(id)give a chat of your own the Upcoming calls card;answerWidget(id, data, summary)answers a component you draw yourself. - What was said on a voice-bar call, and the captions switch:
Graine.voice.statecarriescaptions(the last 12 lines) andcaptionsOn;setCaptionsOn(on)andclearCaptions(). - On Android,
chat.statusisRECONNECTINGwhenever the Android SDK is making the chat again — after a first try that failed too, as on iOS. scheduleCall's own refusals say the booking sheet's words: “Booking a call isn't available right now”, “Pick a time at least 5 minutes from now”, “Pick a time within 14 days”.- On iOS, link
GraineInAppof this version (andGraineInAppRTCat the same version): with an older one the new chat, voice-bar, design and variables members stay empty or do nothing. - The full list of changes is in the Changelog.
What's new in 0.8.0
- Booking a call, drawn by the SDK.
Graine.presentBooking(reason)(andGraineAgent.presentBooking) shows the native SDK's booking sheet — the day, the time (each platform's own time picker), the phone's time zone, What's it about? (optional) and Book call — in the agent's design, and returns the booked call, ornullwhen the user closed it without booking. Refusals stay in the sheet as one line. bookingAvailable, onGraineand each agent: Graine rings this phone, the agent lets users book, and the user is identified. Show your own “Call me” button only then.- On iOS the sheet is
GraineInApp's, so linkGraineInApp0.8.0: with an older one,bookingAvailableisfalseandpresentBookingthrowsUnavailable, and the app keeps running. - On iOS, VoiceOver now reaches into
GraineChat(),GraineCallScreen()andGraineAgentBar(). - The SDK's chat windows (
GraineChat(),GraineViews.chat()) offer Book a call in their Upcoming calls card by the same rule. - Calls hold up on weak networks (the native SDKs, 0.8.0): on Android a slow uplink no longer holds the call up, hanging up lets go of the microphone at once, and a connection that has gone silent for 50 seconds ends the call; on iOS a call that cannot connect fails within 20 seconds, and one whose audio never starts says so.
- The full list of changes is in the Changelog.
Every symbol, A to Z
Every public symbol of the Kotlin Multiplatform SDK — 544 in all — by name, each linked to where this page documents it: a type or function first, then its members.
Graine—agent·agents·bookingAvailable·calls·cancelScheduledCall·chat·clearScreen·configure·currentVariables·DEFAULT_AGENT·events·forgetMemory·identify·installId·isConfigured·memories·memoryEnabled·noteInteraction·onRequestPermissionsResult·presentBooking·registerAction·removeField·scheduleCall·scheduledCalls·schedulingRules·session·setMemory·setScreen·setVariables·tap·theme·track·updateField·voiceGraineActionResult—data·error·message·ok·refused·statusGraineActionResult.Status—ERROR·OK·REFUSED·wireGraineAgent—bookingAvailable·cancelScheduledCall·chat·currentVariables·events·forgetMemory·isConfigured·memories·name·presentBooking·registerAction·scheduleCall·scheduledCalls·schedulingRules·session·setVariables·startCall·theme·voiceGraineAgentBarGraineCallInfo—agent·agentSpeaking·callerName·callId·caption·hosted·incoming·missedCall·muted·showsCallScreen·speakerOn·state·transport·transportFallbackReasonGraineCallOutcome—agent·callId·durationSeconds·hosted·outcome·reasonGraineCallOutcome.Kind—ANSWERED·DECLINED·ENDED·FAILED·MISSED·RINGING·wireGraineCallPermission—FULL_SCREEN·MICROPHONE·NOTIFICATIONS·of·wireGraineCallPermissions—allGranted·fullScreen·microphone·notificationsGraineCallPermissionStatus—BLOCKED·DENIED·GRANTED·isSatisfied·NOT_NEEDED·of·wireGraineCalls—answer·callBack·callPermissions·callSetupProblems·configure·decline·dismissMissedCall·enableVoIPPushes·end·handlePush·incoming·onNewToken·openCallSettings·outcomes·requestCallPermissions·setMuted·setSpeaker·startCall·stateGraineCallsConfig—callTransport·displayName·includeInRecents·ringTimeoutSeconds·ringtone·smallIconGraineCallScreenGraineCallSetupProblem—code·messageGraineCallSource—Agent·Api·App·Dashboard·from·Unknown·wireGraineCallState—ACTIVE·CONNECTING·ENDED·IDLE·RINGINGGraineCallStatus—Answered·Cancelled·Completed·Declined·Expired·Failed·from·Missed·NoDevice·Ringing·Scheduled·Unknown·wireGraineCallTransport—AUTO·ofCall·WEBRTC·WEBSOCKET·wireGraineCaption—live·role·textGraineChat—answerWidget·cancelUpcomingCall·clear·close·newConversation·open·refreshUpcomingCalls·retry·send·status·turns·typing·upcomingCallsGraineChatChannel—CHAT·VOICE·wireGraineChatDay—DATE·TODAY·YESTERDAYGraineChatRow—Day·key·Turn·VoiceCallGraineChatRows—build·callLabel·dayLabel·offersNewConversation·offersStartersGraineChatSize—COMPACT·FULL·LARGE·wireGraineChatStatus—CONNECTING·IDLE·OFFLINE·ONLINE·RECONNECTINGGraineChatTurn—at·channel·component·delivery·id·live·proactive·restored·role·textGraineComponent—id·kind·payload·titleGraineConfig—appVersion·baseUrl·captureTaps·chatSize·debug·endOnBackground·publishableKey·transport·variablesGraineDelivery—FAILED·SENDING·SENTGraineErrorCode—DeviceLimit·from·IdentityRequired·InvalidKey·InvalidRequest·Network·NotFound·OriginRefused·OutsideWindow·RateLimited·SchedulingOff·TooFar·TooManyPending·TooSoon·Unavailable·Unknown·wireGraineEvent—ActionCompleted·ActionRequested·Caption·ChatMessage·Component·ComponentPending·Error·Friction·MicrophoneDenied·SessionReady·VoiceEndedGraineException—code·retryAfterSecondsGraineField—error·label·name·optional·status·valueGraineFieldStatus—EMPTY·FILLED·INVALID·LOCKED·PENDING·wireGraineIncomingCall—agent·callerName·callId·hosted·reasonGraineInitProviderGraineKmpVersion—NAMEGraineMemory—createdAt·id·text·updatedAtGraineMissedCall—at·callerName·callId·hosted·reasonGrainePlatform—ANDROID·IOS·wireGraineRole—AGENT·USERGraineScheduledCall—at·canCancel·id·isUpcoming·reason·source·statusGraineSchedulingRules—callName·earliest·hostedRinging·latest·maxDaysAhead·maxPendingPerUser·minLeadMinutes·platforms·ringTimeoutSeconds·usersCanBookGraineScreen—availableActions·data·error·fields·journey·screen·titleGraineScreen.Journey—name·next·of·stepGraineSession—agentId·declaredActions·devicesPerUser·identityField·memoryEnabled·schedulingRulesGraineThemeTokens—bubbles·call·chat·chrome·colors·elevation·font·glass·icons·isDark·motion·preset·scheme·shape·spacing·vGraineThemeTokens.Call—acceptFill·acceptIcon·avatar·avatarFill·avatarShape·avatarText·background·backgroundTo·buttonActiveFill·buttonActiveIcon·buttonFill·buttonIcon·callbackFill·callbackIcon·captions·controls·declineFill·declineIcon·endFill·endIcon·endShape·ringtone·statusText·text·textMuted·timerGraineThemeTokens.Call.StatusText—connecting·ended·missed·ringing·scheduledGraineThemeTokens.Chat—avatar·background·composerBackground·composerBorder·composerPlaceholder·composerText·headerBackground·headerSubtext·headerText·newConversation·pattern·patternColor·placeholder·showHistory·showTimestamps·showUpcomingCalls·size·subtitle·suggestions·title·upcomingTitleGraineThemeTokens.Colors—accent·accentSoft·agentBubble·agentBubbleText·background·border·danger·onAccent·scrim·success·surface·surfaceRaised·text·textMuted·userBubble·userBubbleText·warningGraineThemeTokens.Elevation—card·floating·hardOffsetGraineThemeTokens.Font—body·heading·letterSpacing·lineHeight·scale·weightsGraineThemeTokens.Font.Pair—body·titleGraineThemeTokens.Font.Scale—body·caption·display·titleGraineThemeTokens.Font.Weights—body·strong·titleGraineThemeTokens.Glass—blur·surfaceOpacityGraineThemeTokens.Glyph—id·path·strokeGraineThemeTokens.Icons—launcher·stroke·styleGraineThemeTokens.Motion—bounce·durationMs·easingGraineThemeTokens.Offset—x·yGraineThemeTokens.Shadow—blur·color·opacity·spread·yGraineThemeTokens.Shape—borderWidth·launcher·radiusGraineThemeTokens.Shape.Radius—lg·md·sm·xlGraineThemeTokens.Spacing—cardPadding·gap·unitGraineTransport—WEBRTC·WEBSOCKET·wireGraineViews—callScreen·chat·voiceBarGraineVoice—clearCaptions·end·setCaptionsOn·setMuted·start·stateGraineVoiceInfo—captions·captionsOn·connected·connecting·muted

