Back to Documentation

Chat Widget

The cvz-widget documentation for easy chat integration

Last updated: 2/8/2026

ConverZen Chat Widget

A beautiful, customizable, and easy-to-integrate chat widget SDK for ConverZen. Supports streaming responses, session management, and full customization. Please visit https://github.com/converzen/chat-widget for the latest release or to fork your own version.

Features

  • 🚀 Easy Integration - Works with any website, no framework required
  • 💬 Real-time Streaming - Server-Sent Events (SSE) for live chat responses
  • 🎨 Fully Customizable - Colors, sizes, position, and more
  • 🔐 Flexible Authentication - API key or JWT token support
  • 💾 Session Management - Continue conversations across page reloads
  • 📱 Responsive Design - Works on desktop and mobile
  • 🎯 TypeScript Support - Full type definitions included

Installation

Choose the method that works best for you:

Option 1: CDN (Easiest - No Build Step)

Perfect for static websites or quick integration. Just include the script tag:

<!DOCTYPE html>
<html>
<head>
  <title>My Website</title>
</head>
<body>
  <h1>Welcome</h1>
  <!-- Include the widget -->
  <script
          async
          src="https://converzen.de/widget/latest/cvz-widget.js"
          id="cvz-widget-script"
  ></script>
  
  <!-- Initialize -->
  <script>
      (function() {
          const script = document.getElementById('cvz-widget-script');

          const initWidget = () => {
              if (window.cvzWidget) {
                  window.cvzWidget.init({
                      apiKey: "your-api-key-here",
                      headerMsg: "Chat with us",
                      initialGreeting: "Hello! How can we help you?",
                      onSaveMessages: async (sessionId, messages) => {
                          // Save messages to your backend
                          await fetch('/api/messages', {
                              method: 'POST',
                              body: JSON.stringify({ sessionId, messages})
                          });
                      },
                      onLoadMessages: async () => {
                          // Load messages from your backend
                          const response = await fetch('/api/messages');
                          return response.json();
                      }
                  });
              }
          };

          // Case 1: Script finishes loading after this block runs
          script.addEventListener('load', initWidget);

          // Case 2: Script was already cached/loaded (Fail-safe)
          if (window.cvzWidget) {
              initWidget();
          }
      })();
  </script>
</body>
</html>

Option 2: NPM Package

Best for projects using bundlers (Webpack, Vite, etc.):

npm install @converzen/chat-widget

Then import and use:

import { init } from '@converzen/chat-widget';

init({
  apiKey: "your-api-key-here",
  // ... configuration
});

Or in TypeScript:

import { init, WidgetConfig } from '@converzen/chat-widget';

const config: WidgetConfig = {
  apiKey: "your-api-key-here",
  headerMsg: "Chat with us",
  // ... other options
};

init(config);

Option 3: GitHub Repository

For developers who want to customize or contribute:

git clone https://github.com/converzen/chat-widget.git
cd chat-widget
npm install
npm run build

Then include the built file from dist/cvz-widget.js in your project.

Configuration

Basic Configuration

cvzWidget.init({
  // Optional: API endpoint (optional, defaults to https://chat.converzen.de)
  chatUrl: "https://chat.converzen.de",
  
  // Authentication: Use either apiKey OR getToken (not both)
  apiKey: "sk_test_...", // For demo/insecure mode
  // OR
  getToken: async () => {
    // For secure mode - keeps the api-key out of the client.
    // call your backend to get a JWT token
    // The token can be retrieved from https://chat.converzen.de/api/get_token 
    // using your api-key. 
    // Your route should be secured by a login or recaptcha.
    const response = await fetch('/api/get-token');
    const data = await response.json();
    return data.token; // or { token: "...", expiresAt: 1234567890 }
  },
  
  // Message persistence
  onSaveMessages: async (messages) => {
    // Save to your backend or local storage
  },
  onLoadMessages: async () => {
    // Load from your backend or local storage
    return [];
  },
  
  // UI Customization
  headerMsg: "Chat with us",
  initialGreeting: "Hello! How can we help?",
  promptPlaceholder: "Type your message...",
  
  // Optional: Continue existing conversation
  sessionId: "existing-session-id",
  
  // Optional: Persona identifier
  persona: "your-persona-id",
});

Styling Customization

Customize the widget appearance:

cvzWidget.init({
  // ... other config
  style: {
    // Position: preset or custom
    position: 'bottom-right', // 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'
    // OR custom: { bottom: '20px', right: '20px' }
    
    // Dialog size: preset or custom
    dialogSize: 'medium', // 'small' | 'medium' | 'large'
    // OR custom: { width: 400, height: 600 }
    
    // Frame/border color (hex)
    frameColor: '#E5E7EB',
    
    // Button colors (hex codes)
    buttonColor: {
      normal: '#2563EB',  // Normal state
      hover: '#1D4ED8',   // Hover state
      open: '#1F2937'     // When chat is open
    }
  }
});

Advanced: Token with Expiration

For better performance and security, return token expiration:

getToken: async () => {
  const response = await fetch('/api/get-token');
  const data = await response.json();
  return {
    token: data.token,
    expiresAt: Date.now() + (data.expires_in * 1000) // Convert to milliseconds
  };
}

API Reference

WidgetConfig

OptionTypeRequiredDefaultDescription
chatUrlstringNo"https://chat.converzen.de"API endpoint URL
apiKeystringNo*-Direct API key (insecure/demo mode)
getToken() => Promise<string | TokenResponse>No*-Function to get JWT token
onSaveMessages(messages: ChatMessage[]) => Promise<void>Yes-Save messages callback
onLoadMessages() => Promise<ChatMessage[]>Yes-Load messages callback
headerMsgstringNo"Support Chat"Header title
initialGreetingstringNo-Initial greeting message
promptPlaceholderstringNo"Type a message..."Input placeholder
sessionIdstringNo-Continue existing session
personastringNo-Persona identifier
styleWidgetStyleNo-Styling customization

*Either apiKey or getToken must be provided.

WidgetStyle

OptionTypeDescription
position'bottom-right' | 'bottom-left' | 'top-right' | 'top-left' | { bottom?: string, top?: string, left?: string, right?: string }Widget position
dialogSize'small' | 'medium' | 'large' | { width: number, height: number }Dialog dimensions
frameColorstringBorder color (hex code)
buttonColor{ normal?: string, hover?: string, open?: string }Button colors (hex codes)

TokenResponse

interface TokenResponse {
  token: string;
  expiresAt?: number; // Unix timestamp in milliseconds
}

Examples

Example 1: Simple Integration with API Key

Careful! While this is the quick (and dirty) option, it forces you to keep your api-key in the client, which is generally not good practice and could lead to your key being stolen and abused.

cvzWidget.init({
  chatUrl: "https://chat.converzen.de",
  apiKey: "sk_test_your_key",
  onSaveMessages: async (m) => console.log("Saving:", m),
  onLoadMessages: async () => [],
});

Example 2: Secure Integration with Backend

Your api-key is only used on the backend and can not be stolen easily. Token access to chat also makes life easier for the server - all relevant routing information is present in the token.

For ultimate safety secure your token endpoint with a Recaptcha or reserve it for authenticated users.

cvzWidget.init({
  chatUrl: "https://chat.converzen.de",
  getToken: async () => {
    const res = await fetch('/api/auth/token');
    const { token } = await res.json();
    return token;
  },
  onSaveMessages: async (messages) => {
    await fetch('/api/messages', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(messages)
    });
  },
  onLoadMessages: async () => {
    const res = await fetch('/api/messages');
    return res.json();
  },
  headerMsg: "Support",
  initialGreeting: "Hi! How can we help?"
});

Example 3: Custom Styling

cvzWidget.init({
  chatUrl: "https://chat.converzen.de",
  apiKey: "sk_test_...",
  style: {
    position: 'bottom-left',
    dialogSize: 'large',
    frameColor: '#D1D5DB',
    buttonColor: {
      normal: '#10B981',
      hover: '#059669',
      open: '#374151'
    }
  },
  onSaveMessages: async () => {},
  onLoadMessages: async () => []
});

Development

Building

npm install
npm run build

Development Mode

npm run dev

This will watch for changes and rebuild automatically.

Testing

Open test.html in your browser after building to test the widget locally.

Browser Support

  • Chrome (latest)
  • Firefox (latest)
  • Safari (latest)
  • Edge (latest)

License

MIT

Support

For issues, questions, or contributions, please visit our GitHub repository.