Inline Trigger: open the agent from your own button
The Inline Trigger turns your own button into a chat opener. Visitors click a button, link or menu item that is already on your page, and a chat window opens. The button stays exactly where you put it.
A button on your page opens the chat in a window that slides in from the side.
What your visitors will see
- Your own button, link or menu item, looking the way you styled it.
- When they click it, a chat window opens. By default it slides in from the right.
- They type a question and get an answer inside that window.
- They close it with the X, or make it bigger.
Add it to your site
There are four steps. Step 2 is a small tool: you click what you want, and it writes your snippet for you.
Step 1: Get your key
Open the dashboard, go to Hubs » Configure » Security, and create a publishable key. Then add your website address to that key, so it only works on your site. A publishable key is safe to put on your page. More in API keys.
Step 2: Choose your look and copy your snippet
Click the look you want. Switch the options you like. Your snippet below changes as you choose. Every picture shows what your visitors will see.
The snippet adds this button. Already have a button? See the class name below.
The snippet adds a button marked with this. The chat opens when anything that matches is clicked. Use .name for a class (the default), #name for an id, or [data-name] for a data attribute. To use a button you already have, mark it the same way. Change it here and both lines in the snippet change together.
Optional. Leave it empty to use the name set up in the dashboard.
Optional. The full URL of an image, starting with https://. It appears in the picture.
Light unless you choose Dark. The chat window does not follow your visitor's device setting by itself. If your site switches between light and dark, ask your developer to pass the matching one.
Side window on the right. This picture changes as you choose.
This is starter code. It already works. Add anything else we support whenever you need it. Every option we support is in the full table below, and each one can go inside the snippet next to the others.
- YOUR_PUBLISHABLE_API_KEYYour publishable key from step 1.
Lines that start with // are switched off and change nothing. To use one, delete the // and put in your own value, or fill the matching box above.
- YOUR_TITLEThe title at the top of the chat. Without it, the name set up in the dashboard is used.
- YOUR_LOGO_URLThe full URL of your logo image, starting with https://.
<head>
<script src="https://cloud.rapidflare.ai/storage/v1/object/public/rapidflare-embed-sdk/latest/agent.js"></script>
</head>
<body>
<button class="rapidflare-ask">Ask us anything</button>
<script>
window.rapidflare.init({
apiKey: 'YOUR_PUBLISHABLE_API_KEY',
display: {
type: 'inline-trigger',
trigger: '.rapidflare-ask',
variant: 'right-modal',
// titleText: 'YOUR_TITLE', (optional: delete the // at the start and put in your own value)
// titleLogo: 'YOUR_LOGO_URL', (optional: delete the // at the start and put in your own value)
}
});
</script>
</body>Step 3: Paste it into your page
Paste the whole snippet into your page's HTML where you want the button to appear. Then replace YOUR_PUBLISHABLE_API_KEY with your key from step 1. Already have a button? Mark it the same way as the button line in the snippet, then delete that button line.
Add the script line only once
The first line of the snippet, the one that starts with <script src=, loads Rapidflare. Put it on the page one time, even if you add more than one widget later. It has to come before the code under it.
Built your site with a website builder such as WordPress or Wix? Our website builder guides show where the snippet goes. Not sure? Send the snippet to the person who edits your site.
Step 4: Check that it works
Open the page in a private window and click your button. The chat should open in the look you picked.
Every option
Everything this widget accepts. A ✓ means the builder above already sets it. Anything else you can add by hand. Options in the first table go inside display: { ... }. Settings in the second table go next to apiKey.
Inside display: the look and behavior
| Option | In the builder | Type | What it does |
|---|---|---|---|
trigger | ✓ | string | HTMLElement | Required. CSS selector or DOM element the SDK binds click events to. |
variant | ✓ | 'right-modal' | 'left-modal' | 'center-modal' | 'right-drawer' | 'left-drawer' | Visual variant of the conversation panel. Default: 'right-modal'. See Pick a look. |
overlay | Add by hand | boolean | Backdrop behind the panel. Default: true for modals, false for drawers. Drawer variants ignore overlay. |
responsive | Add by hand | boolean | When true, windows narrower than 600 px show side modals and drawers as a bottom sheet. Default: true. center-modal is never a bottom sheet. |
fullWidthOnMaximize | Add by hand | boolean | If true, maximizing the panel goes fully edge-to-edge (100% width, no margin) instead of the default capped width. Unlike floating-launcher's equivalent option, this goes flush with the viewport since a drawer/modal surface is expected to cover it when widened. Default: false. |
titleText | ✓ | string | Panel header title. Defaults to the hub configuration name when omitted. |
titleLogo | ✓ | string | Panel header logo URL. Defaults to the hub UI-config logo when omitted. |
Next to apiKey: settings for the whole widget
| Setting | Required? | Type | Default | In the builder | What it does |
|---|---|---|---|---|---|
apiKey | Yes | string | - | Add by hand | |
id | Optional | string | - | Add by hand | |
userId | Optional | string | - | Add by hand | |
theme | Optional | 'light' | 'dark' | 'light' | ✓ | |
prompt | Optional | string | - | Add by hand | |
autoSubmit | Optional | boolean | false | Add by hand | |
context | Optional | object | - | Add by hand | |
conversationId | Optional | string | - | Add by hand | |
onLoad | Optional | function | - | Add by hand | |
onOpen | Optional | function | - | Add by hand | |
onClose | Optional | function | - | Add by hand | |
onError | Optional | function | - | Add by hand | |
doNotTrack | Optional | boolean | false | Add by hand |
If something does not work
| What you see | Why it usually happens | What to do |
|---|---|---|
| Clicking your button does nothing | The button is not marked the way the snippet expects | Use the button line from the snippet as it is. If you use your own button, give it the same class, rapidflare-ask unless you changed it in step 2. |
| Nothing changed after you pasted the snippet | The page was not published or saved, or your site is still showing an older copy of it | Publish or save the page. If your site has a caching or speed plugin, clear its cache. Then open the page in a private window. |
| The chat opens but shows an error instead of answers | The snippet still says YOUR_PUBLISHABLE_API_KEY, or your website address is not on the key's allowed list | Put your own key in the snippet. Then check your website address is on that key in the dashboard. See API keys. |
| The chat opens twice | The snippet was pasted twice | Keep one copy. |
| A change you made in the dashboard does not show yet | Browsers keep the agent's settings for up to an hour | Wait an hour, or open your site in a private window to see it now. |
For developers: open the chat from your own input box
For developers.
You can keep your own input box, ask bar, or suggested-question chips and hand each question to the panel, instead of letting the panel own the input. This is how the Rapidflare marketing site, docs, and blog headers work: a branded pill opens the inline-trigger panel and submits the typed question into it.
Three pieces make it work:
- Give the instance a stable
idatinit(), so you can address the panel. - Open the panel with
ref.open()(or let the user click the trigger). - Hand over the question with two same-window
postMessagecalls addressed to thatid:EMBED_WIDGET_OPENED, thenEMBED_WIDGET_QUESTION_ASKEDwith the question text andautoSubmit: true.
<head>
<script src="https://cloud.rapidflare.ai/storage/v1/object/public/rapidflare-embed-sdk/latest/agent.js"></script>
</head>
<body>
<form id="ask-form">
<button id="ask-open" type="button" aria-label="Open the agent">✦</button>
<input id="ask-input" type="text" placeholder="Ask about our products" />
<button type="submit">Ask</button>
</form>
<script>
const INSTANCE_ID = 'my-ask-bar';
let ready = false;
let pending = null;
const widget = window.rapidflare.init({
id: INSTANCE_ID,
apiKey: 'YOUR_PUBLISHABLE_API_KEY',
display: { type: 'inline-trigger', trigger: '#ask-open', variant: 'right-modal', overlay: true },
onLoad: () => { ready = true; flush(); }
});
// Messages are addressed to the instance, so several widgets on one page never cross-deliver.
function post(message) {
window.postMessage({ ...message, rapidflareEmbedWidgetRootInstanceId: INSTANCE_ID }, '*');
}
// The panel ignores questions while it is still loading; queue until onLoad has fired.
function flush() {
if (!ready || !pending) return;
const question = pending;
pending = null;
setTimeout(() => post({ type: 'EMBED_WIDGET_QUESTION_ASKED', question, autoSubmit: true }), 250);
}
function ask(question) {
widget.open();
post({ type: 'EMBED_WIDGET_OPENED' });
if (question) { pending = question; flush(); }
}
document.getElementById('ask-form').addEventListener('submit', (event) => {
event.preventDefault();
const input = document.getElementById('ask-input');
ask(input.value.trim());
input.value = '';
});
</script>
</body>
An empty ask('') opens the panel without submitting anything, which is the right behavior for a "talk to the agent" button next to the input. Pass autoSubmit: false to place the text in the panel's composer without sending it, so the user can edit first. onOpen / onClose are useful for hiding your own bar while the panel is up.
Why postMessage and not a method?
ref.update({ prompt, autoSubmit: true }) seeds a first question at mount time only; it does not submit a new question into an already-open panel. The message channel above does, any number of times. Both message types are part of the SDK's own search-to-panel contract and are stable.
For developers: every setting
Everything below is for the person who edits your site's code. If you only want the widget on your page, the steps above are all you need. The full list of options is in the table above.
Identifying your users
Rapidflare cannot see your site's login state. The widget runs in its own frame and never reads your session, cookies, or SSO. Whether a conversation is attributed to a person is decided entirely by the userId you pass to init().
| On your page | What to pass | How the conversation is recorded |
|---|---|---|
| Visitor is signed in | userId: their id, email recommended | Stored under that id, so you can filter and sort by user in Home » History |
| Visitor is signed out | Leave userId out | Stored under an anonymous id we generate for that browser |
<script>
window.rapidflare.init({
apiKey: 'YOUR_API_KEY',
// Signed in: their id. Signed out: leave the key out entirely.
...(currentUser ? { userId: currentUser.email } : {}),
display: { type: 'inline-trigger', trigger: '.rapidflare-ask' }
});
</script>
- Set it per visitor, never once for your whole site. A single hardcoded value (a support engineer's address,
admin@yourcompany.com) files every visitor's conversation under that one person, and your history and analytics become unreadable. This is the most common mistake we see. - Omitting beats inventing. The anonymous id we generate is derived from the visitor's browser and your site's host, so the same browser usually comes back as the same id and keeps its earlier conversations. A placeholder such as
guest@example.comoranonymousinstead merges every signed-out visitor into one user. userIdis a claim, not authentication. We record it as declared by your page and never verify it. Don't treat it as an access-control boundary, and don't put anything secret in it.- Changing it starts a clean slate. The chat-history list lives in the visitor's browser, scoped per agent and
userId, and a conversation can only be resumed by the id that started it. So after a sign-in or sign-out, the panel no longer lists or resumes what the previous identity said. Reloading the page with the newuserIdis the simplest approach; if you callupdate({ userId })in place, do it when a new conversation starts.
Giving the agent page context
The agent answers better when it knows what page the visitor is asking from. Pass that in context:
<script>
window.rapidflare.init({
apiKey: 'YOUR_API_KEY',
context: {
referrerPage: { url: window.location.href, title: document.title },
ambient: 'Viewing the Enterprise plan. Trial ends in 3 days.'
},
display: { type: 'inline-trigger', trigger: '.rapidflare-ask' }
});
</script>
Your page tells us that page—we don't guess it. The browser's own document.referrer is empty whenever the link carried rel="noopener noreferrer", the page was opened through window.open, or a referrer policy stripped it on a cross-origin hop, and it never carries a title at all. Reading it would give you a value that quietly disappears on the journeys you most want to see, so you pass what your page already knows.
referrerPageis the page the question is coming from. With the widget embedded in your site that is the page it sits on, as above.- Both fields are optional. Send
referrerPage,ambient, or both. - It travels with every message in that conversation, not just the first.
- It is stored with the conversation and shown in Home » History, so treat it as visible: put in what helps the agent answer, not credentials, payment details, or anything you would not show the visitor.
- Keep it short. A few lines is the intent. Everything you send is combined into a single block capped at 10,000 characters; past that the tail is cut and marked as truncated.
Changing it later. update({ context }) replaces the whole object rather than merging, so send every field you still want:
<script>
const ref = window.rapidflare.init({ /* … */ });
// Single-page app: the visitor navigated, so tell the agent where they are now.
ref.update({ context: { referrerPage: { url: window.location.href, title: document.title } } });
// Signed out, or moved somewhere the context no longer applies.
ref.update({ context: undefined });
</script>
Replacing ambientContext
ambientContext, the earlier flat string, still works: pass it to init() and it becomes context.ambient, with a one-time console warning. Prefer context for new integrations, and if you send both, context.ambient wins. It is honored at init() only—update({ ambientContext }) is ignored, so use update({ context }).
API
| Method | Description |
|---|---|
rapidflare.init(config) | Mounts the widget with the given config. Required: apiKey and display. Returns a ref object with id (string), open(), close(), maximize(), minimize(), update(partial), resetConversation(), destroy(), and config(). |
rapidflare.getInstances() | Returns an array of refs for all currently mounted widget instances. Each ref has id, open, close, maximize, minimize, update, resetConversation, destroy, and config. |
ref.open() | Programmatically open the panel. Most useful for imperative control flows or custom wrappers around an existing trigger element. |
ref.close() | Programmatically close the panel. |
ref.maximize() | Expand the panel to its wider view. |
ref.minimize() | Collapse the panel to its default size. |
ref.update(partial) | Updates a subset of options (e.g. theme, prompt, userId, display properties other than type) without remounting. display.type cannot be changed via update()—call destroy() then init() with a new type instead. |
ref.resetConversation() | Clears the current conversation and starts a fresh one in place, without remounting the widget. Use it to back a "New chat" control in your own UI. The panel stays open and keeps its config; only the conversation is discarded. |
ref.destroy() | Unmounts that widget instance and cleans up. Other instances are unaffected. Call before calling init() again for the same container if you need a different config. |
ref.config() | Returns the current config. |
Script loading and behavior
- Load the script only once per page. Put the
<script src=...>line in once, before anyinit()call. If you add more than one widget, keep that single line and add oneinit()call for each widget. - The loader script does not overwrite
window.rapidflareif it is already set. - There is no global
window.rapidflare.update()orwindow.rapidflare.destroy(); all per-instance control is through the ref returned byinit()(or refs fromgetInstances()). - Multiple instances: Each
init()call returns a separate instance. Callupdate(),destroy(), andconfig()on the returned reference, not onwindow.rapidflare. Usewindow.rapidflare.getInstances()to get all currently mounted instances. - If you call
init()before the runtime script has finished loading, your config is queued and applied automatically when the runtime is ready. - The widget keeps your agent's settings (colors, logo, header text) in the visitor's browser for up to one hour after the visitor first opens the page. A change you make in the dashboard can take up to an hour to appear for returning visitors. Open the page in a private window to see the latest version.
resetConversation()is a no-op if called before the runtime has loaded: the widget has not rendered yet, so there is no conversation to clear.
Script URL
This is the one script every widget needs. Add it once, before your init() call:
<head>
<script src="https://cloud.rapidflare.ai/storage/v1/object/public/rapidflare-embed-sdk/latest/agent.js"></script>
</head>
Load the Embed SDK from this URL (always the latest build—we do not publish or support pinning to older semver paths for this script):
https://cloud.rapidflare.ai/storage/v1/object/public/rapidflare-embed-sdk/latest/agent.js
If you need a fixed artifact for compliance, host a copy you control or coordinate with Rapidflare—do not rely on undocumented URL patterns.
Other ways to show the agent
Every widget type has its own page. Compare all of them or jump to another one.
A chat bubble in a corner of every page. Visitors click it and the agent opens as a popup.
The agent inside a page you own. Also where a search or an AI overview continues.
Your agent on your own domain, like ask.yourcompany.com. Nothing to install.