Skip to main content
Add your agent to any website with one script tag. Visitors can type to it, call it, or do both in the same conversation.

How the widget works

  • One widget does chat and calls. Visitors type in the text box or tap the call button.
  • The agent remembers the conversation. A call picks up where the chat left off. A chat after a call knows what was said on the call.
  • Visitors can type during a call. If the agent is talking, it stops. Then it answers out loud, and the answer also shows as a text message.
  • Nothing starts until the visitor acts. Opening the widget starts nothing, and it never asks for the microphone. The chat starts when the visitor clicks into the text box, and the agent’s greeting shows as the first message.
What a visitor sees:
  1. The launcher. A pill-shaped button with the Bananaflow logo and your label.
  2. The open view. A large logo with a glowing ring and a call button in its center. Below it are your welcome line and a text box.
  3. The chat. Messages appear under a small logo at the top left. A round call button sits next to the text box.
  4. A call. The ring glows while the agent speaks, and a status shows “Listening…” or “Speaking…”. An end call button takes the place of the call button.
  5. After a call. A line such as “Call ended · 1:23” marks the call in the chat. The visitor can keep typing.
How greetings work:
  • A call from the open view: the agent greets the visitor out loud, like any call.
  • A call after chatting: the agent says one short line to pick up the conversation. It does not greet again.
  • A chat after a call: the chat picks up from the call, with no greeting.
  • After the agent ends the conversation at an End Call node, the next chat or call starts fresh, with the normal greeting.
A few more details:
  • On phones (screens under 480 pixels wide), the widget fills the screen.
  • There is no End chat button. A chat ends after a stretch with no activity, or when your page calls end(). The stretch is 30 minutes unless you change Chat Inactivity Timeout. The widget then shows “Conversation ended.” and a Start new chat button.
  • A chat lasts up to 1 hour. After that, the visitor can start a new one.
  • Reloading the page starts a new conversation. History is not kept across page loads.
Already using the widget? You don’t need to change your page. The same embed snippet now loads this widget. Voice widgets and chat widgets are now one widget, so the old Widget Type setting no longer applies.

Before you start

  • Use HTTPS. Serve your page over HTTPS, or from http://localhost while you test. Browsers block the microphone on plain http:// pages, so calls fail there.
  • Allow your domains. If you fill in Allowed Domains, add every domain the widget runs on, including localhost for tests. Requests from other domains are refused. Leave the list empty to allow all domains.
  • The script loads in the background. The snippet loads bananaflow-widget.js asynchronously. Code that uses window.BananaflowWidget must wait for it. See Wait for the widget to load.
  • Strict security headers? If your site sends a Content Security Policy, see Content Security Policy.

How to add it

Step 1: In the agent editor, click the gear icon at the top right to open the agent settings. Step 2: Scroll to Add to Website and click Configure Widget. Step 3: Turn on Enable Embedding and add your domain under Allowed Domains. Pick an Embed Mode, set the look and the text, and click Save Configurations. Each setting is described in the table below. Step 4: Click Copy Code and paste the embed code into your page’s HTML. Changes you save later show up on your site the next time a page loads. You don’t need to paste the code again.

Settings

Embed modes

In every mode, the widget handles the microphone, the call audio, and the chat. The JavaScript API and its callbacks work in all three modes.

Floating widget

A pill button sits in a corner of the page. It shows the Bananaflow logo and your Launcher Label.
  • Clicking the pill opens the widget card.
  • The minimize button at the top right closes the card, and so does the Escape key. The conversation keeps going, and reopening the card shows it again.
  • Minimizing during a call keeps the call going. The pill glows and shows how long the call has run.
Set Launcher Label, Welcome Line, Accent Color, and Position in the dialog. Pasting the snippet is the whole job. You don’t need any other code.

Inline component

The widget card fills a container you place on your page. There is no pill and no minimize button. Give the container a height, since the card fills it. Set Welcome Line and Accent Color in the dialog, plus any widget text you want to change.

Plain HTML

Place the container where you want the widget. The widget finds it on its own.

React

In React, the container can mount after the widget script has loaded. If the widget hasn’t started yet, call initInline with the container. If it has, call refresh to draw the card in the new container. This example uses the declaration from TypeScript.

Headless mode

In headless mode the widget shows nothing. Your page draws its own buttons and messages, and drives the conversation with the JavaScript API. The widget still handles the microphone, the call audio, and the chat. This example has a transcript, a text box, a send button, and a call button:

React call button

A call button that follows the call status. It uses the declaration from TypeScript.
Call startCall() from a click or tap handler. Browsers only let a page play sound after the visitor interacts with it. A call started from a timer or on page load can fail.

JavaScript API

The widget adds window.BananaflowWidget to your page. It works in every embed mode, not just headless. Use it to drive your own interface, or to send widget events to your analytics.

Wait for the widget to load

The snippet loads the widget script asynchronously, so window.BananaflowWidget may not exist yet when your code runs. This helper runs your code once the widget is ready:

Methods

Callbacks

All callbacks fire in every embed mode, including onStatusChange. Each callback function keeps one listener. Calling it again replaces the one before. For example, to send calls to your analytics:
onCallConnected and onCallDisconnected fire only for calls that connect. A blocked microphone or a network failure fires neither, so your numbers stay clean.

TypeScript

window.BananaflowWidget has no published types. Declare the parts you use. The React examples on this page use this declaration:

Pass context to the agent

Your page often knows something about the visitor: their name, their plan, the page they are on. Pass it to the agent, and the agent can use it from the first message. The snippet you copy has a data-bananaflow-context attribute. It holds a JSON object of visitor details. Here is the part of the snippet that sets it. Keep the js.src value from your own snippet, since it holds your embed token.
The snippet builds the object in JavaScript when the page loads, so it can hold anything your page knows. Replace the object inside JSON.stringify(...), for example:
Each key is then available in any node prompt as {{initial_context.<name>}}:
Values can be text, numbers, true or false, or nested objects. The agent gets them in chats and in calls. Each run records them, so you can see what the agent was given.
Key names can’t contain dots, spaces, pipes (|), or braces ({ and }). Those characters mean something in template expressions. A key with one of them is dropped, and the conversation still starts.

Update context after the page loads

The attribute is set once, when the page loads. That doesn’t fit a single-page app, where the visitor logs in or fills a cart later. For details you learn later, call setContext():
Each call merges into the details already collected, so you can add details as you learn them. Send a name again to correct it. getContext() returns the current set. The agent reads the context once, when the conversation starts. Chats and calls later in the same conversation keep that context. So call setContext() before the conversation starts. Details you set after that apply to the next new conversation, such as one started with Start new chat.
The widget script loads asynchronously. Call setContext() inside the helper from Wait for the widget to load, or from a later event such as a click.
Use data-bananaflow-context for what the page knows when it loads, and setContext() for what it learns later. The two merge. If both set the same name, setContext() wins.
Context comes from the page, so a visitor can read it and change it. Never pass secrets. Don’t let it decide what the agent will do or share. Treat plan: "pro" as a hint for wording, not as proof. For data the agent must trust, pass an ID such as customer_id. Then let Bananaflow fetch the real details from your API with Pre-Call Data Fetch.
Limits for each conversation: up to 50 details, names up to 64 characters, and 8 KB in total. Text longer than 2,000 characters is cut short. An object or list longer than 2,000 characters is dropped. Anything else past a limit is dropped, and the conversation still starts. A few names are reserved and ignored, such as provider, call_id, and workflow_run_id.

Change the widget text

You can change every word the widget shows, so it can speak your site’s language. Open Widget Text in the Configure Widget dialog and fill in the fields you want to change. Leave a field blank to keep the default. The Launcher Label and Welcome Line are set with the other settings. The key column shows each text’s name in the widget’s saved settings. Text box and buttons During a call Problems End of a conversation
Did you translate the older widget? Check this list again. New texts show in English until you fill them in. Texts for parts the widget no longer has, such as the End chat button, are no longer used.

See conversations in your dashboard

Each chat and each call is its own run. To see them, open your agent, click the three-dot menu at the top right, and choose View Runs. When one visitor chats and calls, their runs are linked:
  • The chat run holds the whole conversation. It shows the typed messages and the lines from each call. Each call shows as a line such as “Voice call · 2:31” that links to the call’s run.
  • Each call run has its own recording and transcript.
  • Links connect the runs. Runs show Continued from run # and Continued in run # links. Follow them to move through the conversation.
  • Messages typed during a call belong to that call’s run.
Each run does its own follow-up work:
  • Webhooks and QA run once per run. A call’s webhooks and QA run when the call ends. The chat’s webhooks and QA run when the chat ends.
  • A visitor who only chats, or only calls, makes one run, as before.
  • If your embed token has a usage limit, each chat and each call counts once. A visitor who chats and then calls counts twice. A call blocked by the microphone never starts, so it doesn’t count.

Content Security Policy

If your site sends a Content-Security-Policy header, add these sources to it: If your policy leaves out one of these directives, the browser uses default-src for it. Add the sources there instead. The embed snippet itself is an inline script. If your policy blocks inline scripts, add your nonce to the snippet’s <script> tag.