Website and documentation chat widget
Add an AI support chatbot to any website or documentation site with an embed snippet and published knowledge-base articles.
The answerLoops chat widget adds a support chatbot to any website or documentation site that accepts custom JavaScript. Visitors can ask questions about your community, business, or product and receive answers from your published knowledge-base articles without creating an account.
The widget answers from published knowledge-base articles only. Unlike your connected channels, it does not draw on past resolved tickets — those are written for the one person who raised them and often contain account details or internal notes, and a widget visitor is anonymous.
The practical consequence: a workspace with an empty knowledge base gets thin widget answers. Promoting a few resolved tickets into the knowledge base is what makes the widget useful, and it's the same step that generalises an answer so it's safe for anyone to read.
Setup
- Go to Settings → Widget
- Copy the embed snippet — it looks like this:
<script src="https://your-instance.example.com/widget.js" data-widget-id="..."></script>- Paste it into your site's HTML, before the closing
</body>tag
Add the website or documentation domain to Settings → Widget → Allowed domains, as described below. The chat bubble appears at the bottom right of pages where you install the snippet. Visitors select it to open the chat panel.
Adding it by platform
Where you paste the snippet depends on how your site is built. Find your platform below.
Settings → Custom Code → add code → paste the snippet → set it to load on all pages, placement Body - end.
Settings → Advanced → Code Injection → paste the snippet into the Footer field → Save.
Online Store → Themes → Edit code → open theme.liquid → paste the snippet right before </body> → Save. One edit covers every page.
Install a header/footer plugin (e.g. "Insert Headers and Footers"), paste the snippet into the footer field, and save. Avoids hand-editing footer.php, which a theme update can overwrite.
Project Settings → Custom Code → paste the snippet into Footer Code → Save → Publish. Custom code only takes effect after a publish, not just a save.
Load it once from the file that wraps every page, using next/script so it doesn't block rendering.
App Router — app/layout.tsx:
import Script from 'next/script'
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
{children}
<Script
src="https://your-instance.example.com/widget.js"
data-widget-id="..."
strategy="lazyOnload"
/>
</body>
</html>
)
}Pages Router — pages/_app.tsx:
import Script from 'next/script'
import type { AppProps } from 'next/app'
export default function App({ Component, pageProps }: AppProps) {
return (
<>
<Component {...pageProps} />
<Script
src="https://your-instance.example.com/widget.js"
data-widget-id="..."
strategy="lazyOnload"
/>
</>
)
}Paste the raw snippet into public/index.html, before </body> — this is the one file the bundler doesn't compile, so it behaves like a plain static site:
<script src="https://your-instance.example.com/widget.js" data-widget-id="..."></script>Paste the raw snippet into src/index.html, before </body> — Angular's static app shell, equivalent to Vite's index.html:
<script src="https://your-instance.example.com/widget.js" data-widget-id="..."></script>Whichever platform you use, the rule is the same: paste into the one file or field that wraps every page (a layout, an app shell, a footer-code field) — never a single page's content, or the widget only loads there.
Restricting where the widget works
The data-widget-id value is an embed token, and it is deliberately public: it ships in the HTML of every page the widget renders on. There is no way to hide it, which means anyone can copy it out of your page source and use it on their own site — putting your knowledge base and your AI spend behind it.
Settings → Widget → Allowed domains is how you stop that. List the domains you embed on, one per line:
example.com
docs.example.comSubdomains of a listed domain are included, so example.com also covers support.example.com. Ports are ignored. Embeds on any other domain show a short message explaining that the widget is not enabled there, rather than failing silently.
The widget will not load anywhere until you list at least one domain. This is deliberate: an allowlist that defaults to open protects only the people who already thought about it. Your own answerLoops domain is always permitted, so the Preview widget link in Settings works without any configuration.
How to think about the allowlist
The allowlist controls where your widget renders. If someone copies your embed snippet out of your page source and drops it on their own site, that embed is refused.
Treat it as a scoping control rather than as authentication. Your embed token is a public identifier — it appears in your page HTML by design, the same as any embedded widget — so the allowlist is what keeps it useful only where you intend, not a secret that proves who is calling. Usage limits apply independently of it.
Only workspace owners and admins can change the allowed domains or regenerate the token. Members can see the embed snippet but not alter where it works.
Documentation sites
If your docs run on a platform that executes custom JavaScript on every page — Mintlify, Docusaurus, and most others do — you can mount the widget there without editing templates. Drop a small script that injects the snippet, and add the docs domain to your allowed domains.
This is usually the highest-value place to put it: docs visitors are anonymous and arrive with support-shaped questions, which is exactly what the knowledge base answers.
On platforms with no build step, this means the embed token ends up committed to whatever repository holds your docs. The token is a public identifier rather than a secret — it appears in page HTML regardless — but committing it does mean rotating it requires a commit. If that matters for your setup, hold off until the hosted loader ships, which replaces the token in the snippet with a stable slug.
Rotating the token
Tokens expire after 90 days. Settings warns you as the date approaches, and Regenerate token issues a new one.
Regenerating takes effect immediately and invalidates the old token. Every page still using the old snippet stops working until you update it. Rotate when you are able to update your site, not before.
Rate limits & input caps
The widget chat endpoint is public (no login required), so it enforces limits to protect your AI provider bill from abuse:
- Up to 100 requests/minute per widget token (caps total cost exposure for one site even if traffic comes from many IPs)
- Up to 20 requests/minute per visitor (token + IP combination)
- Each message capped at 4,000 characters; at most 50 messages per request
- Request bodies capped at 512KB
The lead-capture endpoint, used when a visitor leaves an email address, is limited separately at 30/minute per token and 5/minute per visitor, with a 64KB body cap and a validated address.
Requests over these limits return 429 Too Many Requests. Limits are enforced across every running instance rather than per process, so the numbers above are the real ceiling. They are not currently configurable.
What the widget can and cannot reach
It answers from your knowledge base — published articles only. It does not read your ticket queue, and it has no access to settings, billing, or team data either; the embed token grants none of those.
Treat knowledge base contents as visible to anyone who can use the widget. If you have ingested internal documentation, review it before making the widget public.