BetCart Widget
BetCart Chat Widget Integration Guide
How to embed the BetCart AI chat assistant on any third-party website. This guide documents the production loader script hosted at https://help.betcart.com/betcart-widget.js.
1. Overview
The BetCart Chat Widget is an iframe-based AI assistant that answers questions from the BetCart help center. You add it to your page with one <script> tag. The loader renders a floating launcher button in the bottom-right corner; clicking it opens a 504 × 720 chat panel (full-screen on mobile). Because the widget runs inside an iframe served from help.betcart.com, your page's JavaScript, CSS, and cookies stay isolated from it.
Use it on any site where visitors ask questions the help center already covers. There is no per-site key, no account creation, and no server-side integration.
2. Quick Start
Add this snippet immediately before the closing </body> tag:
<script
id="betcart-widget-js"
src="https://help.betcart.com/betcart-widget.js"
async
></script>With this snippet alone, the widget loads silently. No launcher button is rendered and the chat panel does not open on its own. To open it, call window.BetCartWidget.open() from your site (for example, from a “Need help?” button of your own). To show a floating launcher button or to open the chat on page load instead, opt in via the data attributes listed below.
The loader is idempotent: if the script is loaded more than once on the same page (for example through a SPA route change), the second load is a no-op and logs a warning to the console.
3. Installation Modes
Default (hidden)
The widget is loaded but neither the launcher button nor the chat panel is rendered until your site calls the JavaScript API. Best for hosts that want full control over when the widget appears.
<script
id="betcart-widget-js"
src="https://help.betcart.com/betcart-widget.js"
async
></script>With launcher button
A floating green button is rendered in the bottom-right corner; the chat panel is created lazily the first time the user clicks it. There are two equivalent ways to enable it.
Option A. Data attribute on the script tag:
<script
id="betcart-widget-js"
src="https://help.betcart.com/betcart-widget.js"
data-show-button="true"
async
></script>Option B. Global config object set before the script loads:
<script>
window.BetCartWidgetConfig = { showButton: true };
</script>
<script
id="betcart-widget-js"
src="https://help.betcart.com/betcart-widget.js"
async
></script>Auto-open on page load
The chat panel opens automatically as soon as the script loads. No launcher button is rendered.
<script
id="betcart-widget-js"
src="https://help.betcart.com/betcart-widget.js"
data-auto-open="true"
async
></script>Both: launcher button and auto-open
The chat opens immediately and the launcher button is also present, so the user can re-toggle the panel after closing it.
<script
id="betcart-widget-js"
src="https://help.betcart.com/betcart-widget.js"
data-show-button="true"
data-auto-open="true"
async
></script>Precedence: if both the data attribute and the config object set the same option, window.BetCartWidgetConfig wins when its value is a real boolean (true or false). Anything else falls back to the data attribute. The two options (showButton and autoOpen) are independent. You can set either, both, or neither.
4. Configuration Reference
Data attributes (on the script tag)
| Attribute | Type | Default | Description |
|---|---|---|---|
| data-show-button | string | (omitted) | When set to "true", "1" or "yes" (case-insensitive), a floating launcher button is rendered and the chat opens on click. |
| data-auto-open | string | (omitted) | When set to "true", "1" or "yes" (case-insensitive), the chat panel opens automatically as soon as the script loads. Independent of data-show-button. |
Global config object (window.BetCartWidgetConfig)
| Property | Type | Default | Description |
|---|---|---|---|
| showButton | boolean | false | Same behavior as data-show-button="true". Must be set before the loader script runs. |
| autoOpen | boolean | false | Same behavior as data-auto-open="true". Must be set before the loader script runs. |
No other configuration is currently read by the loader. Color, position, dimensions and button label are fixed in the script and not customizable per-site at this time. See Section 13.
5. JavaScript API
After the loader has finished initializing, a frozen global object is available at window.BetCartWidget with three methods. In the default (hidden) install, open() is the only way to surface the chat. Wire it to your own button, link, or page event.
// Open the widget programmatically
window.BetCartWidget.open();
// Close it
window.BetCartWidget.close();
// Toggle open/closed
window.BetCartWidget.toggle();| Method | Behavior | No-op when | Returns |
|---|---|---|---|
| open() | Creates the iframe on first call (lazy), shows the chat panel, and hides the floating launcher button. | Already open. The call is safe; the panel stays open and the launcher stays hidden. | undefined |
| close() | Animates the panel out and shows the launcher button (in default mode). | Already closed. Safe to call repeatedly. | undefined |
| toggle() | Calls open() if closed, otherwise close(). | Never (always transitions state). | undefined |
The window.BetCartWidget object is created with Object.freeze, so you cannot monkey-patch its methods. Adding your own properties to the global is a no-op.
Example: open the widget from a custom button
<button id="open-help">Need help?</button>
<script>
document.getElementById('open-help').addEventListener('click', function () {
if (window.BetCartWidget) window.BetCartWidget.open();
});
</script>window.BetCartWidget is defined synchronously inside the loader's init() step, which runs on DOMContentLoaded. If you call the API before then, guard with if (window.BetCartWidget) or defer the call to a later tick.
6. Dimensions & Positioning
The widget's visual envelope is fixed in the loader script:
| Property | Value |
|---|---|
| Desktop panel width | min(504px, calc(100vw − 24px)) |
| Desktop panel height | min(720px, calc(100vh − 24px)) |
| Position | Bottom-right, 24px from each edge (panel and launcher button) |
| Border radius (desktop) | 16px |
| Launcher button | 56 × 56 circle, accent color #10b981 |
| z-index (panel) | 2147483647 |
| z-index (launcher) | 2147483646 |
| Mobile breakpoint | max-width: 480px |
| Mobile layout | Full-screen overlay (100vw × 100dvh), no border radius, dedicated close button in the top-left |
These values are not configurable from the host page. If you need different dimensions, position or a different launcher color for your integration, please contact the BetCart team.
7. PostMessage Protocol
The widget iframe communicates with the host page via window.postMessage. Currently a single one-way event is emitted:
| Direction | Payload | Trigger |
|---|---|---|
| widget → host | { type: 'bc_widget_close' } | User closes the widget from inside the iframe (mobile close button or similar) |
The loader handles bc_widget_close on its own: it closes the panel and restores the launcher button. Host pages can also listen for the event if they want to react when the user dismisses the chat.
Always verify event.origin against https://help.betcart.com before trusting any message:
window.addEventListener('message', function (event) {
if (event.origin !== 'https://help.betcart.com') return;
if (event.data && event.data.type === 'bc_widget_close') {
console.log('BetCart widget was closed by the user');
}
});There is no public host-to-widget protocol today. If your integration needs to send data into the widget (for example, to pre-fill a user context), contact the BetCart team. Host-to-widget messages require changes on both sides.
8. Browser Support
The loader targets modern evergreen browsers. We test against:
- Chrome and Edge: latest two stable versions
- Firefox: latest two stable versions
- Safari (macOS and iOS): latest two stable versions
The script uses document.currentScript, the URL constructor, arrow functions, const/let, requestAnimationFrame, and the 100dvh CSS unit, so any browser that supports all of those will run the widget. Internet Explorer (any version) is not supported.
9. Security & Privacy
- Iframe isolation. The widget runs in an iframe served from
https://help.betcart.com. The browser's same-origin policy prevents the widget from reading your page's DOM, cookies or local storage, and prevents your page from reading the widget's internal state. - Origin checks. The loader only acts on
postMessageevents whoseevent.originmatches the widget origin. Forged messages from other frames are ignored. - No analytics or tracking pixels. The loader script itself injects no trackers, beacons or analytics. Telemetry about the chat conversation lives inside the widget and is governed by the help center's privacy policy.
- No special permissions. The script does not request notification, geolocation, microphone or camera access. The iframe declares only
allow="clipboard-write", so users can copy bot answers to their clipboard. - No user data sent by the loader. The loader does not read or forward any cookies, form values or identifiers from your page. Anything the user types is handled inside the iframe and goes directly to
help.betcart.com.
10. Content Security Policy
If your site sends a strict Content-Security-Policy header, add the following sources so the loader can run and the iframe can load:
script-src 'self' https://help.betcart.com
frame-src https://help.betcart.comscript-srcallows the loader filehttps://help.betcart.com/betcart-widget.js.frame-srcallows the iframe that loadshttps://help.betcart.com/widget.
You do not need to add connect-src for help.betcart.com: chat traffic happens inside the iframe, which has its own origin and CSP context. Your host page's CSP does not govern requests made from inside the widget.
The loader injects a small inline <style> tag at boot to position the launcher and panel. If your CSP forbids inline styles, you will need to permit them with style-src 'unsafe-inline', a nonce, or a hash.
11. Sample Implementations
Plain HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>My Site</title>
</head>
<body>
<h1>Welcome</h1>
<!-- Place once, just before </body> -->
<script
id="betcart-widget-js"
src="https://help.betcart.com/betcart-widget.js"
async
></script>
</body>
</html>Next.js (App Router)
// app/layout.tsx
import Script from 'next/script';
import type { ReactNode } from 'react';
export default function RootLayout({ children }: { children: ReactNode }) {
return (
<html>
<body>
{children}
<Script
id="betcart-widget-js"
src="https://help.betcart.com/betcart-widget.js"
strategy="afterInteractive"
/>
</body>
</html>
);
}React (Vite, CRA)
Mount this component once near the root. The hook injects the loader on first mount and is idempotent across re-renders and HMR.
import { useEffect } from 'react';
export function BetCartWidget() {
useEffect(() => {
if (document.getElementById('betcart-widget-js')) return;
const s = document.createElement('script');
s.id = 'betcart-widget-js';
s.src = 'https://help.betcart.com/betcart-widget.js';
s.async = true;
document.body.appendChild(s);
}, []);
return null;
}Vue 3
<script setup>
import { onMounted } from 'vue';
onMounted(() => {
if (document.getElementById('betcart-widget-js')) return;
const s = document.createElement('script');
s.id = 'betcart-widget-js';
s.src = 'https://help.betcart.com/betcart-widget.js';
s.async = true;
document.body.appendChild(s);
});
</script>WordPress
Add to your active theme's functions.php:
// functions.php
function betcart_widget_footer() {
echo '<script id="betcart-widget-js" '
. 'src="https://help.betcart.com/betcart-widget.js" async></script>';
}
add_action('wp_footer', 'betcart_widget_footer');12. Troubleshooting
The launcher button never appears
- Confirm the loader is reachable:
curl -I https://help.betcart.com/betcart-widget.jsshould return200. - Open the browser devtools console and look for
[BetCartWidget]warnings, CSP errors, or ad-blocker interference. - Make sure the snippet is in the page's HTML (not inside a hidden tab or a branch that never renders).
- If you have NOT set
data-show-button="true", the button is hidden by design. That is the default behavior. Open the widget by callingwindow.BetCartWidget.open()from your own button or page event.
The launcher appears but the iframe is blank
- Almost always a CSP
frame-srcrestriction. Addframe-src https://help.betcart.comto your policy. - Check the iframe's URL in devtools; it should be
https://help.betcart.com/widget. If it isn't, the loader did not derive the origin correctly. Please report it.
The widget covers important page elements
The widget uses the maximum signed 32-bit z-index (2147483647) to stay above your page. If you have a critical UI element that must sit on top (cookie banner, payment overlay), render it before the user opens the widget, or contact us about lowering the widget's z-index for your integration.
Two launcher buttons appear
The script is being loaded more than once. The loader has an idempotency guard (window.__betcartWidgetLoaded), but it only catches loads that share the same JavaScript execution context. Duplicate <script> tags with different src URLs or query strings can still trigger separate inits. Audit your page for duplicate snippets.
window.BetCartWidget is undefined when I call it
The API is published from init(), which fires on DOMContentLoaded. If you call it from a synchronous <script> tag above the loader, the API isn't set yet. Defer your call:
document.addEventListener('DOMContentLoaded', function () {
window.BetCartWidget && window.BetCartWidget.open();
});Mobile keyboard hides the input
The panel uses 100dvh for its height so the layout follows the dynamic viewport when the keyboard opens. If you can still reproduce this on a specific device, please report the browser and device model.
13. Limitations
These features are intentionally not supported by the loader today. They're listed here so integrators know not to build around them:
- Multiple widget instances on the same page
- Custom panel dimensions or position per-site
- Custom launcher button color, label or icon per-site
- Programmatic message-sending into the widget (no host → widget API)
- Passing host-site user identity into the widget (chat is anonymous)
- Custom event hooks beyond
bc_widget_close
If any of these would block your integration, reach out to the Help.BetCart team.
Questions or issues with this guide? Email the BetCart help center team at DevT34m@proton.me and reference the URL /widget/doc/script.
