> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vizochok.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Events & Callbacks

> Handle cart changes, product clicks, errors, and connection events

## Overview

The widget communicates with your application through six callback functions passed in the config. These callbacks let you synchronize your store's cart, track analytics, and handle errors.

```typescript theme={null}
const widget = new VizochokWidget({
  apiKey: 'pk_your_key',
  storeId: 'your-store',

  onCartChanged: (cart) => { /* ... */ },
  onProductClick: (product) => { /* ... */ },
  onSessionComplete: (summary) => { /* ... */ },
  onError: (error) => { /* ... */ },
  onConnect: () => { /* ... */ },
  onDisconnect: () => { /* ... */ },
});
```

***

## onCartChanged

Fired when the AI assistant adds, removes, updates, or clears items in the cart via webhooks. Use this callback to update your store's cart UI in real time.

```typescript theme={null}
onCartChanged: (cart: CartChangedEvent) => void
```

### CartChangedEvent

| Field      | Type         | Description                                                                                                           |
| ---------- | ------------ | --------------------------------------------------------------------------------------------------------------------- |
| `action`   | `string`     | The action that triggered the change: `'item_selected'`, `'item_removed'`, `'quantity_updated'`, or `'cart_cleared'`. |
| `sku`      | `string?`    | SKU of the affected product (absent for `cart_cleared`).                                                              |
| `name`     | `string?`    | Name of the affected product.                                                                                         |
| `quantity` | `number?`    | New quantity of the affected product.                                                                                 |
| `price`    | `number?`    | Price of the affected product.                                                                                        |
| `items`    | `CartItem[]` | Complete list of all items currently in the cart.                                                                     |
| `total`    | `number`     | Total cart value.                                                                                                     |

### CartItem

| Field         | Type             | Description                   |
| ------------- | ---------------- | ----------------------------- |
| `sku`         | `string`         | Product SKU.                  |
| `name`        | `string`         | Product display name.         |
| `price`       | `number`         | Current price per unit.       |
| `promo_price` | `number \| null` | Promotional price, if active. |
| `brand`       | `string \| null` | Product brand name.           |
| `quantity`    | `number`         | Quantity in cart.             |

### Example: Sync with store cart

```typescript theme={null}
onCartChanged: (cart) => {
  // Update your store's cart state
  store.setCartItems(cart.items.map(item => ({
    id: item.sku,
    name: item.name,
    price: item.promo_price ?? item.price,
    qty: item.quantity,
  })));

  // Update cart total in your header
  document.getElementById('cart-total').textContent = `${cart.total} $`;

  // Update cart badge count
  const itemCount = cart.items.reduce((sum, i) => sum + i.quantity, 0);
  document.getElementById('cart-badge').textContent = String(itemCount);
},
```

<Note>
  The `onCartChanged` callback is a **UI notification only**. The actual cart change has already been confirmed via the server-to-server webhook to your backend. This callback is for updating the client-side UI.
</Note>

***

## onProductClick

Fired when a user clicks the "Add" button on a product card within the chat. This callback is informational -- the cart update itself is handled by `onCartChanged` after the server confirms the webhook.

```typescript theme={null}
onProductClick: (product: ProductItem) => void
```

### ProductItem

| Field           | Type             | Description                                  |
| --------------- | ---------------- | -------------------------------------------- |
| `sku`           | `string`         | Product SKU.                                 |
| `name`          | `string`         | Product display name.                        |
| `price`         | `number`         | Current price per unit.                      |
| `unit`          | `string`         | Unit of measurement (e.g., `'pcs'`, `'kg'`). |
| `quantity_step` | `number`         | Minimum quantity increment.                  |
| `quantity`      | `number?`        | Selected quantity.                           |
| `brand`         | `string \| null` | Product brand name.                          |
| `image_url`     | `string \| null` | Product image URL.                           |
| `promo_price`   | `number \| null` | Promotional price, if active.                |

### Example: Track product interactions

```typescript theme={null}
onProductClick: (product) => {
  // Analytics tracking
  analytics.track('product_selected_via_assistant', {
    sku: product.sku,
    name: product.name,
    price: product.promo_price ?? product.price,
    unit: product.unit,
  });

  // Open product detail page
  window.open(`/products/${product.sku}`, '_blank');
},
```

***

## onSessionComplete

Fired when the server sends a response with a session summary attached. This provides the current state of all items the user has selected during the conversation.

```typescript theme={null}
onSessionComplete: (summary: SessionSummary) => void
```

### SessionSummary

| Field            | Type         | Description                                  |
| ---------------- | ------------ | -------------------------------------------- |
| `selected_items` | `CartItem[]` | All items currently selected in the session. |
| `total`          | `number`     | Total value of selected items.               |
| `item_count`     | `number`     | Number of distinct items selected.           |

### Example: Update checkout state

```typescript theme={null}
onSessionComplete: (summary) => {
  console.log(`Session: ${summary.item_count} items, total ${summary.total}`);

  // Sync with your checkout system
  if (summary.item_count > 0) {
    checkout.updateItems(summary.selected_items);
    checkout.setTotal(summary.total);
  }
},
```

<Info>
  `onSessionComplete` is also called when a session is restored on reconnect, providing the previously selected items.
</Info>

***

## onError

Fired on any error, whether from the server (rate limits, internal errors) or from the client (connection failures, message too large).

```typescript theme={null}
onError: (error: WidgetError) => void
```

### WidgetError

| Field     | Type     | Description                                                                              |
| --------- | -------- | ---------------------------------------------------------------------------------------- |
| `code`    | `string` | Machine-readable error code. See [Error Codes](/concepts/error-codes) for the full list. |
| `message` | `string` | Localized human-readable error message in the widget's configured language.              |

### Server Error Codes

`rate_limit_exceeded`, `user_message_limit`, `user_conversation_limit`, `tenant_daily_token_limit`, `tenant_monthly_token_limit`, `agent_busy`, `message_too_large`, `invalid_json`, `internal_error`, `max_rounds_exceeded`, `session_token_limit`, `no_catalog_data`

### Client Error Codes

`auth_error`, `auth_timeout`, `ws_error`, `message_too_large`, `queue_full`, `max_reconnect`, `connection_lost`

### Example: Error logging

```typescript theme={null}
onError: (error) => {
  // Report to your error tracking service
  errorTracker.captureError(new Error(error.message), {
    tags: { source: 'vizochok', code: error.code },
  });

  // Show user-facing notification for critical errors
  if (error.code === 'auth_error') {
    showToast('Chat unavailable. Please refresh the page.');
  }
},
```

***

## onConnect

Fired when the WebSocket connection is authenticated and ready to send/receive messages. This is called after the `auth_ok` message is received from the server.

```typescript theme={null}
onConnect: () => void
```

### Example

```typescript theme={null}
onConnect: () => {
  console.log('VIZOCHOK widget connected');
  document.getElementById('ask-assistant-btn').disabled = false;
},
```

***

## onDisconnect

Fired when the WebSocket connection is closed, whether intentionally (unmount/destroy) or due to a network issue. The widget automatically attempts reconnection for non-auth failures.

```typescript theme={null}
onDisconnect: () => void
```

### Example

```typescript theme={null}
onDisconnect: () => {
  console.log('VIZOCHOK widget disconnected');
  document.getElementById('ask-assistant-btn').disabled = true;
},
```

***

## Common Patterns

### Cart Synchronization

The most common integration pattern -- keeping your store's cart in sync with the AI assistant's cart:

```typescript theme={null}
const widget = new VizochokWidget({
  apiKey: 'pk_your_key',
  storeId: 'your-store',
  userId: currentUser.id,

  onCartChanged: (cart) => {
    // Replace your cart state entirely with the AI's cart
    // The `items` array always contains the FULL cart, not just the delta
    store.dispatch('cart/replaceAll', {
      items: cart.items,
      total: cart.total,
    });
  },

  onSessionComplete: (summary) => {
    // Also sync on session restore (page refresh / reconnect)
    store.dispatch('cart/replaceAll', {
      items: summary.selected_items,
      total: summary.total,
    });
  },
});
```

<Warning>
  The `items` field in `onCartChanged` contains the **complete** cart, not just the changed item. Always replace your entire cart state rather than trying to apply deltas.
</Warning>

### Analytics Tracking

Track all widget interactions for analytics:

```typescript theme={null}
const widget = new VizochokWidget({
  apiKey: 'pk_your_key',
  storeId: 'your-store',

  onConnect: () => {
    analytics.track('assistant_connected');
  },

  onProductClick: (product) => {
    analytics.track('assistant_product_click', {
      sku: product.sku,
      price: product.price,
    });
  },

  onCartChanged: (cart) => {
    analytics.track('assistant_cart_changed', {
      action: cart.action,
      item_count: cart.items.length,
      total: cart.total,
    });
  },

  onError: (error) => {
    analytics.track('assistant_error', {
      code: error.code,
    });
  },
});
```

### Connection Status Indicator

Show connection state in your page:

```typescript theme={null}
const statusEl = document.getElementById('assistant-status');

const widget = new VizochokWidget({
  apiKey: 'pk_your_key',
  storeId: 'your-store',

  onConnect: () => {
    statusEl.className = 'status-online';
    statusEl.textContent = 'Assistant online';
  },

  onDisconnect: () => {
    statusEl.className = 'status-offline';
    statusEl.textContent = 'Assistant offline';
  },
});
```
