Here is a comprehensive guide for all Forethought widget UI-related issues. Whether you are a developer or just curious, we are here to help!
Note: The widget's default states are managed through Forethought. If you would like to vary from default behavior or customize settings that are not changeable in the dashboard, your team will be responsible for maintenance.
Script Attributes and Values
-
data-api-key (UUID, Forethought provided)
- the required unique org authentication
-
data-workflow-version (string, default is absent)
- which version of the workflows are live
-
data-builder-preview (true | false, default is false)
- Specifies if this widget is showing a preview or is live (if actions / hand-off should really execute)
-
data-intent-title (string, default is absent)
- used to Display the workflows title
-
data-is-model-training (true | false, default is false)
- Indicated if the workflow routing model is still training or not
-
full-screen (true | false, default is false)
- controls if the widget automatically opens on load
-
hidden (non-empty string, default is absent)
- when a string is present widget will hide on load
-
hide-ft-after-zd (true | false, default is false)
- Keeps the Forethought widget hidden after ZD chat ends
-
emit-tracking-events (true | false, default is false)
- Will make Solve UI Widget emit tracking events that you can subscribe to via
window.addEventListener('message', callback)
- Will make Solve UI Widget emit tracking events that you can subscribe to via
Aesthetic Attributes
config-ft-theme-color
- string
Overrides the theme color. Example - #7b33fb
config-ft-agent-avatar-image
- string
Overrides the image URL to be used as the avatar image next to messages.
config-ft-widget-header-image
- string
Overrides the image URL to be used as the header image.
config-ft-widget-button-image
- string
Overrides the image URL to be used as the widget button when the widget is closed.
config-ft-widget-header-title
- string
Overrides the header title.
config-ft-greeting-message
- string
Overrides the initial greeting message when the widget is open.
config-ft-proactive-prompt-greeting-message
- string
Overrides the proactive prompt greeting message. Does nothing if proactive prompt is not enabled.
config-ft-hide-nav
- string - true
| false
(default)
Hides the widget header.
Widget Position Customizations
By default, the widget is placed at 20px away from the bottom right corner
We can customize it with placement attributes however:
- position-x (left | right)
- position-y (left | right)
- offset-x (/^[-]?\d+(px|rem)$/)
- offset-y (/^[-]?\d+(px|rem)$/)
Note: Using position-x="left"
and/or position-y="top"
along with Proactive Bubbles is not supported for all widget placements (UI will look weird).
Example Embedding Script:
<script
src={etc}
type='application/javascript'
data-api-key={lalalala}
position-y='top'
offset-y='5rem'
position-x='left'
offset-x='80px'
></script>
Persistence
- Chat and widget open-close state persistence is managed on the Forethought side.
- State Widget placement and view persistence are managed on your development side.
Changing Widget Behavior with Javascript API
By default, the Forethought widget will be displayed on any page where the embed snippet is present. You can change this and other behavior using the Javascript functions below.
type Forethought = (
selector: 'widget',
action: 'open' | 'close' | 'hide' | 'show' | 'triggerEventComplete',
data?: {
identifier?: string;
payload?: Record<string, unknown>;
},
) => void;
declare global {
interface Window {
Forethought: Forethought;
}
-
window.Forethought('widget', 'open')
- open Solve UI Widget -
window.Forethought('widget', 'close')
- close Solve UI Widget -
window.Forethought('widget', 'hide')
- hide Solve UI iframe -
window.Forethought('widget', 'show')
- show Solve UI iframe -
window.Forethought('widget', 'triggerEventComplete', {identifier: 'trigger_event', payload: {'another cv': 'my updated context value'}})
- used to continue the conversation after a trigger event step which comes from a trigger event action -
window.Forethought('widget', 'showProactivePrompt')
- show proactive prompt (ignores `config-ft-disable-proactive-prompt`, does nothing if proactive prompt is disabled in widget configuration) -
window.Forethought('widget', 'hideProactivePrompt')
- hide proactive prompt (does nothing if proactive prompt is disabled in widget configuration)
Add Script Dynamically to the DOM
The snippet can also be inserted into DOM dynamically (e.g. only on pages that need it) and removed by id (id of the iframe that holds the widget is forethought-chat). An example of adding it dynamically is below:
// Remove old widget, if it exists:
document.querySelector("#forethought-chat")?.remove();
const script = document.createElement("script");
script.setAttribute("src", "https://solve-widget.forethought.ai/embed.js");
script.setAttribute("id", "forethought-widget-embed-script");
script.setAttribute("data-api-key", "Insert API key here");
script.setAttribute("data-ft-foo", "bar"); // optional
document.head.append(script);
Mobile SDK Installation
The recommended mobile solution is to use our Android and iOS SDKs found here. Installation and Usage guides can be found there.
- iOS - https://github.com/Forethought-Technologies/solve-ios
- Android - https://github.com/Forethought-Technologies/solve-android
For more information, see SDK Installation.
Manipulate Widget Behavior
Passing Custom Parameters
To pass parameters that can afterwards be used in conditions that determine behavior, add an attribute prefixed with data-ft- to the script tag.
In the example below, we"ve added a usertype parameter to the script.
<script
src="https://solve-widget.forethought.ai/embed.js"
id="forethought-widget-embed-script"
data-api-key="Insert API key here"
data-ft-usertype="paid"
></script>
There are a few ways your engineering team can get necessary values to pass in - take them from the page contents (e.g. if user email is specified in the profile section on the page), by making an API call to one of your services and using the response value, or by taking relevant info from the window object if it is there.
The attributes are all strings, so if you"re looking to pass a list of values, pass it as a delimiter-concatenated string of “|” (e.g. for [1,2,3] pass “1|2|3”)
Additional Attributes
- config-ft-theme-color - string - Overrides the theme color. Example - "#7b33fb"
- config-ft-agent-avatar-image - string - Overrides the image URL to be used as the avatar image next to messages.
- config-ft-widget-header-image - string - Overrides the image URL to be used as the header image.
- config-ft-widget-button-image - string - Overrides the image URL to be used as the widget button when the widget is closed.
- config-ft-widget-header-title - string - Overrides the header title.
- config-ft-greeting-message - string - Overrides the initial greeting message when the widget is open.
- config-ft-disable-proactive-prompt - string - "true" - Overrides the visibility of the proactive prompt. Does nothing if proactive prompt is not enabled.
- config-ft-proactive-prompt-greeting-message - string - Overrides the proactive prompt greeting message. Does nothing if proactive prompt is not enabled.
- config-ft-hide-nav - string - "true" | "false" (default) - Hides the widget header.
-
config-ft-disable-close - string - "true" | "false" (default) - Hides the "Minimize chat" button in the chat header.
-
config-ft-banner-image-enabled - string - "true" | "false" (default)- Overrides whether the banner image is enabled or not.
-
config-ft-banner-image - string - Image URL. Overrides the banner image if that is enabled.
-
config-ft-banner-image-alt-text - string - Overrides the banner image alt text.
-
config-ft-banner-image-link - string - Overrides the banner redirect URL.
-
config-ft-show-when-conversation-starts - string - "true" | "false" (default) - Overrides whether the privacy consent should be shown when conversation starts
-
config-ft-privacy-policy - string - Overrides the privacy policy shown in the privacy consent modal
-
config-ft-prompt-header - string - Overrides the prompt header of the privacy consent modal
-
config-ft-call-to-action-label - string - Overrides the call to action label of the privacy consent modal
- initial-intent-id -string - conversation will start the specific workflow ID put here
-
initial-query - string - initialize conversation with a specific query
-
hide-intercom-widget - string - "true" (default) | "false" - when false, Intercom will not be hidden on page load.
-
emit-tracking-events - "true" | "false" (default) - Will make Solve UI Widget emit tracking events that you can subscribe to via `window.addEventListener('message', callback)`.
-
config-ft-persistence-id - string - Creates a separate persistence store for this `config-ft-persistence-id`
-
Example: use `config-ft-persistence-id="my-persistence-id-1"` in one snippet and `config-ft-persistence-id="my-persistence-id-2"` in another to have 2 separate persisted widgets.
-
Note: if you use `config-ft-persistence-id`, it disables the default
`data-ft-workflow-tag` persistence behavior. So if you need to also have
separate persistence stores for different workflow tags, you need to make your
workflow tags be a part of `config-ft-persistence-id` value, e. g. `config-ft-persistence-id="my-persistence-id-1 workflow-tag-1 workflow-tag-2"`
-
-
config-ft-ignore-persistence-parameters - pipe-delimited string - Do not reset widget when specified embed script attributes change
- Example: `config-ft-ignore-persistence-parameters="data-ft-location|data-ft-auth"`
- auto-open-zd - "true" (default) | "false" - When false, Forethought will show the Zendesk widget if a chat is active on page load but will not open the Zendesk widget.
Display Script Parameters
-
full-screen - "true" | "false" (default)
- When true, the widget will open automatically on load.
-
hidden - non-empty string, default: absent
- When set to a non-empty string, the widget iframe will get hidden automatically on load.
-
hide-ft-after-zd - true or false (default)
- Will keep Solve UI Widget hidden after a Zendesk chat is ended.
Position Script Parameters
- position-x - left or right (default)
- position-y - top or bottom (default)
- offset-x - horizontal offset - default 20px (can be px or rem)
- offset-y - vertical offset - default 20px (can be px or rem)
Parameters Interacting with Forethought Widget
By default, the Forethought widget will be displayed on any page where the embed snippet is present. You can change this and other behavior using the Javascript functions below.
- window.Forethought("widget", "open") - open Solve UI Widget - picture below
- window.Forethought("widget", "close") - close Solve UI Widget - picture below
- window.Forethought("widget", "hide") - hide Solve UI iframe
- window.Forethought("widget", "show") - show Solve UI iframe
- window.Forethought('widget', 'triggerEventComplete', {identifier: 'trigger_event', payload: {'another cv': 'my updated context value'}}) - used to continue the conversation after a trigger event step which comes from a trigger event action
- window.Forethought('widget', 'clearLocalData') - clear data in localStorage (useful for logging out)
- window.Forethought('widget', 'launchQuery', {payload: {query: string}}) - clears data and initializes a new conversation with provided query
Opened | Closed |
If you want to make one of these calls on page load, please subscribe to the forethoughtWidgetLoaded event, and make your call when the event is received, for example:
class="c-mrkdwn__pre" data-stringify-type="pre"window.addEventListener('message', (event) => {
if (event.data.event === 'forethoughtWidgetLoaded') {
Forethought("widget", "open")
}
});
List of Solve Widget events
// Fired when Solve UI Widget iframe gets hidden:
interface HideSolveWidgetEvent extends MessageEvent {
data: {
event: 'hideSolveWidget';
};
}
// Fired when Solve UI Widget iframe gets shown:
interface ShowSolveWidgetEvent extends MessageEvent {
data: {
event: 'showSolveWidget';
};
}
// Hides Solve UI iframe, does NOT get fired by Solve UI:
interface HideIframeWidgetEvent extends MessageEvent {
data: {
event: 'hideIframeWidget';
};
}
// Shows Solve UI iframe, does NOT get fired by Solve UI:
interface ShowIframeWidgetEvent extends MessageEvent {
data: {
event: 'showIframeWidget';
};
}
// Fired when the chat gets opened:
interface ForethoughtWidgetOpenedEvent extends MessageEvent {
data: {
event: 'forethoughtWidgetOpened';
};
}
// Fired when the chat gets closed:
interface ForethoughtWidgetClosedEvent extends MessageEvent {
data: {
event: 'forethoughtWidgetClosed';
};
}
// Fired when Solve UI Widget starts initializing:
interface ForethoughtWidgetStartedInitializingEvent extends MessageEvent {
data: {
event: 'forethoughtWidgetStartedInitializing';
};
}
// Fired when Solve UI Widget gets loaded (initialized):
interface ForethoughtWidgetLoadedEvent extends MessageEvent {
data: {
event: 'forethoughtWidgetLoaded';
/**
* Needed to not hide forethought when zendesk widget
* is on a page and we are in a one chat
*/
isLiveChatMode: boolean;
// `true` if chat is open on load, `false` if closed:
isOpen: boolean;
};
}
// Fired when Solve UI Widget gets loaded (initialized):
interface SolveWidgetLoadedEvent extends MessageEvent {
data: {
event: 'solveWidgetLoaded';
};
}
// Fired when Solve UI Widget gets closed:
interface SolveWidgetClosedEvent extends MessageEvent {
data: {
event: 'solveWidgetClosed';
};
}
// Fired when Solve UI Widget iframe gets resized (only when the chat is
// closed):
interface ForethoughtWidgetResizeEvent extends MessageEvent {
data: {
event: 'forethoughtWidgetResize';
dimensions: {
height: string;
width: string;
};
};
}
// Fired when Solve UI Widget iframe gets resized to maximum values:
interface ForethoughtWidgetResizeToMaximumValuesEvent extends MessageEvent {
data: {
event: 'forethoughtWidgetResizeToMaximumValues';
};
}
// Fired when performing a handoff to UJET:
interface ForethoughtWidgetHandoffEvent extends MessageEvent {
data: {
event: 'forethoughtWidgetHandoff';
ticketId: string;
};
}
// Fired when performing a generic handoff:
interface ForethoughtWidgetGenericHandoffEvent extends MessageEvent {
data: {
event: 'forethoughtWidgetGenericHandoff';
};
}
// Fired when performing a handoff:
interface ForethoughtWidgetIntegrationHandoffEvent extends MessageEvent {
data: {
event: 'forethoughtWidgetIntegrationHandoff';
additionalParameters?: Record<string, unknown="unknown">;
department?: string;
email: string;
featureFlags?: string[];
integration: HandoffIntegrations;
name: string;
question: string;
};
}
/**
* Fired when a handoff is completed. This does not mean
* the customer's handoff conversation is completed.
*
* success contains whether we were successful or not
*/
export interface ForethoughtWidgetIntegrationHandoffCompletedEvent
extends MessageEvent {
data: {
event: 'forethoughtWidgetIntegrationHandoffCompleted';
handoffData: HandoffData;
success: boolean;
};
}
// Fired when performing a generic handoff:
interface ForethoughtWidgetGenericIntegrationHandoffEvent extends MessageEvent {
data: {
event: 'forethoughtWidgetIntegrationHandoff';
additionalParameters: Record<string, unknown="unknown">;
integration: HandoffIntegrations;
widgetOption: GenericHandoffWidgetOption;
};
}
// Fired when widget is in preview mode, and receives preview_logs from the server for
// conversation related requests
interface ForethoughtWidgetPreviewLogsEvent extends MessageEvent {
data: {
event: 'forethoughtWidgetPreviewLogsEvent';
previewLogs?: PreviewLog[];
conversationId?: string;
};
}
// Fired when widget is in preview mode, and the restart chat button is clicked
interface ForethoughtWidgetPreviewRestartEvent extends MessageEvent {
data: {
event: 'forethoughtWidgetPreviewRestartEvent';
};
}
// Fired when tracking event gets sent (it's an opt-in event, it has to be
// enabled explicitly via `emit-tracking-events="true"` embed script attribute):
interface ForethoughtWidgetTrackingEvent extends MessageEvent {
data: {
event: 'forethoughtWidgetTrackingEvent';
conversation_id: string | undefined;
} & (
| {
event_type: 'widget-opened';
mechanism:
| 'manual-click-on-widget-icon'
| 'manual-click-on-proactive-greeting'
| 'manual-click-on-proactive-intent-button'
| 'js-api-widget-open'
| 'js-api-widget-launchQuery'
| 'full-screen-true';
}
| {
event_type: 'widget-customer-ask-question';
}
| {
event_type: 'widget-customer-closed-proactive-bubbles';
}
| {
event_type: 'widget-customer-proactive-bubbles-shown';
}
| {
doc_id: string;
doc_index: number;
event_type: 'go-to-article';
}
| {
event_type: 'widget-customer-entered-intent-by-proactive-bubble';
intent_id?: string;
workflow_id?: string;
}
| {
event_type: 'widget-customer-entered-intent-by-button';
intent_id?: string;
workflow_id?: string;
}
| {
event_type: 'widget-customer-agent-chat';
intent_id?: string;
workflow_id?: string;
}
| {
event_type: 'widget-customer-article-suggestion-helped';
intent_id?: string;
workflow_id?: string;
}
| {
event_type: 'widget-customer-article-suggestion-unhelpful';
intent_id?: string;
workflow_id?: string;
}
| {
event_type: 'widget-banner-image-clicked';
}
| {
event_type: 'widget-banner-image-closed';
}
);
}
// Fired by trigger event step
interface ForethoughtWidgetTriggerEvent extends MessageEvent {
data: {
additionalContext: TriggerEventStepAdditionalContext;
event: 'forethoughtTriggerEventAction';
expectedContextVariables: string[];
name: string;
};
}
interface ForethoughtWidgetViewImageEvent extends MessageEvent {
data: {
event: 'forethoughtWidgetViewImage';
open: boolean;
};
}
interface ForethoughtWidgetErrorEvent extends MessageEvent {
data: {
error: string;
event: 'forethoughtWidgetError';
};
}
type ForethoughtEvent =
| HideSolveWidgetEvent
| ShowSolveWidgetEvent
| HideIframeWidgetEvent
| ShowIframeWidgetEvent
| ForethoughtWidgetOpenedEvent
| ForethoughtWidgetClosedEvent
| ForethoughtWidgetStartedInitializingEvent
| ForethoughtWidgetLoadedEvent
| SolveWidgetLoadedEvent
| SolveWidgetClosedEvent
| ForethoughtWidgetResizeEvent
| ForethoughtWidgetResizeToMaximumValuesEvent
| ForethoughtWidgetHandoffEvent
| ForethoughtWidgetGenericHandoffEvent
| ForethoughtWidgetIntegrationHandoffEvent
| ForethoughtWidgetGenericIntegrationHandoffEvent
| ForethoughtWidgetPreviewLogsEvent
| ForethoughtWidgetPreviewRestartEvent
| ForethoughtWidgetTrackingEvent
| ForethoughtWidgetTriggerEvent
| ForethoughtWidgetViewImageEvent
| ForethoughtWidgetErrorEvent
| ForethoughtWidgetIntegrationHandoffCompletedEvent;
// Usage example:
window.addEventListener('message', (event: ForethoughtEvent) = {
if (event.data.event === '...') {
// ...
}
});
Hide the Forethought Solve Widget from Unauthenticated Users
To make sure only signed-in users see the Forethought widget, wrap the embed snippet in a simple if-else block like this:
// Widget embed script: only show to logged-in Zendesk users
{{#if signed_in}}
<script
src="https://solve-widget.forethought.ai/embed.js"
type="application/javascript"
data-api-key="Insert Api Token Here"
></script>
{{else}}
{{/if}}