The Fini chat widget puts your Fini AI agent in a chat panel on your public website, inside your authenticated web app, in your iOS and Android apps, or on your help center, all from one embed snippet and one bot configuration. When the agent can’t resolve a conversation, the widget escalates it to your helpdesk as a real ticket: Zendesk, Front, Gorgias, Salesforce, and HubSpot are supported destinations. The Fini widget is a chat experience your customers can use directly on your website, app, or help center. Drop a small JavaScript snippet into your site, choose whether the widget opens as a floating corner bubble or renders as a standalone inline panel, and manage the bot, branding, suggested questions, reference links, and escalation destination from Fini. For logged-in users, you can identify the user to the bot via a signed JWT so the agent answers with full context: who they are, what plan they’re on, what account they belong to.

Where the widget runs

Your public website

Anonymous visitors and prospects. Drop the snippet into any page; no authentication required.

Your authenticated web app

Logged-in customers. Pass a signed JWT so the agent knows who’s asking and personalizes the response with the user’s attributes.

iOS and Android

Native mobile apps. Same widget, same configuration, talk to your Fini contact for the current iOS and Android embedding setup.

Your help center

A complement to the standalone help center surface. Embed the widget on docs pages so readers can ask the agent without leaving the article.

A standalone support route

An inline panel for pages where chat should be the main experience, such as /support.

Before you create your widget

You’ll need:
  • At least one bot already created in Fini and trained on your knowledge. Each widget is tied to one bot; the bot can’t be changed after creation. Create the bot first from the home page.
  • The domain(s) where the widget will live (for example, https://yourcompany.com, app.yourcompany.com, or wildcards like *.yourcompany.com). Each widget can serve multiple domains.
  • (Optional) A connected helpdesk integration if you want widget conversations to escalate to a human ticket. Today the supported destinations are Zendesk, Front, and Salesforce. Connect the destination integration first under Deploy.

Create your widget

Open Deploy → Widget in Fini. If no widget exists yet, the creation form opens directly; if one or more widgets already exist, pick Create new from the top-right.
Widget deploy page in creation mode, showing the empty form
1

Pick the bot

From the Select bot dropdown, choose which Fini bot will answer questions in this widget. Each widget is bound to one bot; once you create the widget you cannot swap the bot, create a new widget instead.
2

Fill in the basics

  • Domain: the URL(s) where the widget will be embedded. Multi-domain widgets are supported, enter one domain per line. Wildcards work (e.g., *.yourcompany.com to cover every subdomain).
  • Widget title: the name shown in the widget header.
3

Click Create

The widget is provisioned. The page switches to the two-tab view: Embed Script and Settings.

Embed it on your site

The Embed Script tab gives you a JavaScript snippet to paste into your site’s HTML.
The Widget page on the Embed Script tab, showing the script snippet inside a dark code panel with the widget id field masked. The page header explains where to paste the snippet, with notes that it must go inside or after the body tag and that localhost works for testing without changing the domain. A Copy button sits below the snippet, with Preview and Delete actions in the top-right of the page.
The snippet looks like this (with your widget id substituted in):
1

Click Copy

The Copy button puts the entire snippet on your clipboard.
2

Paste it into your site

Paste the snippet inside or just after the closing </body> tag of every page where you want the widget. Most teams put it in a global layout template or footer partial so it shows up site-wide.
3

Preview before going live

Use the Preview dropdown above the snippet to open a preview window without deploying. A new browser window opens with the widget rendered against your live bot.
The widget works on localhost without a domain match, you can test locally before configuring the production domain. For production, the live domain you embed on must match one of the entries in the widget’s Domain field.
The Preview dropdown is the fastest way to see the actual rendered widget, your brand color, logo, and configured greeting, without having to embed the snippet anywhere. Use it before going live and after every Settings change to confirm the experience looks right.

Widget modes

The mode option in the embed snippet controls how the widget renders:

mode: "widget"

Bubble in the corner of the page. The default. Users click to open the chat; the widget collapses when minimized.

mode: "standalone"

Inline chat panel that fills its parent container. Useful when you want the chat as a first-class element on the page (a dedicated /support route, for example) rather than a floating bubble.
If you omit mode, the widget uses the floating bubble behavior.

Opening, closing, and starting again

In widget mode, Fini renders a floating button in the bottom-right corner of the page.
  • When the widget is closed, the button shows your brand logo.
  • When the widget is open, the button switches to a close icon.
  • Clicking the button toggles the panel open or closed.
  • The open and close transition scales the panel in and out from the button.
Once a conversation has started, the widget header shows the widget title, description, logo, and a Start Again button. Start Again clears the current conversation, message draft, loading state, and any displayed error. It is disabled while the agent is actively responding. In standalone mode, there is no floating launcher. The chat panel renders inline where the snippet is mounted, so the host page controls where the panel appears.

SDK controls

The widget exposes a small customer-side SDK through window.fini. The embed snippet creates a queue so you can call SDK commands before the widget bundle has fully loaded. Use SDK commands when your product UI needs to control the widget directly:
What each command does:
function
Opens the floating widget panel.
function
Closes the floating widget panel.
function
Opens the widget and sends message as the customer’s next question. Use this when a button, search result, or in-product prompt should start a conversation automatically.
You can also subscribe to widget events:
Use subscriptions for analytics, product instrumentation, or custom UI side effects. For example, you can track when customers open the widget from a billing page, close it, or trigger a pre-filled support question from your own interface.

Answer rendering

Widget answers render Markdown. The same answer renderer is used in the floating widget, standalone widget, and main embedded widget surfaces. The widget supports common Markdown patterns including paragraphs, links, bulleted and numbered lists, inline code, fenced code blocks, and GitHub-flavored Markdown tables. This lets the agent return structured troubleshooting steps, comparison tables, and code snippets without losing formatting in the chat UI.
Keep tables and code blocks compact. They render inside the chat panel and can scroll horizontally when needed, but short labels and concise examples are easier for customers to read.

Loading and notification states

While the agent is responding, the widget shows a loading state:
  • The send button switches from the send icon to a spinner.
  • The assistant message area shows a pulsing skeleton loader.
  • The bot logo pulses while the answer is being generated.
  • The input is guarded so customers cannot submit another message while a response is in progress.
The widget can also notify customers when a new answer arrives and the widget is not the active focus:
  • Browser notifications show a native browser notification when supported and allowed by the customer.
  • Sound notifications play an audio cue for new widget activity when enabled.
  • Pulse notifications visually pulse the launcher so the customer can see that a new message is waiting.
Browser notifications depend on the customer’s browser permission. If the customer blocks notifications, the widget still uses in-page visual states such as pulsing and loading indicators.

Customer implementation checklist

Customers do not need to build their own loading UI. Loading states are handled by the widget bundle after the embed script is installed:
  1. Paste the widget snippet on the page.
  2. Pass the correct widgetId.
  3. Set mode to widget or standalone.
  4. Configure notification behavior in finiWidgetOptions.notifications.
Notification configuration lives in the customer-side script:
For browser notifications:
  1. Serve the page over HTTPS. Browser notifications generally require a secure origin.
  2. Set notifications.browser to "ask" or "always".
  3. Let the widget request notification permission when the customer interacts with it.
  4. If the customer blocks browser notifications, ask them to re-enable notifications from their browser’s site settings.
For sound notifications:
  1. Set notifications.sound to true.
  2. Make sure the widget is loaded on pages where sound is acceptable for your customer experience.
  3. Remember that browsers may block sound until the customer has interacted with the page.
For pulse notifications:
  1. Set notifications.pulse to true.
  2. Use mode: "widget" if you want the floating launcher to pulse for new activity.
  3. Use mode: "standalone" when the chat is always visible; in that mode, the page itself controls where the panel appears.
If you are using a custom Content Security Policy, allow the widget script from storage.googleapis.com and any browser APIs required by your notification policy. Overly strict script, media, or notification policies can prevent sound or browser notifications from working.

Page URL tracking

Fini can record the URL of the page where each widget message is sent. This helps your team understand where a customer was when they asked a question, and gives the agent more page context when reviewing the conversation. Customers do not pass the page URL manually in the embed snippet. Turn it on from Widget Settings → Advanced → Track current page URL. When enabled, the widget captures the current browser URL automatically for messages sent from that page. Use this when the same widget appears across product pages, account pages, help center articles, or checkout flows and you want analytics or conversation review to include the page source.

Identify logged-in users

When you’re embedding the widget inside an authenticated experience (your web app, your mobile app, your customer portal), you can tell the bot who the user is by passing a signed JWT as the customerToken. The bot then has access to the user’s email, name, and any custom attributes you include, so it can answer questions like “what plan am I on?” without asking the user to repeat themselves.

Payload shape

Sign a JWT on your server with this payload structure:
What each field does:
string
The user’s email address. The agent uses this for personalization and can pass it through to helpdesk tickets on escalation.
string
The user’s display name. Shown in conversation context and used in greeting personalization.
boolean
Whether the widget should ask the user to confirm their email before starting the conversation. Set false when you already have a verified email from your auth system; set true when you want a confirmation step regardless.
"web" | "mobile"
The surface the conversation is happening on. Pass "web" for browser-based widgets (your public site or authenticated web app) and "mobile" for your native iOS and Android apps. Fini uses this to distinguish where a conversation originated, so you can segment analytics and tailor agent behavior by surface. Optional, omit it if you don’t need to differentiate surfaces.
object
An arbitrary object of custom fields. Whatever you put here becomes available to the agent, plan tier, account ID, segment flags, anything that helps the bot personalize answers. The agent references these via User Attributes configured in your workspace.

Signing the token

Sign with the HS256 algorithm using your widget’s Signing Key, find it in the Settings tab under the Signing Key section.
Sign JWTs server-side only. The signing key is a secret. Never ship it in browser code, never commit it to source control, never paste it in screenshots. If it leaks, regenerate (contact your Fini contact) and rotate.
Node.js example:
Pass the signed token to the widget by setting customerToken in finiWidgetOptions, the same place where widgetId and mode live.

Configure your widget

All configuration lives on the Settings tab.
Widget Settings page in the Fini Demo workspace showing the Settings tab, Preview menu, Venmo Support widget selector, Connection Details, General setup, Branding, Advanced, Signing Key, Connected Integration, Track current page URL, Collect visitor email, and Save button

Connection details

Read-only metadata about the widget, set at creation time:
datetime
When the widget was created.
string
The teammate who deployed the widget.
string
The bot bound to this widget. Locked after creation, to use a different bot, create a new widget.

General setup

string
The URL(s) where the widget is embedded. Fini uses these as the allowed origins for the widget script. Enter multiple domains on separate lines; wildcards like *.yourcompany.com are supported.
string
Header label shown in the widget chrome (e.g., “Acme AI”, “Support”).
string
Short text shown near the top of the floating widget. Use it to set expectations for what the agent can help with.

Branding

The Branding section of widget settings with controls for the widget's brand color, brand logo, pre-defined questions, and thinking messages.
Visual customization for the widget’s chat experience:
string
The brand color used for the widget’s send button, user message bubbles, and accent elements. Hex format (e.g., #1D84FF).
The icon shown in the widget header next to the bot name. Image upload (PNG recommended); max 2 MB. Use a square, high-contrast logo so it reads at small sizes.

Thinking messages

Thinking messages are the short phrases shown while the bot is preparing a reply. Configure them from Widget Settings → Branding → Thinking messages.
array
Three short phrases that rotate in the widget while an answer is being generated. The messages cycle every 4 seconds.
Use short, reassuring copy. For example:
  • Pulling up the information...
  • Double checking the details...
  • Putting it all together...
Customers do not need to implement this state in their own code. After the widget is embedded, Fini displays the configured thinking messages automatically during the loading state.

Welcome text

The welcome text appears before the customer starts a conversation. In the floating widget, it appears under the header and brand logo. In standalone mode, it appears at the top of the empty chat panel. Use one short sentence that tells customers what to ask. For example: Ask us about orders, returns, account settings, or billing.

Predefined questions

Surface suggested prompts on the widget’s greeting screen so visitors know what they can ask. Predefined questions appear as tappable chips before the conversation starts. When a customer clicks one, the widget submits that question immediately. Choose 3-5 high-leverage questions that cover your top tickets, for example:
  • How do I reset my password?
  • Where is my order?
  • Cancel my subscription
Predefined questions disappear after the conversation starts. When reference links are enabled, the widget can show a collapsible Learn more via… section under an assistant answer. The widget deduplicates links before showing them:
  • Public source links can appear as clickable reference chips.
  • Private Google Cloud Storage references are counted as private links but are not exposed as clickable customer links.
  • If no public links are available, the widget can still tell the customer that private references were used.
Turn reference links off if you do not want customers to see source links below answers.

Notifications

Notification settings are controlled from the customer-side notifications object in finiWidgetOptions.
boolean
Set to true to play a sound when there is new widget activity. Set to false to keep the widget silent.
"never" | "ask" | "always"
Controls browser notifications. Use "never" to disable browser notifications, "ask" to request permission when needed, or "always" to show notifications whenever browser permission is already available.
boolean
Set to true to pulse the floating widget launcher when the widget is closed and there is new activity. This is most useful with mode: "widget".

Advanced

Advanced settings control conversation metadata and visitor identity behavior.

Signing Key

The secret used to sign JWTs for authenticated widget conversations. See Identify logged-in users for the JWT flow. The key is read-only in the UI, you can copy it but not regenerate or edit it from this page. Contact your Fini contact if you need a rotation.

Track current page URL

Off by default. When enabled, Fini records the URL of the page each message is sent from, useful for analytics and bot context when reviewing conversations. Turn it on when the widget appears across multiple pages and you want each message to carry its source page URL automatically.

Collect visitor email

Off by default. When enabled, the widget asks visitors for their email before starting a chat. Leave it off when your authenticated app already passes a verified email through the widget token.

Escalate to your helpdesk

When the agent in the widget can’t resolve a conversation, the conversation can hand off to a human agent as a real ticket in your existing helpdesk. Widget escalation is configured through Business Rules, not from the widget settings panel. Today, five integrations work as widget escalation destinations:

Zendesk

Use a Business Rule template or custom rule.

Front

Use a Business Rule template or custom rule.

Gorgias

Use a Business Rule template or custom rule.

Salesforce

Use a Business Rule template or custom rule.

HubSpot

Use a Business Rule template or custom rule.
General flow regardless of destination:
1

Connect the destination helpdesk first

Authorize the integration on its Deploy page. You don’t need to configure the helpdesk’s own Agent Routing or Reply Settings if you’re using it only as a widget escalation destination, just authorize.
2

Open Rulebook → Business Rules

Business Rules run when a widget conversation escalates. They own ticket creation, handoff metadata, and any final customer-facing message.
3

Create a default or custom Business Rule

Start from a default escalation template when it matches your helpdesk workflow, or create a custom rule when routing depends on attributes, fields, or provider-specific behavior.
4

Map the integration fields

Map the template fields or Tool inputs the rule needs, such as the destination channel, queue, case fields, customer email, transcript, or interaction ID.
5

Test and save the Business Rule

Use Test Rule before relying on the handoff in production. The widget will use the published Business Rule when the conversation escalates.

Destination-specific notes

Zendesk, Front, Gorgias, Salesforce, and HubSpot each need a connected integration before Business Rules can hand off to them.
  • Zendesk: connect Zendesk first on the Zendesk deploy page. For Chat escalations, customer-uploaded files from the widget are forwarded into the Zendesk conversation as native attachments when Zendesk accepts the file. The transcript still includes download links as a fallback.
  • Front: connect Front first on the Front deploy page. Front escalations use email channels.
  • Gorgias: connect Gorgias first on the Gorgias deploy page. Fini creates a Gorgias ticket from the widget escalation and suppresses the integration webhook for that ticket, so Fini does not treat its own escalation as a new Gorgias conversation to answer.
  • Salesforce: connect Salesforce first on the Salesforce deploy page. Fini creates a Salesforce Case with the connected integration credentials; case ownership and queueing follow your Salesforce assignment rules.
  • HubSpot: connect HubSpot first on the HubSpot deploy page. Fini can create a HubSpot ticket directly or submit a published HubSpot support form, depending on the Business Rule template. Direct ticket creation uses the first active open ticket stage unless your rule maps a specific pipeline stage.
For the full configuration flow, see Business Rules.

Verify it’s working

1

Preview the widget

Open the Preview dropdown on the Embed Script tab and pick a preview mode. A new browser window opens with the widget rendered against your live bot.
2

Ask a question the bot should answer

Test the happy path. The bot should respond from your knowledge base with the styling you configured (color, logo).
3

(If escalation is configured) Test the escalation path

Ask a question the agent can’t help with. Then check the destination helpdesk, Zendesk, Front, Salesforce, or HubSpot, to confirm a ticket or case was created with the conversation history attached. If you’re testing Zendesk Chat and the widget conversation included a file, confirm the file appears in the Zendesk conversation too.

Common questions

Sign a JWT on your server with the widget’s Signing Key (HS256) and pass it as customerToken in finiWidgetOptions. The payload carries email, name, collectEmail, platform, and any user_attributes you want the agent to use. Never sign tokens in browser code. See Identify logged-in users.
To a ticket in Zendesk, Front, Gorgias, Salesforce, or HubSpot, through a Business Rule with source Widget and trigger On Escalation. Intercom, LiveChat, and Slack work as direct integrations only and aren’t widget escalation destinations. In workspaces with native ticketing, escalated widget conversations can also be handled as native tickets in Inbox, routed by Agent groups. See Fini on top of your helpdesk for how each destination behaves.
Yes. On the Settings tab you set the Widget title, Description, brand color (Select a color), Brand Logo (PNG recommended, max 2 MB), welcome text, three rotating Thinking messages, and Predefined questions. Use the Preview dropdown to check the result before going live.
Yes. The same widget and configuration run in iOS and Android apps; talk to your Fini contact for the current mobile embedding setup. Pass platform: "mobile" in the JWT so analytics and agent behavior can distinguish mobile conversations from web.
Yes. Enter one domain per line in the Domain field; wildcards such as *.yourcompany.com cover every subdomain. localhost works for testing without a domain match.
Fini supports 130+ languages. The language the agent answers in is part of the agent’s configuration, set in Prompts (language rules live in Role & Context), so the widget follows the same language behavior as the agent’s other surfaces.
To confirm (internal, remove before publish): Are the widget’s own interface strings (input placeholder, Start Again, Learn more via…, email prompt) localized to the customer’s language, or fixed in one language?
Yes. Use the SDK through window.fini: fini("open"), fini("close"), and fini("sendMessage", message), and subscribe to open, close, and sendMessage events for analytics. See SDK controls.
Only if reference links are enabled. Public source links appear under Learn more via…; private Google Cloud Storage references are counted but never exposed as clickable links. Turn reference links off to hide sources entirely.

Troubleshooting

Check in order:
  1. The script snippet is pasted inside or after the closing </body> tag.
  2. Your live domain matches one of the entries in the widget’s Domain field (including any wildcards). Trailing slashes and protocols matter, https://app.acme.com and app.acme.com are different entries.
  3. There’s no Content Security Policy on your site blocking external scripts. The widget script is served from storage.googleapis.com/fini-widget/assets/fini-widget-prod.min.js.
  4. Open the browser console: any errors mentioning “widget”, “fini”, or CORS usually point at the cause.
The customerToken is either missing, malformed, or signed with the wrong key. Confirm:
  1. The token is being signed server-side with the correct Signing Key from this widget’s settings.
  2. The algorithm is HS256.
  3. The token is being passed to finiWidgetOptions.customerToken as a string (not the parsed JSON payload).
  4. Inspect the token at jwt.io (against the signing key) to confirm the payload shape matches what’s documented above.
The domain saved on the widget doesn’t match where the script is loaded from. Update the Domain field under General setup on the Settings tab. Wildcards like *.acme.com cover all subdomains; bare domains don’t.
Confirm:
  1. The destination integration is connected and authorized on its own deploy page.
  2. A published Business Rule is assigned to this bot with source Widget and trigger On Escalation.
  3. The Business Rule template fields or Tool inputs are mapped to the right destination fields.
  4. The rule passes Test Rule with representative widget escalation context.
  5. For Salesforce: widget escalation does not require the manual Salesforce setup. If escalation is failing, verify the Salesforce credentials on the Salesforce deploy page are still valid (tokens expire and may need to be refreshed).