> For the complete documentation index, see [llms.txt](https://docs.limio.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.limio.com/ai/limio-agents/sdk-reference.md).

# SDK Reference

Everything exported from `@limio/sdk/ai`. For a walkthrough, start with [Getting Started](/ai/limio-agents/getting-started.md).

{% hint style="info" %}
Available from Release 117 onwards.
{% endhint %}

## `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.

```ts
const chat = useChat(options)
```

### Options

| Option      | Type                     | Default         | Description                                                                                                             |
| ----------- | ------------------------ | --------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `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()`. |
| `onError`   | `(error: Error) => void` | none            | Called when a send fails, after the pending bubble is rolled back.                                                      |

### Returns

| Property       | Type                              | Description                                                                                                                                                                                                    |
| -------------- | --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `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 last send error message, or `null`.                                                                                                                                                                        |
| `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`

```ts
type ChatMessage = {
  id: string
  role: "user" | "assistant"
  content: ContentBlock[]
  pending?: boolean
  status?: string
}
```

`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](#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](/ai/limio-agents/configuring-an-agent.md#stream-replies)), `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.

```tsx
return m.pending && m.content.length === 0 ? (
  <span>…</span>
) : (
  <MessageContent content={m.content} markdownClassName="chat-md" />
)
```

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:

```tsx
import { useSmoothedText, contentToText } from "@limio/sdk/ai"

const AgentText = ({ message }) => {
  const shown = useSmoothedText(contentToText(message.content))
  return <p>{shown}</p>
}
```

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`.

## `MessageContent`

Renders a message's content blocks to React: `text/markdown` formatted, `text/plain` literal, and component blocks via the registry.

| Prop                | Type                       | Description                                                                         |
| ------------------- | -------------------------- | ----------------------------------------------------------------------------------- |
| `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

```ts
const registry = createMessageComponentRegistry(initialRenderers?)
```

Maps a component name to a renderer. `MessageContent` uses it to render component blocks; unknown names render nothing.

| Member     | Signature                                                  | Description                                                 |
| ---------- | ---------------------------------------------------------- | ----------------------------------------------------------- |
| `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:

```ts
type MessageComponentRenderer = (props: Record<string, unknown>, ctx: { conversationId: string | null }) => ReactNode
```

## 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.

| Function                                                      | Description                                                                                         |
| ------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
| `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.

```ts
const reply = await sendMessageStream(conversationId, text, {
  onDelta: (chunk) => {
    /* append to the bubble */
  },
  onReset: () => {
    /* clear it: what arrived was preamble */
  }
})
```

| Handler   | When it fires                                                                                                                                                                                                                              |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `onDelta` | Each chunk of reply text. Append it to what you have already shown.                                                                                                                                                                        |
| `onReset` | A step called tools, so the text since the last reset was preamble rather than the answer. Discard it. Agents are told not to narrate before a tool call, so this rarely fires; it is the safety net for a model that narrates regardless. |
| `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` 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`, which is what `useChat` does before it reports an error.

### 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:

| Function                            | Description                                     |
| ----------------------------------- | ----------------------------------------------- |
| `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.

| Field                  | Type        | What it is                                                                                                                        |
| ---------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `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`.            |
| `theme`                | `ChatTheme` | Brand colours and bubble style. See [Theming](#theming) below.                                                                    |

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](/ai/limio-agents/theming.md)) to a custom surface. The theme arrives on the session as `session.presentation.theme`.

| Function                | Description                                                                                                                                                     |
| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `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`.

| Field              | Colours                                                                                               |
| ------------------ | ----------------------------------------------------------------------------------------------------- |
| `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

| Function                 | Description                                                                                                                                                                                                                               |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `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. |

```tsx
const { src, onError } = useBrandIcon(session?.presentation?.launcherIcon)

return src ? <img src={src} alt="" onError={onError} /> : <YourDefaultIcon />
```

## 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.

## 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:

```ts
type ContentBlock =
  { type: "text/plain"; data: string } | { type: "text/markdown"; data: string } | { type: "application/vnd.limio.component+json"; data: ToolArtifact }

type ToolArtifact = {
  component: string
  props: Record<string, unknown>
}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.limio.com/ai/limio-agents/sdk-reference.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
