Embed Chat Widget
Add your AI chatbot to any website with a floating chat bubble or iframe embed.
Add your Chipp AI chatbot to any website using either a floating chat bubble widget or an embedded iframe. Choose the option that best fits your site’s design.
Embed Options
| Option | Best For |
|---|---|
| Chat Widget | Floating bubble in corner, doesn’t take page space |
| Iframe | Dedicated chat section embedded in your page |
Option 1: Chat Widget (Floating Bubble)
The chat widget adds a floating chat bubble to the corner of your page. When clicked, it opens your chatbot in an overlay. It’s lightweight (~5KB gzipped), uses Shadow DOM for CSS isolation, and works on any website.
Quick Start
Add a single <script> tag before the closing </body> tag on your website:
<script src="https://your-app.on.chipp.ai/w/chat/chipp-widget.js"></script>That’s it. The widget auto-detects your app from the subdomain and renders a floating chat bubble in the bottom-right corner.
Getting the Widget Code
1
Open your app and click Publish > Share in the navigation.
2
Look for the Widget card on the Share page.
3
Click the code snippet to copy it to your clipboard:
<script src="https://your-app.on.chipp.ai/w/chat/chipp-widget.js"></script>4
Paste the code before the closing </body> tag on your website.
Customizing with Data Attributes
You can customize the widget by adding data-* attributes to the script tag:
<script
src="https://your-app.on.chipp.ai/w/chat/chipp-widget.js"
data-position="bottom-left"
data-greeting="Hey! How can I help?"
data-greeting-delay="3000"
data-color="#ff6600"
data-bubble-size="64"
></script>Attribute Reference
| Attribute | Default | Description |
|---|---|---|
data-app-id | (auto-detected from subdomain) | App slug, e.g. my-app. Required when hosting the script from a different domain |
data-position | bottom-right | Widget position: bottom-right, bottom-left, top-right, top-left |
data-theme | auto | Color theme: light, dark, auto (follows user’s OS preference) |
data-greeting | (none) | Greeting message shown as a preview bubble above the chat button |
data-greeting-delay | 2000 | Delay in milliseconds before the greeting appears (0-30000) |
data-auto-open | (disabled) | Automatically open the chat after this many milliseconds (e.g. 5000 for 5 seconds) |
data-color | (app brand color) | Bubble background color as a hex value (e.g. #2563EB) |
data-bubble-size | 60 | Bubble diameter in pixels (48-80) |
data-bubble-shape | circle | Bubble shape: circle or rounded (rounded rectangle) |
data-icon-url | (app logo) | Custom icon URL for the bubble (falls back to app logo, then a chat icon) |
data-z-index | 2147483646 | CSS z-index for the widget container |
data-hide-on-mobile | false | Set to true to hide the widget on mobile viewports |
Config Priority
Data attributes take the highest priority. If you set a value via data-*, it overrides the server-side configuration. The full priority order is:
- Data attributes on the
<script>tag (highest) - Builder widget config (saved in the Widget tab)
- App brand styles (logo, primary color)
- Defaults (lowest)
Configuring in the Builder
You can also configure the widget visually in the Builder:
- Open your app in the Builder
- Go to Publish > Widget
- Adjust position, size, greeting, and other settings
- See changes reflected in the live preview
- Changes save automatically and update the widget in real-time
The Builder Widget tab lets you configure all the same options as the data attributes, plus a live preview showing exactly how the widget will look on your site.
Widget Features
- CSS isolation: Uses Shadow DOM so your site’s CSS won’t affect the widget and vice versa
- Instant open: Preloads the chat iframe on hover, so it opens instantly when clicked
- Greeting bubble: Optional animated preview message to encourage interaction
- Responsive: Full-screen overlay on mobile, positioned popup on desktop
- Accessible: Full keyboard navigation, ARIA labels, focus management, and reduced-motion support
- Lightweight: ~5KB gzipped (compared to 42KB for legacy widget)
JavaScript API
The widget exposes a window.ChippWidget API for programmatic control. This lets you open/close the chat, identify users, and listen for events from your own JavaScript.
Basic Controls
// Open the chat
ChippWidget.open();
// Close the chat
ChippWidget.close();
// Toggle open/closed
ChippWidget.toggle();
// Check if chat is open
if (ChippWidget.isOpen()) {
console.log("Chat is open");
}
// Remove the widget completely (cannot be undone)
ChippWidget.destroy();User Identification
Identify users so their chat history persists across sessions and you can see who’s chatting in your dashboard.
Client-Side (Simple)
Pass the user’s email and optional name directly. This is the simplest approach for sites where the chat doesn’t require authentication:
ChippWidget.setUser({
email: "jane@example.com",
name: "Jane Doe"
});Server-Side (Secure)
For apps with authentication enabled, use server-side token generation to securely identify users. This prevents spoofing and ensures the user’s identity is verified by your backend.
Step 1: Generate a token on your server
Call the Builder API from your backend to get a bearer token:
curl -X POST https://your-app.on.chipp.ai/api/v1/apps/{APP_ID}/consumers/auth \
-H "Authorization: Bearer YOUR_BUILDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "jane@example.com", "name": "Jane Doe"}'Response:
{
"data": {
"token": "eyJhbGciOi...",
"consumer": {
"id": "...",
"email": "jane@example.com",
"name": "Jane Doe"
}
}
}Step 2: Pass the token to the widget
Send the token to your frontend and pass it to setUser:
ChippWidget.setUser({
email: "jane@example.com",
name: "Jane Doe",
token: "eyJhbGciOi..." // Bearer token from your server
});The token authenticates the user’s session inside the chat iframe without cookies, which makes it work reliably in cross-origin embeds.
You need a Builder API key with write scope to call the /consumers/auth endpoint. Ask the Alchemist to mint one — Alchemist-minted keys default to write on consumers. See Requesting Write Scopes to script the setup.
Per-User Context (Metadata)
Pair /consumers/auth with a metadata field to inject per-user context (territory, project, role, named colleagues, internal IDs) into the system prompt without a separate webhook. The values appear verbatim under an ## About this user block at the end of the prompt, so the agent has them on the first message of every session.
curl -X POST https://your-app.on.chipp.ai/api/v1/apps/{APP_ID}/consumers/auth \
-H "Authorization: Bearer YOUR_BUILDER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"email": "jane@example.com",
"name": "Jane Doe",
"metadata": {
"territory": "Northeast",
"projectId": "PRJ-4821",
"role": "Account Executive",
"managerName": "Sam Reynolds"
}
}'The resulting system prompt includes:
## About this user
- Name: Jane Doe
- Territory: Northeast
- Project id: PRJ-4821
- Role: Account Executive
- Manager name: Sam ReynoldsLimits
- Up to 20 keys per request.
- Keys:
^[a-zA-Z][a-zA-Z0-9_]*$, max 64 chars. EithercamelCaseorsnake_case. - Values:
string(max 2000 chars),number(finite),boolean, ornull. - Total request body capped at 10 KB.
- Reserved keys (
email,name,id,password,identifier,credits,isAnonymous,profile, plus the LLM-managed dossier fieldstimezone/language/preferences/context/notes) are rejected.
Merge semantics
- Per-key upsert. Call
/consumers/authon every page load with whatever metadata you currently know — keys you don’t send are preserved from the previous call. - Pass
"keyName": nullto explicitly delete a key. - Missing keys never error and never render an empty line in the prompt — the corresponding row is simply omitted.
The builder does not need to configure a lead-form or pre-declare fields. Anything you put in metadata shows up in the prompt automatically. If you’d rather collect these values from the user at signup (instead of pushing them from your backend), use the Collection Form feature on the Access tab — that flow stores values in customFields and renders them under the same ## About this user block.
Events
Listen for widget events to react to user interactions:
// Listen for an event (returns an unsubscribe function)
const unsubscribe = ChippWidget.on("message", (data) => {
console.log("New message:", data.role, data.content);
});
// Stop listening
unsubscribe();
// Alternative: manually remove a listener
function onOpen() { console.log("Chat opened"); }
ChippWidget.on("open", onOpen);
ChippWidget.off("open", onOpen);Event Reference
| Event | Payload | Description |
|---|---|---|
open | {} | The chat overlay was opened |
close | {} | The chat overlay was closed |
ready | {} | The chat iframe finished loading and is ready to use |
message | { role, content } | A new message was sent or received. role is "user" or "assistant" |
Full API Reference
| Method | Signature | Description |
|---|---|---|
open() | () => void | Open the chat overlay |
close() | () => void | Close the chat overlay |
toggle() | () => void | Toggle the chat open/closed |
isOpen() | () => boolean | Returns true if the chat is currently open |
setUser(user) | (user: { email: string, name?: string, token?: string }) => void | Identify the current user |
on(event, cb) | (event: string, cb: Function) => () => void | Subscribe to an event. Returns an unsubscribe function |
off(event, cb) | (event: string, cb: Function) => void | Unsubscribe a specific callback from an event |
destroy() | () => void | Remove the widget from the page entirely |
Examples
Open Chat from a Custom Button
<button onclick="ChippWidget.open()">Chat with us</button>
<script src="https://your-app.on.chipp.ai/w/chat/chipp-widget.js"></script>React Integration
import { useEffect } from "react";
function App() {
useEffect(() => {
// Load the widget script
const script = document.createElement("script");
script.src = "https://your-app.on.chipp.ai/w/chat/chipp-widget.js";
script.async = true;
document.body.appendChild(script);
return () => {
// Clean up on unmount
if (window.ChippWidget) {
window.ChippWidget.destroy();
}
script.remove();
};
}, []);
return (
<button onClick={() => window.ChippWidget?.open()}>
Ask a question
</button>
);
}Next.js Integration
"use client";
import Script from "next/script";
export default function ChatWidget() {
return (
<Script
src="https://your-app.on.chipp.ai/w/chat/chipp-widget.js"
strategy="lazyOnload"
data-position="bottom-right"
data-greeting="Need help? Ask me anything!"
/>
);
}Auto-Open After a Delay
Open the chat automatically after 5 seconds, useful for onboarding flows:
<script
src="https://your-app.on.chipp.ai/w/chat/chipp-widget.js"
data-auto-open="5000"
></script>Track Conversations in Analytics
ChippWidget.on("open", () => {
// Google Analytics
gtag("event", "chat_opened", { event_category: "engagement" });
});
ChippWidget.on("message", (data) => {
if (data.role === "user") {
gtag("event", "chat_message_sent", { event_category: "engagement" });
}
});Conditional Widget Loading
Only load the widget for certain users or pages:
if (window.location.pathname.startsWith("/support")) {
const script = document.createElement("script");
script.src = "https://your-app.on.chipp.ai/w/chat/chipp-widget.js";
script.dataset.greeting = "How can we help with your support issue?";
document.body.appendChild(script);
}Server-Side Auth with React
import { useEffect } from "react";
function ChatWidget({ user }) {
useEffect(() => {
if (!user || !window.ChippWidget) return;
// Call your backend to get a widget token
fetch("/api/chat-token", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email: user.email, name: user.name }),
})
.then((res) => res.json())
.then(({ token }) => {
window.ChippWidget.setUser({
email: user.email,
name: user.name,
token,
});
});
}, [user]);
return null;
}Option 2: Iframe Embed
The iframe embeds your chatbot directly into your page layout. Use this when you want the chat to be a fixed part of the page rather than a floating overlay.
Getting the Iframe Code
1
Open your app and click Publish > Share in the navigation.
2
Look for the Iframe card on the Share page.
3
Click the code snippet to copy it:
<iframe src="https://your-app.on.chipp.ai" height="800px" width="100%" frameborder="0" allow="microphone; clipboard-write; screen-wake-lock" title="Your App Name"></iframe>The allow="microphone; clipboard-write; screen-wake-lock" attribute is required for voice mode to work properly on mobile devices. Without screen-wake-lock, the phone screen will lock during a voice call and end the conversation.
4
Paste the iframe code into your website’s HTML where you want the chat to appear.
Customizing the Iframe
Adjust dimensions:
<iframe
src="https://your-app.on.chipp.ai"
height="600px"
width="400px"
frameborder="0"
allow="microphone; clipboard-write; screen-wake-lock"
title="Chat"
></iframe>Responsive container:
<div style="width: 100%; height: 80vh; min-height: 400px;">
<iframe
src="https://your-app.on.chipp.ai"
style="width: 100%; height: 100%; border: none;"
allow="microphone; clipboard-write; screen-wake-lock"
></iframe>
</div>Platform-Specific Instructions
WordPress
For Widget:
- Go to Appearance > Theme Editor (or use a plugin like “Insert Headers and Footers”)
- Add the widget code before
</body>in your theme’s footer
For Iframe:
- Edit your page or post
- Add a Custom HTML block
- Paste the iframe code
Webflow
- Go to Project Settings > Custom Code
- For widget: Paste in “Footer Code”
- For iframe: Add an Embed element to your page
Squarespace
For Widget:
- Go to Settings > Advanced > Code Injection
- Paste in the “Footer” section
For Iframe:
- Add a Code Block to your page
- Paste the iframe code
Shopify
For Widget:
- Go to Online Store > Themes > Edit code
- Find
theme.liquidand paste before</body>
For Iframe:
- Edit the page where you want the chat
- Add a Custom Liquid section
- Paste the iframe code
Wix
For Widget:
- Go to Settings > Custom Code
- Click Add Code in the Body - End
- Paste the widget script tag
For Iframe:
- Add an HTML iframe element to your page
- Set the source URL to your app URL
Framer
For Widget:
- Go to Site Settings > Custom Code
- Add the widget script in the “End of
<body>tag” section
For Iframe:
- Add a Code component
- Paste the iframe embed code
Kajabi
See the full step-by-step walkthrough at Embed Chipp into Kajabi.
Kajabi fully supports both the floating widget and inline iframe embeds. You have two routes — pick the one that fits your goal.
For Widget (site-wide — recommended):
This is the easiest route. The widget appears on every page of your Kajabi site (excluding the checkout and upsell pages, which Kajabi locks down).
- In your Kajabi admin, go to Settings > Site Details
- Scroll to the Header Page Scripts section
- Paste the widget script tag
- Click Save
The widget will appear as a floating bubble on every public page.
For Widget (per-page):
If you only want the widget on specific pages (e.g. a sales page, a specific landing page), use a Custom Code block instead.
- Open the page in the Kajabi page builder (Website > Pages, Marketing > Landing Pages, or Products > Customize on the Premier theme)
- Click Customize > Add block > Custom Code
- Paste the widget script tag
- Click Save and republish the page
For Iframe (per-page):
- Open the page in the Kajabi page builder
- Click Customize > Add block > Custom Code
- Paste the iframe code
- Click Save and republish the page
Momentum-theme Product pages: Kajabi’s Momentum theme blocks <iframe> tags inside Custom Code blocks on Product pages (this is a Kajabi limitation, not a Chipp one). The floating widget script-tag still works there. If you need an inline iframe on a Product page, switch that page to the Premier theme, or use a Website Page or Landing Page instead.
Because Kajabi pages run on *.kajabi.com (or your Kajabi custom domain) and your chatbot runs on *.on.chipp.ai (or your Chipp custom domain), they are cross-origin — which means the Safari ITP / Chrome third-party cookie behavior described in Cookies & Cross-Origin Embeds below applies. If your Kajabi site requires login and you want the chatbot to know who the user is, see the server-side setUser() flow.
Cookies & Cross-Origin Embeds
Chipp automatically handles cross-origin cookie restrictions for iframe and widget embeds. When your chatbot detects it’s running inside an iframe on a different domain, it sets cookies with SameSite=None so the browser allows them in the cross-site context. This means both anonymous and authenticated chat work out of the box in iframe embeds — no extra configuration needed.
Some browsers (notably Safari with Intelligent Tracking Prevention) may still block third-party cookies in certain configurations. If you notice users having trouble maintaining their chat sessions in an iframe embed, consider one of these alternatives:
- Set up a custom domain that’s a subdomain of your website (best for iframe embeds — same-site cookies just work)
- Use server-side auth with
setUser()to pass a bearer token via PostMessage (bypasses cookies entirely)
How Server-Side Auth Bypasses Cookie Issues
For apps with authentication enabled, or for maximum compatibility with strict browser privacy settings, the setUser({ token }) approach avoids cookies entirely:
- Your server calls
POST /api/v1/apps/{APP_ID}/consumers/authwith the user’s email - The API returns a bearer token
- Your frontend passes the token to
ChippWidget.setUser() - The widget sends the token to the iframe via PostMessage
- The iframe uses the bearer token for authentication instead of cookies
This works in cross-origin contexts because PostMessage is not affected by cookie restrictions.
You can configure these settings in:
- Settings page for custom domains
- Access page for authentication settings
Content Security Policy (CSP)
If your website uses a strict Content Security Policy, you’ll need to allow the widget’s script and iframe:
frame-src *.on.chipp.ai;
script-src your-app.on.chipp.ai;Replace with your custom domain if you have one configured.
If you’re using a nonce-based CSP, add the nonce to the script tag:
<script nonce="YOUR_NONCE" src="https://your-app.on.chipp.ai/w/chat/chipp-widget.js"></script>Troubleshooting
Widget Not Appearing
- Check code placement: Widget code should be before
</body>, not inside<head> - Check for conflicts: Other scripts may interfere — check browser console for errors
- Check app status: Ensure your app is published and active
- Check CSP: Your site’s Content Security Policy may block the script or iframe
- Check mobile: If
data-hide-on-mobile="true"is set, the widget won’t appear on mobile
Widget Appears But Chat Won’t Open
- Check the console: Look for network errors or CORS issues
- Check the app URL: Ensure the app is reachable at the URL in the script
src - Check authentication: If auth is enabled, use
setUser()with a token or set up a custom domain
Iframe Shows “Session Not Found” Error
This typically means the browser is blocking cookies in the cross-origin iframe context. Chipp handles this automatically, but some strict privacy settings can still interfere:
- Safari ITP: Safari aggressively blocks third-party cookies. Use a custom domain (subdomain of your site) or server-side auth with
setUser({ token }) - Chrome Incognito: Third-party cookies may be blocked by default in Incognito mode
- Browser extensions: Privacy extensions (uBlock Origin, Privacy Badger) may block cross-site cookies
Iframe Not Loading
- Check the URL: Make sure the iframe
srcmatches your app’s URL exactly - CSP headers: Your site’s Content Security Policy may block iframes
- X-Frame-Options: Some hosting providers set
X-Frame-Options: DENYby default
Styling Issues
- Z-index conflicts: Use the
data-z-indexattribute to adjust widget stacking order - Container overflow: For iframe, make sure parent elements don’t have
overflow: hidden - Mobile display: Use
data-hide-on-mobile="true"if the widget conflicts with your mobile layout - Double widget: If you see two widgets, make sure the script tag is only included once. The widget prevents double-initialization, but old v1 and new v2 scripts can coexist
User Identity Not Working
- Check
setUsertiming: CallsetUser()after the widget script has loaded. Listen for thereadyevent if needed:javascriptChippWidget.on("ready", () => { ChippWidget.setUser({ email: "user@example.com" }); }); - Check token validity: Server-side tokens expire. Generate a fresh token on each page load
- Check API key scope: The Builder API key must have write scope to call
/consumers/auth