> For the complete documentation index, see [llms.txt](https://alhena.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://alhena.gitbook.io/docs/developer-reference/website-sdk/events.md).

# Events

The Alhena Website SDK emits JavaScript events that allow you to respond to user interactions and widget state changes. Use these events to customize the user experience, track analytics, or integrate with other systems.

## Subscribing to Events

Use the `window.gleenWidget.on()` method to subscribe to events:

```javascript
window.gleenWidget.on("event_name", function (data) {
  // Handle the event
  console.log("Event fired:", data);
});
```

{% hint style="info" %}
Event subscriptions should be set up after the SDK script loads. For best results, place your event handlers after the SDK script tag or inside a `widget:loaded` handler.
{% endhint %}

***

## Quick Reference

| Event                                                               | Category   | Description                               |
| ------------------------------------------------------------------- | ---------- | ----------------------------------------- |
| [`widget:loaded`](#widget-loaded)                                   | Widget     | Widget finished loading                   |
| [`widget:opened`](#widget-opened)                                   | Widget     | Widget was opened                         |
| [`widget:closed`](#widget-closed)                                   | Widget     | Widget was closed                         |
| [`ticket:message_submitted`](#ticket-message_submitted)             | Ticket     | User sent a message                       |
| [`ticket:bot_response_finished`](#ticket-bot_response_finished)     | Ticket     | Bot completed its response                |
| [`ticket:agent_handoff`](#ticket-agent_handoff)                     | Ticket     | Ticket transferred to human agent         |
| [`ticket:agent_handoff_initiated`](#ticket-agent_handoff_initiated) | Ticket     | Human handoff process started             |
| [`ticket:link_clicked`](#ticket-link_clicked)                       | Ticket     | User clicked a link in chat               |
| [`ticket:input_focused`](#ticket-input_focused)                     | Ticket     | User focused the input field              |
| [`ticket:attachment_added`](#ticket-attachment_added)               | Ticket     | User attached a file                      |
| [`ticket:closed`](#ticket-closed)                                   | Ticket     | Conversation was closed                   |
| [`ticket:email_submitted`](#ticket-email_submitted)                 | Ticket     | User submitted their email during handoff |
| [`ticket:quiz_options_rendered`](#ticket-quiz_options_rendered)     | Ticket     | Quiz option buttons were displayed        |
| [`product:added_to_cart`](#product-added_to_cart)                   | E-commerce | User clicked "Add to Cart"                |
| [`products:added_to_cart`](#products-added_to_cart)                 | E-commerce | Multiple products added to cart           |
| [`product:page_opened`](#product-page_opened)                       | E-commerce | User clicked a product link               |
| [`product:displayed`](#product-displayed)                           | E-commerce | Product card was rendered                 |
| [`faqs:question_clicked`](#faqs-question_clicked)                   | FAQ        | User clicked a FAQ question               |
| [`faqs:message_submitted`](#faqs-message_submitted)                 | FAQ        | User submitted a FAQ question             |
| [`faqs:bot_response_finished`](#faqs-bot_response_finished)         | FAQ        | Bot finished FAQ response                 |
| [`faqs:link_clicked`](#faqs-link_clicked)                           | FAQ        | User clicked a link in a FAQ response     |
| [`experiment:loaded`](#experiment-loaded)                           | Experiment | A/B test group assigned                   |
| [`icebreaker_question:postback`](#icebreaker_question-postback)     | Widget     | User clicked an icebreaker                |
| [`nudge:loaded`](#nudge-loaded)                                     | Nudge      | A nudge was matched for the current page  |
| [`nudge:triggered`](#nudge-triggered)                               | Nudge      | Standard show logic activated the nudge   |
| [`nudge:hidden`](#nudge-hidden)                                     | Nudge      | A visible nudge should be taken down      |

***

## Widget Events

Events related to the widget's state and lifecycle.

### widget:loaded

Fired when the widget has finished loading and is ready for interaction. Use this event to safely set up other event handlers or perform initial configuration.

**Callback data:** None

```javascript
window.gleenWidget.on("widget:loaded", function () {
  console.log("Widget is ready");
  // Safe to call other widget methods now
  window.gleenWidget.showNudge({
    message: "Need help? Ask me anything!",
  });
});
```

***

### widget:opened

Fired when the chat widget is opened (expanded).

**Callback data:** None

```javascript
window.gleenWidget.on("widget:opened", function () {
  console.log("User opened the chat widget");
  // Track in analytics
  gtag("event", "chat_opened");
});
```

***

### widget:closed

Fired when the chat widget is closed (minimized).

**Callback data:** None

```javascript
window.gleenWidget.on("widget:closed", function () {
  console.log("User closed the chat widget");
});
```

***

## Ticket/Conversation Events

Events related to chat conversations and messages.

### ticket:message\_submitted

Fired when the user sends a message in the chat widget.

**Callback data:**

| Property       | Type   | Description                                    |
| -------------- | ------ | ---------------------------------------------- |
| `text`         | string | The message text sent by the user              |
| `submitAction` | string | How the message was sent — see the table below |

**`submitAction` values:**

| Value                        | The user…                                                               |
| ---------------------------- | ----------------------------------------------------------------------- |
| `button_click`               | Typed a message and clicked the send button                             |
| `enter_key`                  | Typed a message and pressed Enter                                       |
| `icebreaker_clicked`         | Tapped one of the starter questions shown before the conversation began |
| `suggested_question_clicked` | Tapped one of the follow-up questions suggested under a reply           |
| `quiz_option_clicked`        | Picked an answer in a guided quiz                                       |
| `unknown`                    | Sent the message some other way (for example, by clicking a nudge)      |

```javascript
window.gleenWidget.on("ticket:message_submitted", function (data) {
  console.log("User sent:", data.text);
});
```

***

### ticket:bot\_response\_finished

Fired when the AI bot has finished generating and streaming its response.

**Callback data:**

| Property | Type   | Description                    |
| -------- | ------ | ------------------------------ |
| `text`   | string | The complete bot response text |

```javascript
window.gleenWidget.on("ticket:bot_response_finished", function (data) {
  console.log("Bot responded:", data.text);
});
```

***

### ticket:agent\_handoff

Fired when a ticket has been successfully created in the helpdesk system after the user submits their email during human handoff.

**Callback data:**

| Property   | Type   | Description                  |
| ---------- | ------ | ---------------------------- |
| `ticketId` | string | The ID of the created ticket |
| `email`    | string | The user's email address     |

```javascript
window.gleenWidget.on("ticket:agent_handoff", function (data) {
  console.log("Ticket created:", data.ticketId);
  console.log("User email:", data.email);

  // Optionally close the widget after handoff
  setTimeout(function () {
    window.gleenWidget.close();
  }, 5000);
});
```

***

### ticket:agent\_handoff\_initiated

Fired when the AI determines that the conversation should be transferred to a human agent. At this stage, the AI is waiting for the user to submit their email address.

**Callback data:** None

```javascript
window.gleenWidget.on("ticket:agent_handoff_initiated", function () {
  console.log("Human handoff started - waiting for user email");

  // Example: Switch to another support widget
  // window.gleenWidget.closeTicket();
  // window.gleenWidget.close();
  // loadAlternativeSupportWidget();
});
```

***

### ticket:link\_clicked

Fired when the user clicks a link within a chat message.

**Callback data:**

| Property | Type   | Description              |
| -------- | ------ | ------------------------ |
| `url`    | string | The URL that was clicked |
| `text`   | string | The link text            |

```javascript
window.gleenWidget.on("ticket:link_clicked", function (data) {
  console.log("User clicked link:", data.url);
  // Track outbound link clicks
});
```

***

### ticket:input\_focused

Fired when the user focuses on the message input field.

**Callback data:** None

```javascript
window.gleenWidget.on("ticket:input_focused", function () {
  console.log("User is about to type a message");
});
```

***

### ticket:attachment\_added

Fired when the user attaches a file to their message.

**Callback data:**

| Property   | Type   | Description               |
| ---------- | ------ | ------------------------- |
| `fileName` | string | Name of the attached file |
| `fileType` | string | MIME type of the file     |
| `fileSize` | number | Size in bytes             |

```javascript
window.gleenWidget.on("ticket:attachment_added", function (data) {
  console.log("User attached:", data.fileName);
});
```

***

### ticket:closed

Fired when a conversation is closed.

**Callback data:**

| Property    | Type   | Description                 |
| ----------- | ------ | --------------------------- |
| `ticket_id` | string | The ID of the closed ticket |

```javascript
window.gleenWidget.on("ticket:closed", function (data) {
  console.log("Conversation closed:", data.ticket_id);
});
```

***

### ticket:email\_submitted

Fired when the user submits their email address during the human handoff flow, before the ticket is created in the helpdesk.

**Callback data:**

| Property | Type   | Description                             |
| -------- | ------ | --------------------------------------- |
| `email`  | string | The email address submitted by the user |

```javascript
window.gleenWidget.on("ticket:email_submitted", function (data) {
  console.log("User submitted email:", data.email);
});
```

***

### ticket:quiz\_options\_rendered

Fired when [quiz option](/docs/features/chat-widget/quiz-options.md) buttons are displayed in the conversation.

**Callback data:**

| Property    | Type   | Description                                  |
| ----------- | ------ | -------------------------------------------- |
| `options`   | array  | Array of option strings shown to the user    |
| `messageId` | string | The ID of the message containing the options |

```javascript
window.gleenWidget.on("ticket:quiz_options_rendered", function (data) {
  console.log("Quiz options shown:", data.options);
});
```

***

## E-commerce Events

Events for e-commerce integrations. These events help you connect the chat widget with your shopping cart and product pages.

{% hint style="info" %}
**Important:** By adding an event handler for the `product:added_to_cart` event, you will cause the "Add to Cart" button to appear in product cards. You must implement custom JavaScript specific to your e-commerce platform to handle this event.
{% endhint %}

### Where the add came from

Alhena can recommend products from more than one place on your store, and both add-to-cart events carry a `source` telling you which one the shopper used. Use it to attribute revenue to the right surface, or to route each surface's adds to different cart behavior.

| `source` value   | The shopper added the product from…                                          |
| ---------------- | ---------------------------------------------------------------------------- |
| `CHAT_WIDGET`    | The chat widget — typed chat or a voice conversation                         |
| `PRODUCT_FAQ`    | A [Product FAQ](/docs/features/product-faqs.md) block on a product page      |
| `AI_SEARCH`      | [Conversational Search](/docs/features/conversational-search.md) results     |
| `EMBEDDED_AGENT` | An [embeddable agent](/docs/features/embeddable-agents.md) — panel or inline |

***

### product:added\_to\_cart

Fired when a customer adds a single product to their cart from an Alhena surface — the "Add to Cart" button on a product card, or an add the AI makes on the shopper's behalf.

**Callback data:**

| Property          | Type   | Description                                                                    |
| ----------------- | ------ | ------------------------------------------------------------------------------ |
| `variantId`       | string | The product variant ID                                                         |
| `masterProductId` | string | The product ID                                                                 |
| `productName`     | string | The product title                                                              |
| `quantity`        | number | Quantity to add (usually 1)                                                    |
| `price`           | number | Product price                                                                  |
| `currency`        | string | Currency code for the price, such as `USD`                                     |
| `category`        | string | The product's category                                                         |
| `attributes`      | object | Variant options such as size or color, when your catalog has them              |
| `source`          | string | Which Alhena surface the add came from — [see above](#where-the-add-came-from) |

```javascript
window.gleenWidget.on("product:added_to_cart", function (data) {
  console.log("Add to cart - Variant:", data.variantId);
  console.log("Quantity:", data.quantity);
  console.log("Added from:", data.source);

  // Example: Add to Shopify cart via AJAX
  fetch("/cart/add.js", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      id: data.variantId,
      quantity: data.quantity,
    }),
  }).then(function () {
    // Show cart drawer or redirect to cart
    openCartDrawer();
  });
});
```

***

### products:added\_to\_cart

Fired when several products are added to the cart at once — for example from a product bundle, an outfit or room set, or quiz results.

**Callback data:** an array of product objects. Each product object contains the same fields as [`product:added_to_cart`](#product-added_to_cart):

| Property          | Type   | Description                                                                    |
| ----------------- | ------ | ------------------------------------------------------------------------------ |
| `variantId`       | string | The product variant ID                                                         |
| `masterProductId` | string | The product ID                                                                 |
| `productName`     | string | The product title                                                              |
| `quantity`        | number | Quantity to add                                                                |
| `price`           | number | Product price                                                                  |
| `currency`        | string | Currency code for the price, such as `USD`                                     |
| `category`        | string | The product's category                                                         |
| `attributes`      | object | Variant options such as size or color, when your catalog has them              |
| `source`          | string | Which Alhena surface the add came from — [see above](#where-the-add-came-from) |

```javascript
window.gleenWidget.on("products:added_to_cart", function (products) {
  console.log("Adding multiple products:", products.length);

  products.forEach(function (product) {
    // Add each product to your cart
    addToCart(product.variantId, product.quantity);
    console.log("Added from:", product.source);
  });
});
```

{% hint style="info" %}
If you only handle `product:added_to_cart`, Alhena falls back to firing that event once per product, so single-product handlers keep working for bundles.
{% endhint %}

***

### product:page\_opened

Fired when a customer clicks a product link rendered inside the chat widget.

**Callback data:**

| Property    | Type   | Description            |
| ----------- | ------ | ---------------------- |
| `variantId` | string | The product variant ID |
| `productId` | string | The product ID         |
| `url`       | string | The product page URL   |

```javascript
window.gleenWidget.on("product:page_opened", function (data) {
  console.log("User opened product:", data.variantId);
  // Optionally track product views
});
```

***

### product:displayed

Fired when a product card is rendered inside the chat widget.

**Callback data:**

| Property    | Type   | Description            |
| ----------- | ------ | ---------------------- |
| `variantId` | string | The product variant ID |
| `productId` | string | The product ID         |

```javascript
window.gleenWidget.on("product:displayed", function (data) {
  console.log("Product displayed:", data.variantId);
  // Track product impressions
});
```

***

## FAQ Events

Events for the [Product FAQ](/docs/features/product-faqs.md) feature.

### faqs:question\_clicked

Fired when a user clicks on an AI-generated FAQ question.

**Callback data:**

| Property | Type   | Description       |
| -------- | ------ | ----------------- |
| `text`   | string | The question text |

```javascript
window.gleenWidget.on("faqs:question_clicked", function (data) {
  console.log("FAQ question clicked:", data.text);
});
```

***

### faqs:message\_submitted

Fired when a user submits a custom question in the product FAQ text box.

**Callback data:**

| Property       | Type   | Description                                                                             |
| -------------- | ------ | --------------------------------------------------------------------------------------- |
| `text`         | string | The user's question                                                                     |
| `submitAction` | string | How the question was sent: `button_click` (clicked send) or `enter_key` (pressed Enter) |

Taps on the FAQ questions themselves fire [`faqs:question_clicked`](#faqs-question_clicked) rather than this event.

```javascript
window.gleenWidget.on("faqs:message_submitted", function (data) {
  console.log("FAQ question asked:", data.text);
});
```

***

### faqs:bot\_response\_finished

Fired when the AI completes its response in the FAQ widget. This applies to both clicked FAQ questions and custom user questions.

**Callback data:**

| Property | Type   | Description                |
| -------- | ------ | -------------------------- |
| `text`   | string | The AI's complete response |

```javascript
window.gleenWidget.on("faqs:bot_response_finished", function (data) {
  console.log("FAQ response:", data.text);
});
```

***

### faqs:link\_clicked

Fired when a user clicks a link within a FAQ response.

**Callback data:**

| Property | Type   | Description              |
| -------- | ------ | ------------------------ |
| `url`    | string | The URL that was clicked |

```javascript
window.gleenWidget.on("faqs:link_clicked", function (data) {
  console.log("FAQ link clicked:", data.url);
});
```

***

## Experiment Events

Events for A/B testing functionality.

### experiment:loaded

Fired when an active A/B test experiment is running and the user has been assigned to a group. See [A/B Testing](/docs/developer-reference/website-sdk/ab-testing.md) for full documentation.

**Callback data:** `string` — either `'test'` or `'control'`

```javascript
window.gleenWidget.on("experiment:loaded", function (experimentGroup) {
  console.log("User assigned to:", experimentGroup);
  // experimentGroup is 'test' or 'control'

  if (experimentGroup === "control") {
    // Load alternative support solution for control group
    loadZendeskWidget();
  }
});
```

***

## Icebreaker Events

Events related to conversation starters and quick replies.

### icebreaker\_question:postback

Fired when a user clicks an icebreaker question or quick reply button.

**Callback data:**

| Property           | Type   | Description                  |
| ------------------ | ------ | ---------------------------- |
| `question_text`    | string | The icebreaker question text |
| `type`             | string | The type of icebreaker       |
| `postback_payload` | string | The postback payload value   |

```javascript
window.gleenWidget.on("icebreaker_question:postback", function (data) {
  console.log("Icebreaker clicked:", data.question_text);
});
```

***

## Nudge Events

Events for [AI Nudges](/docs/features/chat-widget/nudges.md). These let you build a **custom nudge UI** driven by the same standard nudge logic (page matching, timer/scroll triggers, dismissal rules) that powers the built-in nudge.

{% hint style="info" %}
These events fire whether or not the default nudge UI is visible. To suppress Alhena's built-in launcher and nudge UI and render your own, set [`hideDefaultUI: true`](/docs/developer-reference/website-sdk/javascript-api.md#configuration-options-reference) in your config, then use [`clickNudge()`](/docs/developer-reference/website-sdk/javascript-api.md#clicknudge) to run the standard click behavior from your own elements.
{% endhint %}

All three events deliver the same payload describing the nudge:

| Property                  | Type   | Description                                                                                                             |
| ------------------------- | ------ | ----------------------------------------------------------------------------------------------------------------------- |
| `id`                      | string | The nudge's unique ID                                                                                                   |
| `nudge_type`              | string | The nudge type — `generic` or `product_faqs`                                                                            |
| `nudge_texts`             | array  | The nudge text(s) to display, trimmed and with empty entries removed                                                    |
| `nudge_button_text`       | string | The configured call-to-action button label (e.g. "Ask us"). May be empty, in which case the built-in UI shows no button |
| `nudge_header_title_text` | string | The configured nudge header title                                                                                       |

### nudge:loaded

Fired as soon as a nudge has been **matched** for the current page and its text is available — before any trigger condition (timer/scroll) is met. Use it to prepare your custom nudge UI ahead of time.

**Callback data:** the nudge payload described above.

```javascript
window.gleenWidget.on("nudge:loaded", function (nudge) {
  console.log("Nudge matched for this page:", nudge.nudge_texts);
  // Pre-render your custom nudge UI (still hidden) so it's ready to reveal
  prepareCustomNudge(nudge.nudge_texts[0], nudge.nudge_button_text);
});
```

***

### nudge:triggered

Fired when the standard nudge show logic activates — the trigger condition (timer elapsed or scroll threshold met) is satisfied, the nudge hasn't been dismissed, and the widget is closed. This is the moment a custom nudge UI should reveal itself.

**Callback data:** the nudge payload described above.

```javascript
window.gleenWidget.on("nudge:triggered", function (nudge) {
  // Reveal your custom nudge UI now
  showCustomNudge(nudge.nudge_texts[0]);
});
```

{% hint style="info" %}
When the visitor clicks your custom nudge, call [`window.gleenWidget.clickNudge()`](/docs/developer-reference/website-sdk/javascript-api.md#clicknudge) to run the exact same behavior (analytics + opening the conversation) as the built-in nudge.
{% endhint %}

***

### nudge:hidden

Fired when a nudge that was showing should now be taken down. This is the counterpart to `nudge:triggered` — hide your custom nudge UI here.

**Callback data:** the payload of the nudge that was showing (not of any nudge that matches next).

A single `nudge:hidden` covers every reason a nudge goes away, and the payload doesn't say which one it was. The common causes:

* The visitor interacted with the [Product FAQ](/docs/features/product-faqs.md) widget — clicked a question or sent a message
* The visitor dismissed the nudge with the built-in close button (suppressed for 30 minutes), or dismissed it in another browser tab
* The chat widget opened, including by clicking the nudge itself
* Your site called [`hideNudge()`](/docs/developer-reference/website-sdk/javascript-api.md#hidenudge)
* On a single-page app, the visitor navigated to a page that matches no nudge
* An [A/B test](/docs/developer-reference/website-sdk/ab-testing.md) placed the visitor in the control group

```javascript
window.gleenWidget.on("nudge:hidden", function (nudge) {
  // Take your custom nudge UI down
  hideCustomNudge();
});
```

{% hint style="info" %}
Nudge events are not replayed to handlers registered after they fire. Subscribe right after the SDK `<script>` tag — early registrations are preserved — rather than waiting for a later moment.
{% endhint %}

***

## Complete Example

Here's a complete example showing how to set up multiple event handlers:

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Alhena Events Example</title>
  </head>
  <body>
    <!-- Alhena SDK -->
    <script>
      document.gleenConfig = {
        company: "your-company-key",
        apiBaseUrl: "https://app.alhena.ai",
      };
    </script>
    <script src="https://app.alhena.ai/sdk/gleenWidget.js"></script>

    <script>
      // Widget lifecycle events
      window.gleenWidget.on("widget:loaded", function () {
        console.log("Chat widget ready");
      });

      window.gleenWidget.on("widget:opened", function () {
        gtag("event", "chat_widget_opened");
      });

      window.gleenWidget.on("widget:closed", function () {
        gtag("event", "chat_widget_closed");
      });

      // Conversation events
      window.gleenWidget.on("ticket:message_submitted", function (data) {
        gtag("event", "chat_message_sent", {
          message_length: data.text.length,
        });
      });

      window.gleenWidget.on("ticket:agent_handoff", function (data) {
        gtag("event", "human_handoff_completed", {
          ticket_id: data.ticketId,
        });
      });

      // E-commerce events
      window.gleenWidget.on("product:added_to_cart", function (data) {
        // Add to your cart
        addToCart(data.variantId, data.quantity);

        // Track in analytics
        gtag("event", "add_to_cart", {
          currency: "USD",
          items: [
            {
              item_id: data.productId,
              item_variant: data.variantId,
              quantity: data.quantity,
            },
          ],
        });
      });

      // A/B testing
      window.gleenWidget.on("experiment:loaded", function (experimentGroup) {
        gtag("event", "experiment_assigned", {
          experiment_group: experimentGroup,
        });
      });
    </script>
  </body>
</html>
```

***

## Related Resources

* [JavaScript API](/docs/developer-reference/website-sdk/javascript-api.md) - Complete method reference
* [A/B Testing](/docs/developer-reference/website-sdk/ab-testing.md) - Experiment setup and events
* [Cart & Checkout Events](/docs/developer-reference/website-sdk/cart-checkout-events.md) - Revenue tracking
* [Examples](/docs/developer-reference/website-sdk/examples.md) - Code examples for common use cases
