Packages
@akapulu/react@akapulu/react-ui@akapulu/server
Quickstart (React)
AkapuluProvider takes paths to your local API routes:
connectPath: your local connect endpointupdatesPath: your local updates endpoint
@akapulu/server.
Backend connect and updates routes
Your localconnect route should:
- call
akapulu.connectConversation(...)with the Akapulu Labs connect schemascenario_id: stringavatar_id: stringruntime_vars?: Record<string, string>stt_keywords?: string[]record_conversation?: boolean
- return the connect response fields required by the SDK:
room_urltokenconversation_session_id
updates route should:
- read
conversation_session_idfrom the query param - call
akapulu.pollConversationUpdates(conversationSessionId) - return the updates payload fields required by the SDK:
call_is_readycompletion_percentlatest_update_textconversation_session_id
updatesPath with conversation_session_id in the URL query string.
Install @akapulu/server in your backend and create two routes:
POST /api/akapulu/connectGET /api/akapulu/updates?conversation_session_id=...
AkapuluProvider to those local paths:
connectPath: "/api/akapulu/connect"updatesPath: "/api/akapulu/updates"
Browser vs server
-
In the browser:
AkapuluProvider(viaconfig.endpoints.connectPathandconfig.endpoints.updatesPath) requests your connect and updates backend routes. -
On your server: those routes use
@akapulu/server, read the Akapulu Labs API key from server-side environment variable, and call the Akapulu Labs HTTP API. That way your api key stays on the server and does not get exposed in the browser
AkapuluProvider expects your local connect and updates endpoints to return the JSON schema described under Backend connect and updates routes above.
Passing custom payload to your local connect route
If you want,AkapuluProvider can send a custom JSON payload to your local connectPath route via config.connectBody.
- If
connectBodyis provided, the SDK sends it as the POST JSON body to your local connect route. - If
connectBodyis omitted, the SDK sendsPOST /connectPathwith no body.
Styling AkapuluConversation
@akapulu/react-ui supports slot-level customization via:
classNamefor the rootclassesfor slot class namesstylesfor slot inline style overrides
Example 1: Slot key overrides (classes + styles)
Use slot keys when you want to target specific UI parts directly from React props.
Example 2: Default class overrides (global CSS)
Use built-in default classes when you want to apply theme-like global styles from CSS.Example 3: data-slot overrides (stable CSS selectors)
Use data-slot selectors when you want explicit, inspectable selectors in DevTools.
Slot map
Each slot has both a default class and adata-slot marker so users can inspect and target it in DevTools without guessing.
Layout
Loading
Error modal
Tool events
Video + controls
Transcript
Behavior customization (handlers + custom elements)
Beyond styles,@akapulu/react-ui lets you customize behavior and rendering for transcript/tool events directly on AkapuluConversation.
Built-in handler props on AkapuluConversation
transcriptFilter(entry)to hide transcript rowsrenderTranscriptEntry(entry)to render transcript rows with your own JSXonToolEvent(tool)to run side effects when a tool event arrivesrenderToolEvent(tool)to replace the default tool toast elementtoolEventTimeoutMsto control how long the tool toast stays visible
toolEventTimeoutMs behavior:
- default:
4000 - pass a number to customize the auto-hide timeout
- pass
nullto disable auto-hide
TranscriptEntry shape (entry):
NormalizedToolEvent shape (tool) by tool type:
RAG tool event
vision tool event
http tool event
Handling all conversation events while keeping prebuilt UI
For full event handling (node changes, bot speaking state, transcript updates, tool calls, and timeout), add a small sibling listener component that usesuseAkapuluEvents.
event.type:
status_changed
bot_speaking_state_changed
node_changed
tool_event
transcript_updated
call_ready
timeout
Using @akapulu/react without @akapulu/react-ui
The same connect/updates route pattern applies: AkapuluProvider points at your local connectPath and updatesPath, your server uses @akapulu/server. What changes is UI: you build layout yourself and pull state from hooks instead of mounting AkapuluConversation.
How it fits together
- Wrap your tree in
AkapuluProvider(as in the Quickstart).
-
Call
useAkapuluSession()anywhere under that provider. It exposes everything in the session store plusstart/end: Lifecycle & connectionstatus— where the client is in the join/leave flow:"idle"→"connecting"→"connected"→"disconnecting"/"ended", or"error"if something failed.start/end— async actions that begin the conversation (connect + Daily join) or hang up and reset session state.error— whenstatusis"error", structured details (codeoptional,messagerequired) for your error UI or logging.
callIsReady— whether the backend considers the call ready (often used whilestatus === "connecting"so you are not stuck on a spinner forever).completionPercent— numeric progress through the scenario (0–100).latestUpdateText— short human-readable status line for loading/progress copy.
currentNode— the active scenario node ({ key, label }) ornullif none.
transcripts— ordered list of rows (id,text,speaker,timestamp,isFinal) for your own transcript UI.botSpeakingState—"idle","speaking", or"listening"for indicators or turn-taking UI.
conversationSessionId— Akapulu Labs conversation session id from connect (used when polling updates; also useful if your app logs or links out to dashboard/API records).
- The provider joins the Daily room for realtime media. Install
@daily-co/daily-react(and peer@daily-co/daily-js) and use its primitives to draw video—for exampleDailyVideo,useDaily, anduseVideoTrack. For assistant video selection, useuseAkapuluParticipantRolesfrom@akapulu/react.
- Use
useAkapuluMediaControls()for in-call mic and camera toggles (wired to that Daily session).
- Render
<AkapuluBotAudio />once in the tree so assistant audio plays (small hidden element; required for typical voice/video bots).
- Optionally
useAkapuluEvents(callback)to react to the full event stream (transcript_updated,tool_event,node_changed,timeout, etc.) while still rendering your own UI.
@akapulu/react
Ending a call: When your user ends the call in your app, call
end() from useAkapuluSession(). That leaves the Daily room from the browser and runs the SDK teardown so the session is no longer live. If the assistant disconnects from Daily first, AkapuluProvider calls end() for you so status does not stay connected with no assistant in the room.
Types and shared logic
Use AkapuluEvent from @akapulu/react. For TranscriptEntry, NormalizedToolEvent, and related handler shapes on AkapuluConversation, align with the prop types exported from @akapulu/react-ui.
Conversation detail and recording
Separate from the live call connect and updates loop,@akapulu/server can fetch post-call (or in-progress) conversation detail and recording responses from your backend—for example dedicated HTTP handlers your UI calls after conversation_session_id is known.
Conversation detail retrieval
@akapulu/server also exposes getConversationDetail(conversationSessionId) for fetching a completed or in-progress conversation detail payload from your backend.
ConversationDetailResponse from @akapulu/server):
avatar
recording (detail payload)
transcript_rows[]
Conversation recording retrieval
@akapulu/server also exposes getConversationRecording(conversationSessionId) for fetching the recording response for a conversation from your backend.
ConversationRecordingResponse from @akapulu/server):
kind: "redirect"
kind: "json"
kind: "binary"
Examples
Runnable apps that mirror this guide are available as standalone Vite + Express repos. Each has an Expressbackend/ with @akapulu/server route handlers for POST /api/connect and GET /api/updates?conversation_session_id=…, plus a React frontend/.
-
Prebuilt UI —
Akapulu/prebuilt-ui:@akapulu/react(AkapuluProviderwith localconnectPath/updatesPath) plus@akapulu/react-ui(AkapuluConversationfor the full default layout: video, transcript, controls). Useful when you want to ship quickly and customize via props or CSS rather than rebuilding layout. -
Prebuilt UI (styled) —
Akapulu/prebuilt-ui-styled: the same prebuilt path with a dark theme, a custom tool toast, and a post-call review screen (recording + transcript). -
Customized UI —
Akapulu/customized-ui:@akapulu/reactonly—same provider pattern, thenuseAkapuluSession,useAkapuluMediaControls,useAkapuluParticipantRoles,useAkapuluEvents,AkapuluBotAudio, and@daily-co/daily-react(DailyVideo, track hooks) for your own chrome. Useful when you need full control over markup and state wiring.

