

<div class="admonition admonition-important"><div class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /></svg></div><div class="admonition-body"><div class="admonition-content">

This feature is available in  private preview mode and accessible only on selected workspaces. To get access, contact your account manager or Synerise Support.

</div></div></div>


The AI Assistant SDK renders the Customer AI Assistant chat on a web page and talks to Synerise on the customer's behalf. This article is the reference of its options and API. For a step-by-step guide, see [Implementing the Customer AI Assistant on your website](/docs/ai-hub/ai-assistant/implementing-the-ai-assistant-on-your-website).

## Packages and delivery

The SDK is published as four npm packages that share one version number and as ready-to-use files served by Synerise.

| Package | For |
| --- | --- |
| `@synerise/ai-assistant-sdk` | Vanilla JavaScript. Exposes the `init()` function and is also served as the files below. |
| `@synerise/ai-assistant-react` | React 18. Exposes the `AIAssistant` component. |
| `@synerise/ai-assistant-core` | Preact. The component the other packages build on. |
| `@synerise/ai-assistant-ui` | UI primitives and the theme, for a custom chat with your own state handling. |

Files served directly by Synerise:

| File | Format |
| --- | --- |
| `https://web.snrbox.com/ai-shop-assistant/sdk.es.js` | ES module. About 155 kB initially; the Markdown renderer and other heavy parts load on demand. |
| `https://web.snrbox.com/ai-shop-assistant/sdk.umd.js` | Single UMD file. About 325 kB; exposes the `window.SyneriseAIShopAssistant` global object |

The files always serve the latest release. The npm packages let you pin a version.

ES module served by Synerise:

```js
import { init } from "https://web.snrbox.com/ai-shop-assistant/sdk.es.js";
```

UMD file served by Synerise:

```js
const chat = SyneriseAIShopAssistant.init({ rootElementId: "synerise-assistant" });
```

npm package:

```js
import { init } from "@synerise/ai-assistant-sdk";
```

## Initialization options

Renders the chat into an existing, empty element and returns an [instance](#instance-methods). The chat starts closed; call `open()` to show it. Initialization fetches the assistant's availability and, if the assistant is available, starts a conversation (or loads the one given in `threadId`).

| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `rootElementId` | `string` | required | ID of the element that hosts the chat. Must exist and be empty. |
| `stream` | `boolean` | `false` | When `true`, answers are streamed (server-sent events) and appear progressively. Recommended. Streaming also enables the `onStreamError` and `onConversationTitle` callbacks. |
| `header` | `string` | `"AI Assistant"` | Title shown in the header. For a custom element, use the `slots.header.title` slot. |
| `avatar` | `string` | Synerise AI icon | URL of the avatar shown next to the assistant's messages. |
| `assistantId` | `string` | workspace default | ID (UUID) of the assistant configuration to talk to. |
| `context` | `object \| null` | `null` | Context sent with every request, for example store and language. |
| `additionalContextValues` | `object \| null` | `null` | Extra metadata sent with every request, for example segment IDs or experiment flags. |
| `pageContext` | `{ pageType: "product"; itemId: string } \| null` | not set | Product the customer is viewing. See [Storefront page context](#storefront-page-context). |
| `autoDetectPageContext` | `boolean` | `false` | Read the product from Open Graph tags and keep it in sync with changes to the `<head>` element. |
| `threadId` | `string` | not set | Initialize with an existing conversation loaded instead of starting a new one. |
| `textAreaMaxRows` | `number` | `3` | Number of lines the message input grows to before it scrolls. |
| `actionsLayout` | `"inline" \| "dock"` | `"inline"` | Where the latest answer's buttons and suggestions render: inside the message or in a full-width row below the last message. |
| `theme` | `{ variables: Record<string, string> }` | defaults | Overrides of the visual variables. See [Customizing the AI Assistant chat](/docs/ai-hub/ai-assistant/customizing-the-ai-assistant-chat#colors-fonts-and-corners). |
| `texts` | `AIAssistantTranslations` | English | Overrides of the interface texts. See [Texts](#texts). |
| `slots` | `ChatSlots` | not set | Replacements for parts of the interface. See [Slots](#slots). |
| `fastMode` | `boolean` | `false` | Ask for faster, less thorough answers. |
| `apiUrl` | `string` | `https://api.synerise.com/agents/v1/ai-assistant` | Base address of the assistant API. |
| `disableXSRFToken` | `boolean` | `true` | Authentication mode. See [Authentication](#authentication). |
| `settings` | `{ assistantVisible: boolean; responseFeedbackEnabled: boolean }` | not set | Special integrations only. Skips the availability check and uses these values. See [Assistant availability](#assistant-availability). |
| `onLoad` | `({ assistantVisible }) => void` | not set | Called once when the availability is known, before the conversation starts. |
| `onMessage` | `(response) => void` | not set | Called after every successful answer with the response body. See [Response shape](#response-shape). |
| `onStreamError` | `({ message, type, stage }) => void` | not set | Called when the assistant refuses a streamed exchange. See [Refused responses](#refused-responses). |
| `onConversationTitle` | `({ threadId, title }) => void` | not set | Called when Synerise generates the conversation's title (streaming only). |

## Instance methods

| Method | Returns | Description |
| --- | --- | --- |
| `open()` | `void` | Shows the chat. |
| `close()` | `void` | Hides the chat without removing it. The conversation is kept. |
| `message(text)` | `void` | Sends a message as if the customer had typed it. |
| `unmount()` | `void` | Removes the chat from the page and empties the container. |
| `setContext(value)` | `void` | Replaces `context` for the following requests. |
| `setAdditionalContextValues(value)` | `void` | Replaces `additionalContextValues` for the following requests. |
| `setPageContext(value)` | `void` | Sets or clears (`null`) the product the customer is viewing. |
| `loadThread(threadId)` | `Promise<void>` | Replaces the current conversation with an existing one. |
| `getConversations()` | `Promise<AIAssistantConversation[]>` | Lists the customer's conversations. |

### Response shape

`onMessage` receives the body of each successful response:

```ts
type Response = {
  data: {
    messages: Array<HumanMessage | AIMessage>;
    context: Context | null;
    additionalContextValues: AdditionalContextValues | null;
  };
  meta: { threadId: string };
};
```

`messages` is the full conversation so far, made of the elements described in [Message elements](#message-elements).

### Conversations

```ts
type AIAssistantConversation = {
  threadId: string;
  agentType: string;
  realm: string;
  businessProfileId: number;
  userId?: number | null;
  clientId?: string | null;
  createdAt: string;          // ISO timestamp
  title?: string | null;      // generated by Synerise
  summary?: string | null;    // generated by Synerise
  meta?: Record<string, unknown> | null;
};
```

If `loadThread()` or initialization with `threadId` hits a conversation that does not exist (HTTP 404), the chat enters the `THREAD_NOT_FOUND` error state and shows a **Start new conversation** button that starts a fresh conversation.

## Authentication

| Mode | `disableXSRFToken` | How requests are authenticated |
| --- | --- | --- |
| Tracking code (default) | `true` | The SDK reads the tracker key (`SR.auth.trackerKey()`) and the customer's UUID (`SyneriseTC.uuid`) from the Synerise Web SDK on the page and sends them with each request. The tracking code must be loaded and initialized first. |
| Cookie and XSRF token | `false` | Requests are sent with credentials and the `XSRF-TOKEN` cookie is mirrored into the `X-XSRF-TOKEN` header. For tenants configured for session-based authentication. |

## Assistant availability

Before the conversation starts, the SDK fetches the assistant's public settings:

```ts
type AIAssistantPublicSettings = {
  assistantVisible: boolean;          // false: the chat renders nothing
  responseFeedbackEnabled: boolean;   // true: answers get the rating toolbar
};
```

`assistantVisible: false` means the assistant is disabled for the workspace or the credits are exhausted. The chat then renders nothing and no conversation starts. If the settings request fails, both flags fall back to `false` and the chat renders nothing. `onLoad` is called exactly once with the result; the flag is not re-checked during the session.

The `settings` option replaces the fetch with fixed values. Use it only for integrations that must not depend on the workspace settings, such as a read-only preview. Everywhere else leave it unset.

## Storefront page context

```ts
type StorefrontPageContext = { pageType: "product"; itemId: string };
```

The product is sent with every request under the `additionalContextValues.page_context` key. Three ways set it: the `pageContext` option, `autoDetectPageContext: true`, and the `setPageContext()` method. Once any of them is used, it takes precedence over a `page_context` key placed in `additionalContextValues` directly: `setPageContext(null)` removes the key even if it is still present there.

Automatic detection reads the `og:type` tag (`product` or `product.item`; `product.group` is ignored) and the `product:retailer_part_no` tag. The tags are read synchronously during initialization, so the first request already carries the product, and `<head>` is observed for changes afterwards.

The detection is also available as a function:

```js
import { detectPageContextFromMeta } from "https://web.snrbox.com/ai-shop-assistant/sdk.es.js";

const detected = detectPageContextFromMeta(); // StorefrontPageContext | null
```

## Refused responses

In streaming mode Synerise can refuse an exchange instead of answering it, most often because guardrails rejected the prompt. The SDK handles both forms:

- With a `message`: the text is shown as the assistant's answer and the conversation continues. `onMessage` reports the turn as usual. Because a refused prompt is not stored on the server, it does not reappear after the conversation is reloaded with the `loadThread()` method.
- Without a `message`: the chat shows the `STREAM_ERROR` error state with a **Try Again** button; the customer's message stays in the conversation.

`onStreamError` is called in both cases with the raw payload:

```ts
type AIAssistantStreamError = {
  message?: string;   // user-facing copy, when provided
  type?: string;      // e.g. "validation_failed"
  stage?: string;     // e.g. "input" or "output"
  [key: string]: unknown;
};
```

An exception thrown inside the callback is contained and does not affect the chat.

## Texts

```ts
type AIAssistantTranslations = Partial<{
  loadingMessage: string;
  tryAgain: string;
  startNewConversation: string;
  rating: Partial<{ commentPlaceholder: string; cancel: string; submit: string; thanks: string }>;
  errorMessage: Partial<Record<AIAssistantErrorType, string>>;
}>;

type AIAssistantErrorType =
  | "UNKNOWN_ERROR"
  | "NETWORK_ERROR"      // offline, blocked request, CORS, timeout
  | "SERVER_ERROR"       // HTTP 5xx
  | "CLIENT_ERROR"       // HTTP 4xx
  | "THREAD_NOT_FOUND"   // the requested conversation does not exist
  | "STREAM_ERROR";      // refused exchange without user-facing copy
```

In the React and Preact packages every text also accepts a React node. Defaults and usage are listed in [Customizing the AI Assistant chat](/docs/ai-hub/ai-assistant/customizing-the-ai-assistant-chat#texts-of-the-interface).

## Message elements

Messages returned in `response.data.messages` and passed to message slots:

```ts
type HumanMessage = {
  type: "HUMAN";
  content: { type: "BUBBLE_TEXT"; content: string };
  actionParams?: Record<string, unknown>;
};

type AIMessage = {
  type: "AI";
  seqNo?: number | null;      // position of the answer in the conversation; null for the greeting
  feedback?: { vote: "POSITIVE" | "NEGATIVE"; comment?: string };
  isHighlighted: boolean;
  content: MessageElement[];
};

type MessageElement =
  | { type: "TEXT"; content: string }                           // Markdown
  | { type: "BUBBLE_TEXT"; content: string; productLinks?: ProductLink[] }
  | { type: "OL" | "UL"; items: Array<{ content: string; nestedList?: MessageElement }> }
  | { type: "BUTTON"; content: string; action: ChatAction; iconLeft?: IconName; iconRight?: IconName;
      tooltip?: { title: string; description?: string }; disabled?: boolean }
  | { type: "SUGGESTION"; content: { title: string; description: string }; action: ChatAction; disabled?: boolean }
  | { type: "CAROUSEL"; content: ProductItem[] };               // rendered as product cards

type ProductItem = {
  itemId?: string;            // absent on items that cannot be attributed; such items emit no click events
  img: string;
  title: string;
  description: string;
  meta: string[];             // additional lines, HTML allowed
  action: ChatAction;
  attributes: Record<string, unknown>;
  correlationId?: string | null;
  campaignId?: string | null;
  searchCorrelationId?: string | null;
};

type ProductLink = { url: string; itemId: string; correlationId?: string | null;
  campaignId?: string | null; searchCorrelationId?: string | null };
```

Clicks on product cards and on inline product links are reported to Synerise as `assistant.click` events automatically; see [Measuring the AI Assistant](/docs/ai-hub/ai-assistant/measuring-the-ai-assistant).

### Chat actions

Every button, suggestion, and product card carries an `action`:

```ts
type ChatAction =
  | { type: "MESSAGE"; content: string; params?: object }   // sends a message to the assistant
  | { type: "REDIRECT"; url: string }                        // opens a page
  | { type: "CUSTOM"; name: string; params?: object };      // handled by the host application
```

`CUSTOM` actions are produced by **Custom action** tools of the assistant configuration. The React and Preact packages deliver them to the `onCustomAction` callback as an object with the `actionName` and `params` fields. The vanilla `init()` does not expose this callback in the current version.

### Icon names

Used by `iconLeft` and `iconRight` on buttons:

```
AngleLeftM | AngleRightM | ArrowRightM | AttachmentsM | CloseM |
CopyClipboardM | FileTypeImage | InfoFillM | InfoM | LinkM |
OptionVerticalM | PauseM | RefreshM | SearchM | StepBackM |
StepForwardM | StopM | WarningFillM | WarningM
```

## Slots

A slot is a function that returns a DOM element (or a React node in the framework packages). The chat calls it where the default element would render and uses the result instead. Return `null` to hide the element, `undefined` to keep the default.

### Header

| Slot | Parameters | Replaces |
| --- | --- | --- |
| `header.title` | none | The title. |
| `header.closeButton` | `{ onClose }` | The close button. Call `onClose()` to close the chat. |
| `header.extraActions` | none | An extra area before the close button. |

### Messages

| Slot | Parameters | Replaces |
| --- | --- | --- |
| `messages.avatar` | `{ src }` | The avatar next to the assistant's messages. |
| `messages.loader` | `{ avatar, message }` | The typing indicator row. |
| `messages.error` | `{ code, message, tryAgainMessage, onRetry }` | The error row with the retry button. |
| `messages.toolbar` | `{ seqNo, isLastMessage, feedback, submitFeedback }` | The rating toolbar under an answer. Rendered only when rating is enabled and the answer can be rated. |
| `messages.type.text` | `{ type: "text", content }` | A Markdown text element. |
| `messages.type.bubbleText` | `{ type: "bubble-text", content, productLinks, onItemClick }` | A short text bubble. Call `onItemClick(itemId)` when a product link is clicked to keep click tracking. |
| `messages.type.button` | `{ type: "button", content, iconLeft, iconRight, tooltip, disabled, action, onClick }` | A button. |
| `messages.type.suggestion` | `{ type: "suggestion", content: { title, description }, action, disabled }` | A suggestion card. |
| `messages.type.list` | `{ type: "ordered-list" \| "unordered-list", items }` | A list. |
| `messages.type.products.container` | `{ type: "products", content: ProductItem[], onItemClick }` | The wrapper around the product cards. |
| `messages.type.products.item` | `ProductItem & { onItemClick }` | A single product card. Call `onItemClick()` on click to keep click tracking. |
| `messages.type.custom[name]` | `data` | An element of a type the SDK does not render itself. |

### Message input

| Slot | Parameters | Replaces |
| --- | --- | --- |
| `textArea.input` | `{ value, onChange, disabled, placeholder, ref, state }` | The text field. Assign the element to the `ref.current` property. |
| `textArea.submitButton` | `{ onSubmit, disabled, icon, state }` | The send button. |

### Markdown

| Slot | Parameters | Replaces |
| --- | --- | --- |
| `markdown[tagName]` | the element's data | The renderer of one HTML tag in Markdown answers, for example the `a`, `code`, or `table` tag. |

## React and Preact packages

```bash
npm install @synerise/ai-assistant-react preact
```

`@synerise/ai-assistant-react` wraps the Preact component for React 18 applications. The chat itself is rendered with Preact, so `preact` must be installed next to the `react` and `react-dom` packages.

```tsx
import { useRef } from "react";
import { AIAssistant, type AIAssistantRef } from "@synerise/ai-assistant-react";

export function AssistantPanel() {
  const chatRef = useRef<AIAssistantRef>(null);

  return (
    <AIAssistant
      ref={chatRef}
      apiUrl="https://api.synerise.com/agents/v1/ai-assistant"
      displayMode="bordered"
      stream
      disableXSRFToken
      authParams={{ code: SR.auth.trackerKey(), clientUUID: SyneriseTC.uuid }}
      onPromptSuccess={(response) => console.log(response.meta.threadId)}
      onCustomAction={({ actionName, params }) => console.log(actionName, params)}
    />
  );
}
```

Differences from `init()`:

| Prop | Description |
| --- | --- |
| `apiUrl` | Required. Base address of the assistant API. |
| `onPromptSuccess` | Required. The equivalent of the `onMessage` option. |
| `displayMode` | `"drawer"` or `"bordered"` - the layout of the window. |
| `isOpen` | Controls whether the chat is shown; `open()` and `close()` do not exist. |
| `authParams` | `{ code, clientUUID }` for tracking-code authentication; the React package does not read them from the page by itself. |
| `onCustomAction` | Receives `CUSTOM` actions. |
| `onError` | Receives every error as an object with the `errorType`, `operation`, and `error` fields. |
| `header`, `avatar`, `texts` | Accept React nodes in addition to strings. |

The component is a `forwardRef`; the ref exposes the `loadThread(threadId)` and `getConversations()` methods. The `usePageContext({ autoDetect, initial })` hook returns `[pageContext, setPageContext]`; merge the value into `additionalContextValues.page_context` yourself. The API helpers are available without rendering anything as `assistantApi` (`initChat`, `sendChatMessage`, `getConversations`, and others).

Render the component on the client only. In server-side rendered applications (Next.js, Remix), load it dynamically with SSR disabled or mount it inside an effect.

`@synerise/ai-assistant-core` exposes the same `AIAssistant` component for Preact applications. `@synerise/ai-assistant-ui` exposes the chat primitives (`ChatBase`, message elements, `Icon`, the theme) for a chat with custom state handling.

## Endpoints used by the SDK

All requests go to the address in `apiUrl` (by default `https://api.synerise.com/agents/v1/ai-assistant`). Allow this host and `web.snrbox.com` in your Content Security Policy.

| Request | Purpose |
| --- | --- |
| `GET /public-settings` | Assistant availability and feature flags |
| `POST /chat` | Starts a conversation and returns the greeting |
| `POST /chat/{threadId}/messages` | Sends a customer's message |
| `GET /chat/{threadId}/messages` | Loads the messages of an existing conversation |
| `GET /chat/conversations` | Lists the customer's conversations |
| `PUT /chat/{threadId}/messages/rating` | Records a rating of an answer |
| `POST /chat/{threadId}/events/click` | Records a click on a product |

