SDK Reference
API reference for the agent client SDK (@limio/sdk/ai).
Everything exported from @limio/sdk/ai. For a walkthrough, start with Getting Started.
useChat(options)
The conversation state machine. Bootstraps a session, lazily creates the conversation on the first send, renders optimistic messages, and rolls back on error.
const chat = useChat(options)Options
greeting
string
server greeting
Opening assistant message, shown immediately. Falls back to the agent's server-configured greeting when omitted.
autoStart
boolean
true
Bootstrap the session on mount. Set false to defer until the surface is shown, then flip to true or call start().
onFailure
(report: ChatFailureReport) => void
none
Called once for each failed or recovered send, and when the session cannot start. Use it to send failures to your own monitoring. See Monitoring failures.
Returns
messages
ChatMessage[]
The conversation, including the greeting and any pending bubble.
session
Session | null
The current session once bootstrapped.
conversation
Conversation | null
The conversation once created, after the first send.
status
ChatStatus
"idle", "ready", "sending", or "error".
error
string | null
The technical message of the last failed send, or null. Do not show it to shoppers; classify with failure.
failure
ChatFailureReport | null
The report of the last failed send, or null. Use its capture to show the shopper a plain message. See Monitoring failures.
send
(text: string) => Promise<void>
Send the visitor's message. Optimistic; creates the conversation lazily.
reset
() => void
Start a fresh conversation: clears messages, drops the conversation, keeps the session. A greeting you supplied in options is re-seeded; the server-configured greeting is not, so the list resets to empty.
start
() => Promise<void>
Bootstrap the session manually when autoStart is false. Idempotent.
ChatMessage
pending is true on the placeholder bubble shown while the agent's reply is in flight. It starts with empty content, and on a streamed reply that content fills as the text arrives while pending stays true. Render a typing indicator only while the content is empty, not for as long as the flag is set. See Streaming.
status appears on the pending bubble while the agent runs tools: a short shopper-facing line such as "Finding the right offers...". Render it in place of the typing indicator — tool calls are the longest part of a turn, and it is what the built-in widget shows. It clears when the reply text starts.
Streaming
When the agent has Stream replies enabled (see Configuring an Agent), useChat streams the reply into the pending bubble. There is nothing to turn on in your code: the hook reads the agent's setting from the session and picks the transport itself. session.streaming carries that setting if you want to read it, but the server decides per reply, so treat it as a hint rather than a guarantee.
A streamed reply arrives as a growing content on the pending message and settles into an ordinary message when it completes, holding exactly what the buffered path would have returned. Tool artifacts such as checkout links are unaffected: they arrive on the settled message either way.
Rendering a streamed reply
A pending bubble starts with empty content and fills as the reply streams, while pending stays true throughout. Test the content, not the flag: fall back to a typing indicator only while the bubble is still empty.
Keying on m.pending alone is not an error, but it hides the streamed text and delivers the reply whole at the end, which is the behaviour streaming was turned on to avoid.
useSmoothedText(text)
Models emit a reply in a handful of large chunks rather than character by character, so text rendered straight from content lands in visible jumps. useSmoothedText reveals a growing string at a steady, bounded rate, which reads as typing:
Only growth animates. A message that is already complete when it mounts renders whole, so settled history is never re-typed, and a string that changes rather than grows snaps to its new value instead of animating backwards. Under prefers-reduced-motion the type-in is off and text appears as it arrives.
The hook takes and returns a string, so a surface that wants both smoothing and markdown should smooth the text first and render the result with Markdown rather than passing blocks to MessageContent.
An agent with Show progress messages on can send one message with more than one text block: a short progress line, then the reply. Show each text block as its own bubble. contentToText joins the blocks with one line break, which markdown renders as one paragraph, so smooth each block on its own rather than the joined string.
MessageContent
Renders a message's content blocks to React: text/markdown formatted, text/plain literal, and component blocks via the registry.
content
ContentBlock[]
The message content to render.
registry
MessageComponentRegistry
Optional. Renders application/vnd.limio.component+json blocks. Omit to skip them.
conversationId
string | null
Optional. Passed to the registry as render context.
markdownClassName
string
Optional. Class on each rendered markdown block, for styling the generated HTML.
textClassName
string
Optional. Class on plain-text blocks.
For full control you can import the underlying markdown renderer, Markdown, directly.
Component registry
Maps a component name to a renderer. MessageContent uses it to render component blocks; unknown names render nothing.
register
(name: string, render: MessageComponentRenderer) => void
Add or replace a renderer.
render
(descriptor, ctx) => ReactNode
Render a descriptor; returns null if the name is unknown.
A renderer receives the component's props and a context object:
Transport
Lower-level functions for calling the Chat API directly. useChat is built on these; reach for them only when you need to bypass the hook.
createSession()
Bootstrap or confirm the visitor's session. Returns the session, including the configured greeting.
createConversation()
Start a new conversation thread.
getConversation(id)
Fetch an existing conversation.
listMessages(conversationId)
The conversation's 50 most recent messages, oldest first. Not the full history of a long chat.
sendMessage(conversationId, text)
Send a message and return the agent's reply.
sendMessageStream(conversationId, text, handlers, options?)
Send a message and stream the reply. Resolves the same final Message as sendMessage.
initiateUpload(conversationId, input)
Request a presigned upload for a document.
uploadFileToS3(init, file)
Upload the file bytes to the presigned target.
contentToText(content)
Flatten a content array to a plain string. Text blocks only; component blocks are skipped.
sendMessageStream
A superset of sendMessage: await it the same way and you get the same final Message, but the text also reaches handlers as it arrives. Calling it for an agent that has streaming switched off is safe and returns the reply in one piece, firing no deltas, so a surface can always use it.
onDelta
Each chunk of reply text. Append it to what you have already shown.
onReset
A step called tools and Show progress messages is off, so the text since the last reset was preamble rather than the answer. Discard it. These agents are told to write nothing before a tool call, so there is rarely anything to discard.
onSegment
A step called tools and Show progress messages is on, so the text since the last segment stays. Close the current message, so the next chunk starts a new one.
onTools
The agent started running tools. The argument is a short shopper-facing label ("Finding the right offers..."); show it in place of the typing indicator.
onDelta is required; onReset, onSegment and onTools are optional. options.signal takes an AbortSignal to cancel the request and stop reading mid-reply, which is worth wiring to unmount. options.inactivityTimeoutMs (default 45 000) aborts a stream that has gone silent.
If the connection drops after some text has rendered, the reply is unaffected: the turn finishes and is saved server-side regardless of whether anyone is listening. Recover it with listMessages. useChat does this for you. After a failure in transit (a cut or stalled stream, a network error, or a gateway timeout), it reads the conversation again after 1, 2, 4, 8, 10 and 5 seconds. It also reads at once when the browser comes back online. Meanwhile the pending bubble shows "Reconnecting…" as its status line. It shows the error only when no reply is saved within 30 seconds. A failure that the server reports, such as an error frame, gets one read and the error at once.
Configuring the transport
By default the transport calls the Chat API same-origin, authenticated by the visitor's Limio session cookie. A component running on your shop needs no configuration. For a cross-origin embed, configureChatTransport switches to bearer-token auth:
configureChatTransport(overrides)
Set the API base URL and a bearer-token source.
resetChatTransport()
Restore the default same-origin cookie mode.
Presentation
Everything the admin configured for the agent arrives on the session as session.presentation (type ChatPresentation). Every field is optional: absent means your surface applies its own default, so read each one with a fallback.
greeting
string
Opening assistant message. useChat seeds it for you; you only need this field to render it yourself.
title
string
Chat header title.
placeholder
string
Composer placeholder.
pointers
string[]
Starter questions to offer before the visitor's first message. The displayed text is the text to send.
footer
string
Markdown shown beneath the composer. Links allowed.
launcherIcon
string
The agent's logo, for a launcher or a header avatar. A site-relative /public/... asset path: resolve it with resolveAssetUrl.
launcherLabel
string
Short call-to-action for a launcher, such as "Ask Limio".
greetingPopup
boolean
Whether the greeting should show as a popup bubble from a closed launcher.
greetingPopupDelay
number
Seconds to wait before showing that popup.
greetingPopupMessage
string
Dedicated text for the popup bubble. Prefer it over greeting when set; the opened chat still greets with greeting.
The launcher fields describe a floating launcher, which the built-in widget renders. A custom surface is free to ignore them or to use them for its own entry point.
Theming
Apply the agent's admin-configured theme (see Theming an Agent) to a custom surface. The theme arrives on the session as session.presentation.theme.
themeRootProps(theme)
The props a surface spreads onto its root element: token overrides as style, plus a data-bubble-style attribute. A no-op when the theme is absent or empty.
themeTokens(theme)
The framework-neutral half: the CSS custom-property overrides as a plain object, for non-React callers to apply with el.style.setProperty.
The overrides are Limio design-token variables, so they restyle elements whose styles consume those tokens. A surface with its own palette should read the hex fields off the theme directly instead.
ChatTheme
Every field is optional; absent means the design-system default. Colours are 6-digit #hex.
accentColor
The brand colour: visitor bubbles, the launcher and buttons, unless a finer field below overrides it.
userBubbleColor
The visitor's bubbles only.
agentBubbleColor
The agent's bubbles.
headerColor
The header bar. Its text colour is derived for you, so it stays legible.
launcherColor
The launcher only.
backgroundColor
The panel surface.
textColor
Body text.
borderColor
Panel and composer borders.
bubbleStyle
A bubble decoration preset, not a colour. See below.
bubbleStyle is a BubbleStyle: "rounded" (the default), "tailed", "flat", "outlined", "shadowed", "bordered", "minimal" or "gradient". themeRootProps applies it as a data-bubble-style attribute on your root element.
The colours reach your surface as CSS variables, so they land whatever stylesheet you ship. The bubble shapes do not: the presets live in the widget's own stylesheet, keyed off that attribute. A surface with its own bubble CSS should read bubbleStyle and style its bubbles itself. A surface that wants the stock shapes needs three things: import the widget's stylesheet (@limio/chat/dist/chat.css), put the lmo-chat class on the root you spread themeRootProps onto, and mark each bubble element with data-bubble and data-bubble-role="user" or "assistant" — the preset selectors key off all of them. The stylesheet is scoped to .lmo-chat, so it will not touch the rest of your page.
Bubble text colour is not a field: it is derived from the bubble colour behind it, so text on a themed bubble stays readable whatever the admin picks.
Brand assets
resolveAssetUrl(value)
Resolve a site-relative /public/... asset path (such as presentation.launcherIcon) against the origin the transport talks to, so it also loads in a cross-origin embed. Absolute URLs pass through untouched.
useBrandIcon(iconPath)
React hook wrapping the above for an <img>: returns { src, onError }. src is the resolved URL, or undefined when the icon is unset or failed to load, so you can fall back to your own default rather than render a broken image.
Analytics
A surface built on useChat reports engagement automatically: if the page runs Google Tag Manager, a limio_chat_message_sent event is pushed to the dataLayer on every send, and limio_chat_error on failure — pseudonymous IDs only, never message content. There is nothing to configure, and pages without a dataLayer are untouched. pushChatEvent and chatEventContext are exported for a surface that wants to add the open/close events the built-in widget emits.
Monitoring failures
useChat does not send failures anywhere itself. To monitor your surface, pass onFailure and send each report to your own tool. The built-in Limio widget reports to Limio. Your surface sends nothing to Limio.
A report never contains the text of a message. A callback that throws does not stop the chat.
ChatFailureReport
capture
ChatFailureCapture
What happened. See the table below.
code
string | null
The error code, for example reply_failed, http_500 or network. null for an error that did not come from the chat API.
status
number | null
The HTTP status of the response, or null when no response arrived.
gatewayRequestId
string | null
The request id from the response (x-amzn-requestid). Give it to Limio support to find the request. null when no response arrived.
traceId
string | null
The trace id from the response (x-amzn-trace-id), or null.
transport
"stream" | "buffered" | null
How the reply was requested. null for session_failed.
elapsedMs
number
The time from the send (or the session start) to the report, in milliseconds.
online
boolean
navigator.onLine at the moment of the failure.
conversationId
string | null
The conversation, when it exists.
agentId
string | null
The agent of the session, when it exists.
error
Error
The error itself. A failure from the chat API is a ChatSendError. Any other error, such as a bug in your surface, is passed as it is, with its stack.
capture
When
reply_incomplete
The reply was cut after it started (for example at the 29-second gateway limit), or the stream sent nothing for 45 seconds, and no saved reply was found within 30 seconds. code says which: stream_stalled for a silent stream.
send_rejected
The chat API answered the send with an error, or the agent could not make a reply and said so in the stream (status 200).
send_network
The send got no response, for example because the browser is offline.
send_failed
Any other failure, such as a bug in the surface.
reply_recovered
The reply did not arrive, but useChat found it saved and showed it. Not an error, but a sign of a slow or unstable connection.
session_failed
The session could not start.
failure is the report of the last failed send (a recovered reply and a session that could not start never set it). Map its capture to your own words, so that the shopper does not see a technical message. The built-in widget shows "We could not reach the chat. Check your connection and try again." for send_network, "The reply took too long. Try again." for reply_incomplete, and "Something went wrong. Try again." for the others.
ChatSendError is exported, with code, status, gatewayRequestId and traceId. replyMayBeSaved(error) is true when the reply did not reach the browser but the server can still save it, so useChat looks for it before it shows an error.
Types
The wire types come from Limio's shared agent protocol and are re-exported for convenience: ContentBlock, Message, ToolArtifact, and Usage, plus the transport types Session, Conversation, CreateConversationResponse, ChatTransport, StreamHandlers, and ApiError.
A ContentBlock is a typed piece of message content:
Last updated
Was this helpful?

