Documentation
Widget Reference
Embed your AI chatbot on any website with one script tag. Use the JavaScript API to control the widget programmatically.
Quick Start
Copy your chatbot ID from the dashboard, paste the snippet before the closing </body> tag, and you're live.
<script src="https://startsnow.site/widget.js"></script>
<script>
ChatBot.init({
chatbotId: "YOUR_CHATBOT_ID"
});
</script>How it works
- The script is tiny — it loads asynchronously and never blocks your page.
- The chat UI renders inside a Shadow DOM root, so your site's CSS and the widget's never affect each other — and no
frame-srcCSP rule is required. - No cookies are set by default.
Allowed Domains
This is the #1 reason a widget doesn't appear. Each chatbot has a server-side list of domains it's allowed to load on, managed in your dashboard under Settings → Allowed Domains.
- Empty list = loads everywhere. A brand-new chatbot has no domains configured, so the widget works on any site out of the box.
- Add one domain and only listed domains load it. As soon as you add a domain, every other domain is blocked — the widget simply doesn't render (it fails silently, leaving a hint in the console rather than showing a broken bubble).
- Use exact hostnames. The dashboard list matches the visitor's hostname exactly — wildcards are not expanded here. If you serve both
example.comandwww.example.com, add both. - localhost is always allowed.
localhost,127.0.0.1, andstartsnow.siteare permitted automatically for local development and dashboard previews — you never need to add them.
If your widget isn't showing, open the browser console. A message starting with [StartsNow Widget] This domain is not in the allowed list means you need to add the current domain in Settings → Allowed Domains.
Domain Lock
Optionally restrict the widget from a second angle — directly in the embed snippet — so it can't be initialised by anyone who copies your chatbot ID onto another site. This is a separate, client-side guard from the dashboard Allowed Domains list above. Pass an allowedDomains array to ChatBot.init — unlike the dashboard list, this one does support wildcard subdomains.
<script src="https://startsnow.site/widget.js"></script>
<script>
ChatBot.init({
chatbotId: "YOUR_CHATBOT_ID",
allowedDomains: [
"yourwebsite.com",
"*.yourwebsite.com"
]
});
</script>Domain patterns
// Exact match
allowedDomains: ["yourwebsite.com"]
// All subdomains of yourwebsite.com (e.g. app.yourwebsite.com)
allowedDomains: ["*.yourwebsite.com"]
// Multiple domains
allowedDomains: ["yourwebsite.com", "*.yourwebsite.com", "staging.example.com"]Leave allowedDomains empty (or omit it) to allow all domains.
Data Attribute
If you prefer no inline scripts, set data-chatbot-id directly on the script tag. The widget auto-initialises when the DOM is ready.
<script
src="https://startsnow.site/widget.js"
data-chatbot-id="YOUR_CHATBOT_ID"
></script>With domain lock
Pass a comma-separated list via data-allowed-domains.
<script
src="https://startsnow.site/widget.js"
data-chatbot-id="YOUR_CHATBOT_ID"
data-allowed-domains="yourwebsite.com,app.yourwebsite.com"
></script>Framework Guides
The widget is plain JavaScript and works on any stack. Below are drop-in recipes for the most common ones. Replace YOUR_CHATBOT_ID with the ID from your dashboard.
Next.js — App Router
Inline <script>tags don't run reliably in JSX, and a Server Component can't use onLoad. Create a small Client Component that loads the widget and calls init from onLoad — this guarantees ChatBot exists before you call it.
// components/ChatbotWidget.tsx
"use client";
import Script from "next/script";
declare global {
interface Window {
ChatBot?: {
init: (config: { chatbotId: string; allowedDomains?: string[] }) => void;
};
}
}
export default function ChatbotWidget() {
return (
<Script
src="https://startsnow.site/widget.js"
strategy="afterInteractive"
onLoad={() => {
window.ChatBot?.init({
chatbotId: "YOUR_CHATBOT_ID",
allowedDomains: ["yourwebsite.com", "*.yourwebsite.com"],
});
}}
/>
);
}Then render it once in your root layout:
// app/layout.tsx
import ChatbotWidget from "@/components/ChatbotWidget";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
{children}
<ChatbotWidget />
</body>
</html>
);
}No proxy route needed
Earlier versions of this guide suggested creating a proxy /api/widget route inside your Next.js app. That is no longer required — widget.js automatically calls its own origin (startsnow.site), so the standard two-tag snippet works as-is. If you added a proxy route for the widget, you can safely remove it.
Next.js — Pages Router
Add it once in _app.tsx using next/script.
// pages/_app.tsx (Pages Router)
import Script from "next/script";
import type { AppProps } from "next/app";
export default function App({ Component, pageProps }: AppProps) {
return (
<>
<Component {...pageProps} />
<Script
src="https://startsnow.site/widget.js"
strategy="afterInteractive"
onLoad={() => {
window.ChatBot?.init({ chatbotId: "YOUR_CHATBOT_ID" });
}}
/>
</>
);
}React / Vite / CRA
No next/script here — inject the script in a useEffect and init from its onload.
// src/ChatbotWidget.tsx (React / Vite / CRA)
import { useEffect } from "react";
export default function ChatbotWidget() {
useEffect(() => {
const script = document.createElement("script");
script.src = "https://startsnow.site/widget.js";
script.async = true;
script.onload = () => {
window.ChatBot?.init({ chatbotId: "YOUR_CHATBOT_ID" });
};
document.body.appendChild(script);
return () => script.remove();
}, []);
return null;
}
// Then render <ChatbotWidget /> once near your app root.Vue 3 / Nuxt 3
Inject the script from onMounted so it only runs in the browser, then init from its onload. Place it in App.vue (or a default layout) so it loads once.
<!-- Vue 3 / Nuxt 3 — App.vue or a layout component -->
<script setup>
import { onMounted } from "vue";
onMounted(() => {
const s = document.createElement("script");
s.src = "https://startsnow.site/widget.js";
s.async = true;
s.onload = () => window.ChatBot?.init({ chatbotId: "YOUR_CHATBOT_ID" });
document.body.appendChild(s);
});
</script>WordPress
Recommended: use a header/footer plugin so a theme update never wipes your snippet.
Easiest (no code editing) — use a header/footer plugin:
1. Install the "WPCode" or "Insert Headers and Footers" plugin.
2. Go to Settings → Insert Headers and Footers (or WPCode → Header & Footer).
3. Paste the snippet below into the "Footer" / "Body" box and Save.
<script src="https://startsnow.site/widget.js"></script>
<script>
ChatBot.init({ chatbotId: "YOUR_CHATBOT_ID" });
</script>Or edit the theme directly:
<!-- Appearance → Theme File Editor → footer.php
Paste just before the closing </body> tag -->
<script src="https://startsnow.site/widget.js"></script>
<script>
ChatBot.init({
chatbotId: "YOUR_CHATBOT_ID",
allowedDomains: ["yourwebsite.com", "*.yourwebsite.com"]
});
</script>Shopify
Add the snippet to your theme's theme.liquid layout file so it loads on every storefront page.
<!-- Online Store → Themes → ⋯ → Edit code → Layout/theme.liquid
Paste just before the closing </body> tag -->
<script src="https://startsnow.site/widget.js"></script>
<script>
ChatBot.init({
chatbotId: "YOUR_CHATBOT_ID",
allowedDomains: ["yourstore.myshopify.com", "yourstore.com"]
});
</script>Google Tag Manager
If GTM is already on your site, you can deploy the widget without touching code.
Google Tag Manager (works on any site GTM is installed on):
1. In GTM, create a new Tag → "Custom HTML".
2. Paste the snippet below.
3. Set the trigger to "All Pages", then Submit / Publish.
<script src="https://startsnow.site/widget.js"></script>
<script>
ChatBot.init({ chatbotId: "YOUR_CHATBOT_ID" });
</script>Webflow, Squarespace, Wix & Framer
Every major site builder has a custom-code / code-injection slot. Paste the snippet into the footer / end-of-body section and publish.
Webflow: Project Settings → Custom Code → Footer Code → paste → publish.
Squarespace: Settings → Advanced → Code Injection → Footer → paste.
Wix: Settings → Custom Code → Add Code → "Body - end" → paste.
Framer: Site Settings → General → Custom Code → End of <body> tag.
<script src="https://startsnow.site/widget.js"></script>
<script>
ChatBot.init({ chatbotId: "YOUR_CHATBOT_ID" });
</script>API Reference
ChatBot.init(options)async → Promise<boolean>Loads chatbot config, injects the widget DOM, and returns true on success. Must be called before any other method.
| Option | Type | Required | Description |
|---|---|---|---|
| chatbotId | string | Yes | Your chatbot's unique ID from the dashboard. |
| allowedDomains | string[] | No | List of domains allowed to embed the widget. Omit to allow all domains. |
ChatBot.open()voidOpens the chat window. Does nothing if already open.
ChatBot.open(); // programmatically open the chat windowChatBot.close()voidCloses the chat window. Does nothing if already closed.
ChatBot.close(); // programmatically close the chat windowChatBot.toggle()voidFlips the chat window between open and closed.
ChatBot.toggle(); // flip between open and closedChatBot.sendMessage(text)voidPre-fills a message into the chat input box. Useful for seeding context when the user arrives from a specific page or product.
// Pre-fill a message into the chat input box
ChatBot.sendMessage("Hi, I need help with my order");Events
The widget dispatches native CustomEvents on window that you can listen for in your own code.
| Event | detail payload | Fired when |
|---|---|---|
| chatflow-widget-ready | { chatbotId, version } | Widget has fully initialised and the chat UI is rendered. |
// Fired once the widget has fully initialised and is ready
window.addEventListener("chatflow-widget-ready", (event) => {
console.log("Widget ready", event.detail.chatbotId, event.detail.version);
});Content Security Policy
Most sites don't set a Content-Security-Policy and need nothing here. If yours does, allow our script and the API calls it makes — the same two directives you'd add for any third-party widget (analytics, support chat, etc.):
Content-Security-Policy:
script-src 'self' https://startsnow.site;
connect-src 'self' https://startsnow.site;script-src— lets the browser loadwidget.js.connect-src— lets the widgetfetch()its config and send chat messages.- No
frame-srcrule is needed — the chat renders in a Shadow DOM root on your page, not in an iframe.
Debug Mode
Enable verbose [StartsNow Widget] logs in the browser console to diagnose initialization, config loading, and chat request issues.
// Enable verbose logging in the browser console
ChatBot.enableDebug();
// Disable debug logging
ChatBot.disableDebug();Debug mode is persisted to window.__STARTSNOW_DEBUG__ and survives navigation within a single-page app session.
Troubleshooting
Open your browser's DevTools console and match the symptom below.
The bubble never appears
Check Allowed Domains first — this is the most common cause. Make sure the exact domain you're viewing is in the list, or that the list is empty. Also confirm both <script> tags are present and the chatbotId is correct.
Console shows "[StartsNow Widget] This domain is not in the allowed list"
Add the current domain in your dashboard under Settings → Allowed Domains. Remember it's an exact hostname match (add www. separately if you use it).
Console shows a CSP error mentioning script-src or connect-src
Your site has a strict Content-Security-Policy. Add https://startsnow.site to both the script-src and connect-src directives — see Content Security Policy. There is no frame-src requirement.
The bubble opens but messages fail
Confirm the chatbot is Activein the dashboard and hasn't hit its monthly message limit. Check the Network tab for the /api/chat request and its response.
Ready to embed?
Create a chatbot in the dashboard and copy your embed code from the chatbot detail page.
Go to Dashboard