Overview
The embeddable chat widget allows you to add a Claude Code-powered chat interface to any website. Your users can ask questions about your codebase directly from your documentation, support pages, or internal tools.
Quick Start
Create a Chat Agent
Go to Chat Agents and click "New Chat Agent". Give it a name and configure the system prompt that defines how the assistant should respond.
Get Your Embed Code
Click the "Embed" button on your chat agent to view the embed code. You'll see a JavaScript snippet that looks like this:
<script
src="https://critical.cx/embed/YOUR_TOKEN/loader.js"
async
data-position="bottom-right"
></script>
Add to Your Website
Paste the script tag into your HTML just before the closing </body> tag. The chat widget will appear automatically.
Configuration Options
Customize the widget behavior using data attributes:
<script
src="https://critical.cx/embed/YOUR_TOKEN/loader.js"
async
data-position="bottom-right"
data-theme="dark"
data-button-text="Ask AI"
data-customer-id="12345"
data-account-tier="enterprise"
></script>
Available Options
data-position- Widget position:bottom-right,bottom-leftdata-theme- Color theme:light,dark,autodata-button-text- Custom text for the chat buttondata-customer-*- Pass customer context to enhance responses
Page Context with data-critical-support
The embed widget automatically reads the data-critical-support attribute from any element on your page. This context is passed to the AI to provide more relevant, page-specific answers.
How It Works
Add the data-critical-support attribute to any HTML element (typically the <body> tag or a container div). The widget will collect all instances and include them as context for the AI.
<body data-critical-support="This is the Pricing page. Users here are comparing plans and considering a purchase.">
<!-- Your page content -->
<div data-critical-support="Current promotion: 60-day free trial, no credit card required.">
<!-- Promotional content -->
</div>
</body>
Best Practices
- Include relevant page context that helps the AI understand where the user is
- Add specific information that might be helpful for answering questions
- Use server-side rendering to include dynamic data (user tier, feature flags, etc.)
- Multiple elements with
data-critical-supportare combined automatically
The widget also automatically includes the page title and URL path as context, so users don't need to explain which page they're on.
Prompt Enrichment Endpoints
For more dynamic context, you can configure API endpoints that are called before each AI response. The JSON data from these endpoints is automatically injected into the system prompt.
Setting Up Endpoints
In the Chat Agent settings, scroll to "Prompt Enrichment Endpoints" and add your API URLs:
Click "Add endpoint"
Enter a name, URL, and description for each endpoint you want to use.
Configure Your Endpoints
Each endpoint should return valid JSON. The response will be included in the AI's context.
Example Endpoints
# Pricing data endpoint
GET /api/public/pricing
Response: {
"plans": [
{"name": "Starter", "price": "$29/mo", "features": [...]},
{"name": "Pro", "price": "$99/mo", "features": [...]}
]
}
# Documentation topics with full content
GET /api/public/documentation
Response: {
"topics": [
{
"slug": "getting-started",
"title": "Getting Started",
"description": "Quick start guide",
"url": "/docs/getting-started",
"content": "Full plain-text content of the documentation article..."
}
],
"total": 5
}
The /api/public/documentation endpoint includes the full content of each documentation article as plain text. This allows the AI to answer detailed questions about how to use the service.
Endpoint Requirements
- Must return valid JSON with
Content-Type: application/json - Should respond within 5 seconds (timeout)
- Can be internal or external URLs
- Enable/disable individual endpoints as needed
Use enrichment endpoints for data that changes frequently (pricing, product catalog, documentation updates) rather than hardcoding it in the system prompt.
Passing Context
Make your chat responses smarter by passing context data attributes. This information is included with each message to help the AI provide more relevant answers.
<script
src="https://critical.cx/embed/YOUR_TOKEN/loader.js"
data-user-name="John Smith"
data-user-email="john@company.com"
data-account-plan="enterprise"
data-current-page="/docs/api"
></script>
Dynamic data attributes can be rendered server-side to include user-specific information like account status, feature flags, or transaction history.
Contact Collection
The embedded chat widget automatically collects visitor contact information on first message. This helps you identify and follow up with users.
How It Works
- When a new visitor opens the chat, they see a contact form (first name, last name, email)
- On first message submission, the contact info is captured and an ExternalUser is created
- A secure visitor token cookie is set to recognize returning visitors
- Subsequent visits show the chat history without requiring re-identification
Visitor Identification Flow
- New Visitor: Shows contact form + message input
- First Message: Creates ExternalUser, sets visitor cookie, hides contact form
- Returning Visitor: Cookie identifies them, shows previous conversation
The visitor token cookie persists for 1 year and uses SameSite=None; Partitioned for cross-site iframe compatibility.
JavaScript API
Control the widget programmatically using the window.CABWidget API:
// Open the widget
CABWidget.open();
// Close the widget
CABWidget.close();
// Toggle the widget
CABWidget.toggle();
// Check if widget is open
if (CABWidget.isOpen()) {
console.log('Widget is open');
}
// Update page context dynamically
CABWidget.updateContext('User just completed checkout for Plan X');
Data Attributes Reference
Complete list of data attributes for configuring the embed widget:
| Attribute | Description | Example |
|---|---|---|
data-position |
Widget button position | bottom-right, bottom-left |
data-theme |
Color theme | light, dark, auto |
data-button-text |
Custom button label | Ask AI |
data-user-* |
User attributes passed to AI | data-user-name="John" |
data-customer-* |
Customer attributes for context | data-customer-plan="pro" |
data-account-* |
Account attributes for context | data-account-tier="enterprise" |
API Endpoints
The embed widget uses these API endpoints internally:
Show Embedded Chat
GET /embed/:embed_token
Query params:
- page_context: URL-encoded string of page context
Response: HTML page with embedded chat interface
Send Message
POST /embed/:embed_token/chats/:chat_id/messages
Form params:
- message[content]: The user's message
- first_name: (optional) Contact first name
- last_name: (optional) Contact last name
- email: (optional) Contact email
Response: Redirect to embedded chat (Turbo Stream updates)
Load Widget Script
GET /embed/:embed_token/loader.js
Response: JavaScript that initializes the widget
Page Context Flow
Understanding how page context is captured and used:
- Collection: The loader.js script collects all
data-critical-supportattributes plus page title/URL - Transmission: Context is URL-encoded and passed as
page_contextquery param to the iframe - Storage: The controller stores page context in the session and on the Chat record
- Enrichment: When AI responds,
build_enriched_promptincludes the page context under "## Current Page Context" - Updates:
CABWidget.updateContext()can send updated context via postMessage
Security Considerations
- Domain Restriction: Configure
allowed_domainsto restrict which sites can embed your widget - CSRF Protection: Embedded endpoints skip CSRF verification (stateless)
- Frame Ancestors: CSP
frame-ancestorsheader limits embedding to allowed domains - Rate Limiting: 10 messages per minute per IP address
Troubleshooting
Widget Not Appearing
- Check browser console for JavaScript errors
- Verify the embed token is correct
- Ensure your domain is in the allowed_domains list (or list is empty for any domain)
Messages Not Displaying
- Check that the ActionCable WebSocket connection is established (look for
/cablein Network tab) - Verify Redis is running for ActionCable pub/sub
- Check the Sidekiq queue for failed jobs
Contact Form Issues
- Both first name and email are required for contact capture
- If contact info isn't saving, check that the hidden fields are being synced before form submission
- The
turbo:submit-startevent triggers the sync