Skip to main content
The prebuilt widget covers the common case. When you need your own UI - a full-page chat, a custom panel, native React components - use the headless client or the React bindings directly. Both drive the same streaming core; they do not reimplement it.

Headless: @karta/widget

KartaAgentClient is a transport- and UI-agnostic client. Zero runtime dependencies, ESM-only, strict TypeScript. It talks to the Managed Agents consumer API by default (the richest token-authed surface: decoupled POST-event / GET-stream gives reload-resume and tool confirmation), with the OpenAI Responses API as a portable fallback.
Assistant message text is cumulative - each event carries the full message so far. Replace the rendered bubble on every event; never concatenate.

Methods

  • createSession(): Promise<{ sessionId }> - open a Managed Agents session.
  • sendMessage(text): AsyncGenerator<AgentEvent> - ensure a session, post the message, stream the turn until done/error.
  • stream(fromSeq?): AsyncGenerator<AgentEvent> - low-level resume of an in-flight turn from a cursor.
  • resume(sessionId): Promise<AgentEvent[]> - adopt an existing session, rebuild its cursor from event history, and return its prior events (normalized AgentEvents) so a UI can rebuild the transcript.
  • listSessions(): Promise<SessionSummary[]> - the end-user’s past conversations, newest first, for a session sidebar.
  • openSession(sessionId): Promise<TranscriptMessage[]> - reopen a past conversation: load its transcript for display and adopt the session so the next sendMessage continues it.
  • newSession(): void - drop the active session so the next sendMessage opens a fresh one.
  • confirmTool(requestId, decision): Promise<void> - respond to an input_required pause whose kind is "tool_permission". decision is one of "approve_once" (allow this one call), "approve_session" (allow this and the rest of the session), or "deny".
  • answerQuestion(requestId, answers): Promise<void> - respond to an input_required pause whose kind is "user_question" (the agent asked the user something). Render the pause’s questions as choices, then pass answers as an object keyed by question text; for a multiSelect question join the chosen labels with ", ". Answering never grants a tool for the session.
  • uploadFile(name, contentBase64): Promise<string> - upload a file to the session; returns its workspace-relative path. Opens a session first if none exists.
  • interrupt(): Promise<void> - ask the server to stop the current turn.
  • identify(user): void - set soft / verified identity, applied on the next createSession.
  • shutdown(): void - abort all in-flight work and close the client.

Authentication

Pass exactly one auth source: a publishable embedKey (the client exchanges it at /v1/embed/session-tokens), a static token, a tokenEndpoint URL the client GETs for { token }, or a refreshable tokenFn:

React: @karta/react

A thin layer over @karta/widget with two entry points. react and react-dom (>=18) are peer dependencies.

<KartaWidget/> - the prebuilt widget as a component

It mounts into document.body as an iframe (the agent-chat app in widget mode), so <KartaWidget/> itself puts nothing in your React tree. It is SSR-safe (mounts in a client-only effect) and re-mounts only when an identity-defining prop changes (agent, embedKey, baseUrl, token, tokenEndpoint, transport, user.id, user.identityToken) - changing theme or a callback updates in place without dropping the conversation. Wire host-page events via callback props (onReady, onOpen, onClose, onSessionStarted, onMessageSent, onMessageReceived, onUnread, onEscalate, onError), and drive it from a ref:
The handle exposes open(), close(), and sendMessage(text).
<KartaWidget/> does not accept tokenFn or model. For a refreshable token function or a custom model, build a custom UI with useKartaAgent() below, which exposes the full client option surface.

useKartaAgent() - build a custom UI

useKartaAgent() returns { messages, send, status, error, sessionId, reset }:
  • messages: ChatMessage[] - { id, role, text, tools?, streaming? }. For an agent message, text is cumulative and is REPLACED on each event (the hook handles this - never concatenate).
  • send(text) - appends a user message and a streaming agent message, then consumes the turn.
  • status - 'idle' | 'streaming' | 'error'.
  • error - the Error from the last failed turn (carries code when present).
  • sessionId - the backing session id once a turn has started.
  • reset() - clears the transcript and aborts any in-flight stream.
The client is recreated only when an identity-defining option changes; the in-flight stream is aborted on unmount and on reset().

Next

Identity

Soft vs verified, and the HMAC scheme the user/identify fields feed.

Security & privacy

The credential boundaries and the data path.