Graine AI

Voice

The assistant bar's voice on every platform — starting, ending and muting a call, every state member, the launcher, the bar and the component views with their props, React Native's voice hooks, and the website engine's messages — with types, defaults and threading.

The bar's voice runs on the in-app engine — how it connects, its transports and recovery are on Voice; drawing your own call UI from these members is on Your own UI. Native calls, which ring on the lock screen, are on Calls.

Start, end, mute

<GraineVoiceLauncher webView={WebView} active={open} autoStart>
  {(api) => <MyBar onTalk={api.start} onEnd={api.end} onMute={() => api.setMuted(!api.muted)} />}
</GraineVoiceLauncher>
// api.start(): void · api.end(): void · api.setMuted(muted: boolean): void · api.retry(): void

active={false} also ends a live call, and loads the engine idle so a later start is instant.

Starting asks for the microphone if it is missing (in Flutter through your requestMicrophone), and a start before the engine is ready is held and placed when it is. A refused microphone places no call and reports micDenied. Only one conversation runs at a time: starting the bar during a native call is refused with an error.

State

WhatReact Native (api)iOSAndroidFlutterValues
On a call / placing oneconnected, connectingconnected, connectingconnected, connectingconnected, connectingBool
A call can start nowreadyreadyreadyreadyBool
MutedmutedmutedmutedmutedBool
One word to draw fromphasephase (GrainePhase)phase (CallPhase)from connected, agentSpeakingidle · connecting · reconnecting · thinking · speaking · listening
Where a connect isconnectStage, slowConnect, callFailedthe samethe samethe sameregistering · dialling · retrying · switching; slowConnect after 4 s
The linequality, network, reconnectingquality, online, reconnectingquality, online, reconnectingquality, online, reconnectingexcellent · good · poor · lost · unknown
The engine pageconnectivityconnectivityconnectivityconnectivityloading · ready · retrying · failed
What was saidcaptions (12 kept), captioncaptionscaptions (12 kept, GraineCaption), and onCaptioncaptions, captionrole, text, live
Captions switchcaptionsOn, setCaptionsOn, clearCaptions()captionsOn, setCaptionsOn(_:), clearCaptions()captionsOn, setCaptionsOn(on), clearCaptions()captionsOn, setCaptionsOn(on), clearCaptions() (GraineCaptionsSwitch)On by default; the SDK's bar hides its caption while it is off; what was said is kept
Microphone refusedmicDeniedvia onMicDeniedvia onMicDeniedmicDenied
A setting must be fixederrorvia onErrorerrorerror

Value meanings: Connection values. Kotlin Multiplatform has the same in Graine.voice.state: captionsOn and captions, with setCaptionsOn(on) and clearCaptions().

The session and the brand's design

React NativeiOSAndroidFlutterKotlin Multiplatform
The agent's settings have loadeduseGraineReady() → { ready, error } (also as useGraineInit() → { isInitialized, error })session: GraineSessionConfig?; onSessionReadysession: SessionConfig?; onSessionReadysession, sessionListenableGraine.session: StateFlow<GraineSession?>
The brand's design tokens, for UI of your ownuseGraineTheme(): ThemeTokens — works outside a provider tooonTheme(_:)themeTokens(context?, scheme?): ThemeTokens?tokens, tokensListenableGraine.theme: StateFlow<GraineThemeTokens?> (each agent's theme)
Try a design before it is saved——previewDesign(design); null goes back to the agent's——
The engine is mounted—voice.isAttachedvoice.attachedvoice.attached—
Your own UI showing the voice opened or closedthe launcher's activevoice.viewOpened(), voice.viewClosed()voice.viewOpened(), voice.viewClosed()voice.viewOpened(), voice.viewClosed()—

ready (or isInitialized) turns true once the agent's settings have been fetched — its look, its declared actions, how to connect — and error carries the first thing that went wrong: show a splash until then, or an error state when the publishable key is wrong. Do not infer it from connected, which stays false on a voice-only screen until someone taps the microphone. The settings' fields: Session settings.

The tokens are the colours, type, corners and depth the SDK draws with, for the device's current light or dark appearance, as plain numbers and #RRGGBB strings; they change when the device switches. In Flutter, iOS and Android, tell the voice when a UI of yours that shows it opens and closes (viewOpened(), viewClosed(); React Native: the launcher's active): a slow or failed connect is only reported while one is open.

The launcher (React Native)

GraineVoiceLauncher owns the engine page and the call.

PropTypeDefault
webViewcomponentrequiredWebView from react-native-webview
activebooleantrueMount early with false; true when your UI opens. false ends a live call
autoStartbooleanfalsePlace the call when the page is ready and the launcher is active
transport"ws" or "rtc"the dashboard'sForce a transport, for testing
timingobjectstallMs 15000 · bootMs 15000 · hardCapMs 120000 · retryDelaysMs 1, 2, 4, 8, 15, 15 s · endWatchdogMs 4000 · slowConnectMs 4000
onCaption(caption) => voidEach line as it is said
onCallState({ connected, connecting }) => void
onEnded() => voidOnce per call
onMicDenied(reason) => voidNotAllowedError or NotFoundError
onError(message) => voidVoice cannot work until a setting is fixed
onTransport({ transport, requested, reason, message }) => voidA fallback, and why
children(api) => ReactNodeThe voice api: the state above, start, end, setMuted, retry

The bar and the launcher button

ComponentProps
GraineVoiceBarapi (required) · onClose · bottomInset (default 96) · draggable (default true) · placement / onPlacementChange ({ bottomOffset }) · name · accessory
GraineBarActionlabel (required) · icon ("call" or "chat", default "call") · onPress (required)
GraineLauncherButtononPress (required) · style · label (default "Talk to" + the heading) · size (default 56)
GraineAgentBarname, accent, avatarUrl · bottomInset · hidden · captionsOn · scheme (auto/light/dark) · theme · draggable · placement / onPlacementChange
GraineLoadermoment (connecting, reconnecting, thinking, loading) · label · kind · size · color
GraineWidgetDraws the agent's components where you place it

Multi-agent API: with several agents, the bar takes the agent it draws — iOS GraineAgentBar(graine: GraineInApp.named("sales")), Android agentBar.graine = GraineInApp.get("sales"), Flutter GraineAgentBar(app: GraineInApp.named('sales')), Kotlin Multiplatform GraineAgentBar(agent = "sales"); in React Native, the nearest provider's. Each agent has its own voice; one conversation runs at a time across all of them.

React Native hooks for voice

HookReturns
useGraineConnection(){ quality, online: boolean | null, network: "online" | "offline" | "unknown" }
useKeyboardLift()number — pixels to add to a bottom-anchored bar while the keyboard is up
useDraggablePlacement({ baseBottom, initialOffset, onChange }){ offset, panHandlers, onLayout } — spread on your own bar
useGraineVoice(adapter){ listening, start, stop } — only if you bring your own native audio: 16 kHz mono PCM16 in, with the provider's voice prop
useGraineLoader()The loader's look and the accent, for a loader of your own

The website engine's messages

A page that hosts the engine itself (rather than the loader's widget) drives it with messages — the full list is on Any other platform.

Out of the pagePayload
graine:ready— the page is mounted
graine:call{ connected, connecting }
graine:caption{ role, text, live }
graine:progress{ stage } — registering, dialling, retrying, switching, failed
graine:quality{ sample: { rtt, jitter, loss, quality } }
graine:network, graine:reconnecting{ online }, { active }
graine:mic-denied{ reason }
graine:transport{ transport, requested, reason, message }
graine:need-identity, graine:need-ticketThe page needs the visitor's identity, or a ticket from your origin
graine:ended— once per call

On this page