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

# Events

Embedded web chat supports a variety of events that allow you to trigger specific actions or customize behavior at different stages of the chat lifecycle. Events enable you to intercept, modify, or respond to user interactions and system state changes in real time.

The following tables list all supported events, grouped by category.

* [Conversation events](#conversation-events)
* [Customization events](#customization-events)
* [Feedback events](#feedback-events)
* [Lifecycle events](#lifecycle-events)
* [Message events](#message-events)
* [Security events](#security-events)
* [View events](#view-events)

## Conversation events

Conversation events track conversation lifecycle operations such as restarts and thread loading.

| Event name                                                 | Description                                                             |
| ---------------------------------------------------------- | ----------------------------------------------------------------------- |
| [`pre:restartConversation`](events/preRestartConversation) | Triggered before the conversation restarts.                             |
| [`restartConversation`](events/restartConversation)        | Triggered after the conversation restarts, before a new session begins. |
| [`pre:threadLoaded`](events/preThreadLoaded)               | Triggered before a conversation thread is loaded from history.          |

## Customization events

Customization events enable you to handle custom response types and render custom UI elements.

| Event name                                          | Description                                                                         |
| --------------------------------------------------- | ----------------------------------------------------------------------------------- |
| [`userDefinedResponse`](events/userDefinedResponse) | Triggered when a response contains an unrecognized or `user_defined` response type. |
| [`customEvent`](events/customEvent)                 | Triggered for custom events like button clicks.                                     |

## Feedback events

Feedback events track user interactions with feedback controls on messages. For a complete guide on implementing thumbs-up and thumbs-down feedback, see [Thumbs-up and thumbs-down feedback](events_feedback).

| Event name                    | Description                                                            |
| ----------------------------- | ---------------------------------------------------------------------- |
| [`feedback`](events/feedback) | Triggered when the user interacts with feedback controls on a message. |

## Lifecycle events

Lifecycle events track the initialization and readiness state of the chat widget.

| Event name                       | Description                                                                  |
| -------------------------------- | ---------------------------------------------------------------------------- |
| [`chat:ready`](events/chatReady) | Triggered when the web chat is fully loaded and ready to receive user input. |

## Message events

Message events allow you to intercept and modify messages as they flow between the user and the agent.

| Event name                                  | Description                                                                        |
| ------------------------------------------- | ---------------------------------------------------------------------------------- |
| [`pre:send`](events/preSend)                | Triggered before the web chat sends a message to the agent.                        |
| [`send`](events/send)                       | Triggered after the web chat sends a message to the agent.                         |
| [`pre:stream:delta`](events/preStreamDelta) | Triggered before each streaming delta chunk is processed and rendered in the chat. |
| [`pre:receive`](events/preReceive)          | Triggered before the web chat receives a response from the agent.                  |
| [`receive`](events/receive)                 | Triggered after the web chat receives a response from the agent.                   |

## Security events

Security events help manage authentication and token lifecycle.

| Event name                                  | Description                                                                    |
| ------------------------------------------- | ------------------------------------------------------------------------------ |
| [`authTokenNeeded`](events/authTokenNeeded) | Triggered when the JWT authentication token expires and needs to be refreshed. |

## View events

View events track changes to the chat widget's visual state and layout.

| Event name                                                     | Description                                                     |
| -------------------------------------------------------------- | --------------------------------------------------------------- |
| [`view:pre:change`](events/viewPreChange)                      | Triggered before the view state changes.                        |
| [`view:change`](events/viewChange)                             | Triggered after the view state changes.                         |
| [`view:properties:pre:change`](events/viewPropertiesPreChange) | Triggered before view properties (like maximized state) change. |
| [`view:properties:change`](events/viewPropertiesChange)        | Triggered after view properties (like maximized state) change.  |

## Complete example

The following example demonstrates how to configure multiple events in the embedded web chat:

```javascript [expandable] theme={null}
<script>
    function preSendHandler(event, instance) {
        console.log('pre:send event', event);
        if (event?.message?.message?.content) {
            event.message.message.content = event.message.message.content.toUpperCase();
        }
    }

    function sendHandler(event, instance) {
        console.log('send event', event);
    }

    function feedbackHandler(event, instance) {
        console.log('feedback', event);
        
        if (event.interactionType === 'details') {
            // Submit feedback to backend
            // Note: watsonx Orchestrate does not persist feedback internally
            fetch('/api/store-feedback', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'Authorization': `Bearer ${jwtToken}`
                },
                body: JSON.stringify({
                    messageId: event.message.id,
                    isPositive: event.isPositive,
                    text: event.text,
                    categories: event.categories
                })
            });
        }
    }

    function preReceiveHandler(event, instance) {
        console.log('pre-receive event', event);
        
        // Modify response text
        event?.message?.content?.forEach((element) => {
            if (element?.text?.includes('assistant')) {
                element.text = element.text.replace('assistant', 'Agent');
            }
            element.type = 'user_defined';
        });

        // Add feedback controls
        const lastItem = event?.message?.content?.[event.message.content.length - 1];
        if (lastItem) {
            lastItem.message_options = {
                feedback: {
                    is_on: true,
                    show_positive_details: false,
                    show_negative_details: true,
                    positive_options: {
                        categories: ['Helpful', 'Accurate', 'Clear'],
                        disclaimer: "Your feedback helps us improve.",
                    },
                    negative_options: {
                        categories: ['Inaccurate', 'Incomplete', 'Confusing', 'Other'],
                        disclaimer: "Please provide details to help us improve.",
                    },
                },
            };
        }
    }

    function receiveHandler(event, instance) {
        console.log('received event', event);
        instance.off('pre:receive', preReceiveHandler);
    }

    function userDefinedResponseHandler(event, instance) {
        console.log('userDefinedResponse event', event);
        event.hostElement.innerHTML = `
            <div style="background-color:#f4f4f4;padding:15px;border-radius:8px;">
                <h4>Custom Response</h4>
                <p>${event.contentItem?.text || '[No content]'}</p>
            </div>`;
    }

    function preStreamDeltaHandler(event, instance) {
        console.log('pre:stream:delta event', event);
        
        // Filter sensitive content in streaming responses
        event?.delta?.content?.forEach((contentItem) => {
            if (contentItem.response_type === 'text' && contentItem.text) {
                contentItem.text = contentItem.text.replace(/confidential/gi, '[REDACTED]');
            }
        });
        
        // Track streaming metrics
        const deltaLength = event?.delta?.content?.[0]?.text?.length || 0;
        console.log('Delta chunk size:', deltaLength);
    }

    function preRestartConversationHandler(event, instance) {
        console.log('pre:restartConversation event', event);
        console.log('Restart source:', event.interactionType);
        
        // Handle different restart sources
        if (event.interactionType === 'restart_button') {
            console.log('User clicked the Restart button');
        } else if (event.interactionType === 'new_chat_button') {
            console.log('User clicked the New Chat button');
        } else if (event.interactionType === 'instance_method') {
            console.log('Restart triggered programmatically');
        }
    }

    let hasRestarted = false;
    function restartConversationHandler(event, instance) {
        console.log('restartConversation event', event);
        console.log('Restart source:', event.interactionType);
        
        if (!hasRestarted) {
            setTimeout(() => {
                instance.send('Hello! Starting a new conversation.');
            }, 1000);
            hasRestarted = true;
        }
    }

    function preThreadLoadedHandler(event, instance) {
        console.log('pre:threadLoaded event', event);
        
        // Add feedback to historical messages
        event?.messages.forEach((message) => {
            if (message?.sender === 'response') {
                const [lastItem] = message.content;
                lastItem.message_options = {
                    feedback: { is_on: true }
                };
                message.message_state = {
                    content: {
                        1: {
                            feedback: {
                                text: "",
                                is_positive: true,
                                selected_categories: []
                            }
                        }
                    }
                };
            }
        });
    }

    async function authTokenNeededHandler(event, instance) {
        console.log('authTokenNeeded event', event);
        
        // Fetch refreshed token
        const newToken = await fetchRefreshedToken();
        event.authToken = newToken;
    }

    async function viewPreChange(event, instance) {
        console.log('view:pre:change event', event);
        document.body.classList.add('chat-view-changing');
    }

    async function viewChange(event, instance) {
        console.log('view:change event', event);
        document.body.classList.remove('chat-view-changing');
        
        if (event.newViewState.mainWindow) {
            analytics.track('chat_opened', { reason: event.reason });
        }
    }

    function onChatLoad(instance) {
        // Lifecycle events
        instance.on('chat:ready', (event, instance) => {
            console.log('chat:ready', event);
        });

        // Message events
        instance.once('pre:send', preSendHandler);
        instance.on('send', sendHandler);
        instance.on('pre:stream:delta', preStreamDeltaHandler);
        instance.once('pre:receive', preReceiveHandler);
        instance.on('receive', receiveHandler);

        // Conversation events
        instance.on('pre:restartConversation', preRestartConversationHandler);
        instance.on('restartConversation', restartConversationHandler);
        instance.on('pre:threadLoaded', preThreadLoadedHandler);

        // Customization events
        instance.on('feedback', feedbackHandler);
        instance.on('userDefinedResponse', userDefinedResponseHandler);

        // Security events
        instance.on('authTokenNeeded', authTokenNeededHandler);

        // View events
        instance.on('view:pre:change', viewPreChange);
        instance.on('view:change', viewChange);
    }

    window.wxOConfiguration = {
        orchestrationID: '<tenantId>',
        hostURL: 'http://localhost:3000',
        showLauncher: true,
        rootElementID: 'root',
        chatOptions: {
            agentId: '<agentId>',
            agentEnvironmentId: '<agentEnvironmentId>',
            onLoad: onChatLoad,
        },
    };

    setTimeout(function () {
        const script = document.createElement('script');
        script.src = `${window.wxOConfiguration.hostURL}/wxoLoader.js?embed=true`;
        script.addEventListener('load', function () {
            wxoLoader.init();
        });
        document.head.appendChild(script);
    }, 0);
</script>
```

## Best practices

### Event handler patterns

1. **Use `once()` for one-time operations**\
   If you only need to handle an event once, use `instance.once()` instead of `instance.on()` to automatically remove the handler after execution.

2. **Clean up event handlers**\
   Use `instance.off()` to remove event handlers when they're no longer needed to prevent memory leaks.

3. **Handle errors gracefully**\
   Wrap event handler logic in try-catch blocks to prevent errors from breaking the chat experience.

4. **Avoid blocking operations**\
   Keep event handlers lightweight and non-blocking. Use async operations for heavy processing.

### Modifying event data

* **pre:send**
  Modify `event.message.message.content` to change the user's message before sending.

* **pre:receive**
  Modify `event.message.content` to change the agent's response before rendering.

* **pre:stream:delta**
  Modify `event.delta.text` to transform streaming content in real-time before it's rendered.

* **pre:threadLoaded**
  Modify `event.messages` to customize historical messages before display.

* **view:pre:change**
  Modify `event.newViewState` to control the upcoming view state.

### Common use cases

* **Analytics tracking**
  Use `send`, `receive`, and `view:change` events to track user interactions.

* **Custom rendering**
  Use `userDefinedResponse` to render custom UI for specialized content types.

* **Token management**
  Use `authTokenNeeded` to implement automatic token refresh.

* **Message transformation**
  Use `pre:send` and `pre:receive` to transform messages in transit.

* **Streaming content processing**
  Use `pre:stream:delta` to filter, transform, or monitor streaming responses in real-time.

* **Feedback collection**
  Use `feedback` event to capture and process user feedback.
