Embed Chat Widget

Add an AI-powered chat widget to any website

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

1

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.

2

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>
3

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-left
  • data-theme - Color theme: light, dark, auto
  • data-button-text - Custom text for the chat button
  • data-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-support are combined automatically
Tip

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:

1

Click "Add endpoint"

Enter a name, URL, and description for each endpoint you want to use.

2

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
}
Tip

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
Tip

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>
Tip

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

  1. New Visitor: Shows contact form + message input
  2. First Message: Creates ExternalUser, sets visitor cookie, hides contact form
  3. Returning Visitor: Cookie identifies them, shows previous conversation
Tip

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:

  1. Collection: The loader.js script collects all data-critical-support attributes plus page title/URL
  2. Transmission: Context is URL-encoded and passed as page_context query param to the iframe
  3. Storage: The controller stores page context in the session and on the Chat record
  4. Enrichment: When AI responds, build_enriched_prompt includes the page context under "## Current Page Context"
  5. Updates: CABWidget.updateContext() can send updated context via postMessage

Security Considerations

  • Domain Restriction: Configure allowed_domains to restrict which sites can embed your widget
  • CSRF Protection: Embedded endpoints skip CSRF verification (stateless)
  • Frame Ancestors: CSP frame-ancestors header 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 /cable in 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-start event triggers the sync