Skip to content
On this page

AI Chat Widget

The Bildhive AI Chat Widget allows you to embed an interactive AI sales and customer assistant on any website.

Bildhive provides three flexible approaches to integrate AI Chat:

  1. Method 1: Ready-Made Embed Code (Bildhive Admin): Use the visual customizer in the Bildhive Admin AI app to preview, brand, and generate a 1-click copy-paste code snippet.
  2. Method 2: Custom Element Embedding & HTML Attributes: Drop in the compiled Web Component (<bildhive-chat-widget>) and customize appearance, copy, and behaviors directly via HTML attributes and JavaScript DOM properties.
  3. Method 3: Building a Custom / Headless Widget with Bildhive APIs: Build your own bespoke chat UI from scratch by directly calling Bildhive's REST and streaming AI chat endpoints.

Technical Highlights

  • Shadow DOM Encapsulation: Styles in <bildhive-chat-widget> are fully insulated. The widget's CSS cannot leak out to affect your website, and your site's global styles cannot distort the widget's layout.
  • Zero Framework Lock-in: The host website does not need to use Vue or any specific JavaScript framework.
  • Self-Contained Sizing: The web component manages its own :host display dimensions dynamically (seamlessly transitioning between the floating launcher pill, circular trigger, lead registration form, active chat conversation, and full-screen maximize mode). No iframe resizer scripts or postMessage bridges are needed.
  • Real-Time Streaming: Leverages streaming AI responses for quick, interactive user feedback.
  • Built-in Lead Capture & Anti-Spam: Includes lead registration, returning visitor detection via localStorage, invisible honeypot spam protection, human submission speed validation, and phone number conflict resolution.

Prerequisites

To embed and use the AI Chat Widget or connect to the AI Chat APIs, you will need:

  1. Instance Token (instance): Your unique Bildhive project instance token.
  2. AI Agent ID (agent-id): The identifier of the configured AI assistant agent from your Bildhive AI application.
  3. API Add-on Enabled: The Bildhive project must have the API Add-on enabled on its subscription plan.
API Add-on Required: To enable the API add-on, navigate to your Bildhive project settings under Settings > Projects > Edit > Add-ons (or contact your project billing owner). If the API add-on is inactive, API requests will return an authorization error.

Method 1: Ready-Made Embed Code (Bildhive Admin)

The easiest way to get started is using the AI Chat Widget Customizer built directly into the Bildhive Admin AI application.

Accessing the Customizer

  1. Log into your Bildhive Admin portal.
  2. Open your project's AI App.
  3. Navigate to Settings > AI Chat Widget Customizer.
Bildhive Admin
└── AI App
    └── Settings
        └── AI Chat Widget Customizer

Features of the Visual Customizer

  • Live Multi-State Preview: Preview how your widget looks and behaves across all 4 visitor states:
    • First Visit (Expanded launcher pill with CTA copy)
    • Returning Visitor (Compact circular launcher button)
    • Lead Registration (Lead capture form with consent checkboxes and policy links)
    • Chat Conversation (Active messaging interface with avatars, action buttons, and markdown styling)
  • Branding & Smart Theming:
    • Select your primary Brand Color (automatically applies to header banners, launchers, primary buttons, and visitor bubbles).
    • Use the Invert Text Color switch if automatic contrast calculation needs manual adjustment.
    • Set custom background and text colors for the AI Assistant Message Bubbles.
  • Media Library Integration: Pick your Header Logo and AI Profile Image directly from your Bildhive media library or enter image URLs.
  • Copy & Legal Customization: Update the welcome headline, introductory description, email/SMS consent disclaimers, and links to your Privacy Policy and Terms of Use.
  • 1-Click Embed Code: Click Copy Embed Code to get a fully generated, ready-to-paste snippet.

Method 2: Custom Element Embedding

If you want to use the pre-built UI with custom attribute values in your codebase, follow these steps.

Step 1: Load the Widget Script

Include the compiled widget loader script in the <head> or before the closing </body> tag of your website:

html
<!-- Bildhive Chat Widget Script Loader -->
<script src="https://cdn.bildhive.com/scripts/CustomWebsiteWidgets/widgetLoader.js" async defer></script>

Note for Development: If testing against Bildhive's development environment, use https://cdn.bildhive.dev/scripts/CustomWebsiteWidgets/widgetLoader.js.

Step 2: Add the Custom Element Tag

Place the <bildhive-chat-widget> element in your HTML:

html
<bildhive-chat-widget 
    instance="YOUR_INSTANCE_TOKEN" 
    agent-id="YOUR_AI_AGENT_ID"
    position="right"
    offset="20px"
    header-text="AI Sales Assistant"
    banner-background="#f7941e"
    button-background="#f7941e"
    icon-background="#f7941e">
</bildhive-chat-widget>

Complete Embed Example

Below is a complete HTML snippet demonstrating how to embed the Web Component, customize styling via HTML attributes, and pass complex object properties (logos, profile avatars, legal links) via JavaScript:

html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My Website with AI Chat</title>

    <!-- 1. Load the Bildhive Widget Script -->
    <script src="https://cdn.bildhive.com/scripts/CustomWebsiteWidgets/widgetLoader.js" async defer></script>
</head>
<body>

    <!-- 2. Declare the Custom Element with HTML Attributes -->
    <bildhive-chat-widget
        instance="634dd0254f594a11fca146b7"
        agent-id="6a0b672ce473781dabff250a"
        position="right"
        offset="20px"
        header-text="AI Sales Assistant"
        header-text-size="medium"
        ai-profile-name="Sarah - AI Assistant"
        icon-button-text="Chat With AI"
        banner-background="#0f766e"
        button-background="#0f766e"
        button-background-hover="#115e59"
        icon-background="#0f766e"
        user-text-bg-color="#0f766e"
        user-text-color="#ffffff"
        ai-text-bg-color="#f3f4f6"
        ai-text-color="#1f2937"
        border-radius="6"
        border-thickness="1"
        action-background="#0d5f58"
        action-color="#ffffff"
        action-border-radius="8"
        welcome-heading="Welcome to Acme Homes!"
        welcome-intro-text="I can help answer questions about our available communities, floor plans, and pricing."
        email-consent-text="I consent to receive email updates regarding new releases and pricing."
        sms-consent-text="I consent to receive SMS updates. Message and data rates may apply.">
    </bildhive-chat-widget>

    <!-- 3. Assign Complex Object Properties via JavaScript -->
    <script>
      customElements.whenDefined('bildhive-chat-widget').then(function () {
        var widget = document.querySelector('bildhive-chat-widget');
        if (!widget) return;

        // Custom Header Logo
        widget.headerLogo = {
          url: 'https://example.com/assets/logo-white.svg'
        };

        // Custom AI Avatar Image
        widget.aiProfileImage = {
          url: 'https://example.com/assets/ai-avatar.png'
        };

        // Legal Links
        widget.privacyPolicy = {
          label: 'Privacy Policy',
          link: 'https://example.com/privacy'
        };

        widget.termsOfUse = {
          label: 'Terms of Use',
          link: 'https://example.com/terms'
        };
      });
    </script>

</body>
</html>

Configuration & Attributes Reference

All attributes on <bildhive-chat-widget> use standard kebab-case naming in HTML.

Core Configuration

AttributeTypeDefaultDescription
instancestringRequiredYour unique Bildhive project instance token.
agent-idstringRequiredThe ID of the AI Chat Agent to connect with.
environmentstringproductionBackend API environment (production or development).
positionstringrightDocking edge on the screen: left or right.
offsetstring20pxMargin distance from screen edges (e.g. 20px, 1.5rem).

Launcher & Trigger Button

AttributeTypeDefaultDescription
icon-button-textstringChat With AIText label on the expanded trigger pill shown to first-time visitors.
icon-backgroundstring#f7941eBackground color of the floating launcher button.
icon-colorstring#ffffffIcon and text color inside the launcher button.
icon-sizenumber45Diameter of the circular launcher icon in pixels.
icon-positionstringrightAlignment of the trigger icon: left or right.
icon-position-offsetnumber5Offset spacing for the icon position in pixels.
button-fontstringinheritFont family for buttons and trigger labels.

Header & Branding

AttributeTypeDefaultDescription
header-textstringAI Sales AssistantTitle displayed in the chat header banner.
header-text-sizestringmediumFont size for header title: small, medium, or large.
header-colorstring#ffffffText and icon color inside the header banner.
banner-backgroundstring#f7941eBackground color of the header banner.
ai-profile-namestringAI AgentDisplay name of the AI assistant shown in chat messages.
ai-profile-typestringimageAvatar type (image).
ai-profile-colorstring#3a4b67Text/icon color for assistant profile badge fallback.
ai-profile-bg-colorstring#f1f1f1Background color for assistant profile badge fallback.
user-profile-colorstring#3a4b67Text/icon color for visitor profile badge.
user-profile-bg-colorstring#f1f1f1Background color for visitor profile badge.

Chat Bubbles

AttributeTypeDefaultDescription
user-text-bg-colorstring#f7941eBackground color of visitor message bubbles.
user-text-colorstring#ffffffText color of visitor message bubbles.
ai-text-bg-colorstring#f1f1f1Background color of assistant message bubbles.
ai-text-colorstring#3a4b67Text color of assistant message bubbles.

Buttons, Inputs & Actions

AttributeTypeDefaultDescription
button-backgroundstring#f7941ePrimary button background color.
button-colorstring#ffffffPrimary button text color.
button-borderstring#d9d9d9Primary button border color.
button-background-hoverstring#e6821aButton background color on hover.
button-hoverstring#ffffffButton text color on hover.
button-border-hoverstring#d9d9d9Button border color on hover.
border-radiusnumber4Corner radius in pixels for inputs and standard buttons.
border-thicknessnumber1Border thickness in pixels for input fields.
action-backgroundstring#98612bBackground color of header action buttons (History, New Chat).
action-colorstring#ffffffIcon color of header action buttons.
action-background-hoverstring#774e2cBackground color of header action buttons on hover.
action-color-hoverstring#ffffffIcon color of header action buttons on hover.
action-border-radiusnumber8Border radius in pixels for header action buttons.

Lead Form Copy

AttributeTypeDefaultDescription
welcome-headingstringHi there!Headline displayed at the top of the registration screen.
welcome-intro-textstring"I'm here to help you understand..."Subtext describing the assistant's capabilities.
email-consent-textstring"I consent to receive email communications..."Disclaimer copy beside the email consent checkbox.
sms-consent-textstring"I consent to receive SMS communications..."Disclaimer copy beside the SMS consent checkbox.

Complex Object Properties (JavaScript)

Some properties are JavaScript objects that cannot be passed cleanly as HTML string attributes. You can set them directly on the DOM element once the custom element is defined:

javascript
customElements.whenDefined('bildhive-chat-widget').then(() => {
  const widget = document.querySelector('bildhive-chat-widget');
  if (!widget) return;

  // Header Logo URL
  widget.headerLogo = {
    url: 'https://your-domain.com/assets/logo.svg'
  };

  // AI Profile Avatar Image URL
  widget.aiProfileImage = {
    url: 'https://your-domain.com/assets/assistant-avatar.png'
  };

  // Privacy Policy Link
  widget.privacyPolicy = {
    label: 'Privacy Policy',
    link: 'https://your-domain.com/privacy-policy'
  };

  // Terms of Use Link
  widget.termsOfUse = {
    label: 'Terms of Use',
    link: 'https://your-domain.com/terms-of-service'
  };
});

Method 3: Building a Custom / Headless Chat Widget with Bildhive APIs

If you want complete design freedom, need to integrate the chat directly into a mobile app, or want to create a bespoke conversational user interface, you can build a custom widget by calling the Bildhive REST and streaming APIs directly.

API Architecture & Endpoints

EnvironmentCore API Base URLAI Streaming Backend Base URL
Productionhttps://api.bildhive.comhttps://ai-backend.bildhive.com
Developmenthttps://api.bildhive.devhttps://ai-backend.bildhive.dev

Authentication: Pass your project instance token in the query string ?token=YOUR_INSTANCE_TOKEN for all API requests.

For interactive Swagger parameter specifications, request/response models, and status codes, refer to the Bildhive API Documentation (Contacts) and Bildhive API Documentation (AI Chat).


End-to-End Implementation Flow

1. Visitor Arrival
   ├── Check localStorage for 'lead_email'
   └── IF email exists -> GET /v1/contacts (Verify returning contact)
       └── IF valid -> Proceed to History / Chat Session

2. Lead Registration (First-Time Visitor)
   ├── (Optional) POST /v1/contacts/check-phone-duplicate
   └── POST /v1/contacts (Create Contact -> Returns contactId)
       └── Save email to localStorage ('lead_email')

3. Conversation History
   └── GET /v1/ai-agent-conversations?contact={contactId}
       └── Load previous sessions and messages

4. Real-Time Streaming Chat
   └── POST /v1/ai-agent-conversations/{agentId}/chat (Header: X-Stream: true)
       └── Stream SSE chunks (Text tokens, Tool Thinking, UI Elements, Finish, Handoff)

Step 1: Check / Verify Returning Contact

When a visitor opens your custom chat UI, check if an email is stored in localStorage under lead_email. If found, verify their contact profile:

Request:

http
GET https://api.bildhive.com/v1/contacts?token=YOUR_INSTANCE_TOKEN&email=mark@bildhive.com

Response (200 OK):

json
{
  "id": "687e488af4484e271a434bab",
  "firstName": "Mark",
  "lastName": "Evans",
  "email": "mark@bildhive.com",
  "phone": ""
}

If the contact is returned, store contact.id as the active contactId and allow them to proceed straight to the chat or history screen.


Step 2: Lead Registration & Duplicate Phone Validation

For new visitors, display a registration form to collect their contact details before initiating chat sessions.

A. (Optional) Check Duplicate Phone Number

If the user provides a phone number, validate whether the phone number is already registered under a different email:

Request:

http
POST https://api.bildhive.com/v1/contacts/check-phone-duplicate?token=YOUR_INSTANCE_TOKEN
Content-Type: application/json

{
  "email": "john.doe@example.com",
  "phone": "+15551234567"
}

Response:

  • No conflict: { "success": true }
  • Conflict detected:
    json
    {
      "status": "phone_conflict",
      "requiresConfirmation": true,
      "message": "This phone number is already associated with an account. Would you like to continue?"
    }
    If requiresConfirmation is true, prompt the user to confirm before proceeding with registration.

B. Register Contact Profile

Submit the lead registration form to create the contact in your Bildhive CRM:

Request:

http
POST https://api.bildhive.com/v1/contacts?token=YOUR_INSTANCE_TOKEN
Content-Type: application/json
X-Timezone: America/New_York

{
  "fullName": "John Doe",
  "email": "john.doe@example.com",
  "phone": "+15551234567",
  "source": "AI CHAT BOT WIDGET",
  "sourceUrl": "https://mywebsite.com/contact",
  "consent": true,
  "smsConsent": true,
  "agentId": "YOUR_AI_AGENT_ID"
}

Response (201 Created):

json
{
  "id": "64fa821c9e42100018f98a21",
  "fullName": "John Doe",
  "email": "john.doe@example.com",
  "createdAt": "2026-08-27T10:00:00.000Z"
}

After successful registration, store data.email in localStorage.setItem('lead_email', data.email) and keep data.id as the current contactId.


Step 3: Fetching Conversation History

To allow returning users to review previous conversations or resume an earlier discussion, fetch their conversation sessions:

Request:

http
GET https://api.bildhive.com/v1/ai-agent-conversations?token=YOUR_INSTANCE_TOKEN&contact=687e488af4484e271a434bab&_limit=20&_start=0&_sort=updatedAt:desc

Response (200 OK):

json
[
  {
    "id": "6a6b4b23a32ef33f9f183368",
    "sessionId": "fabb5553-8e7b-4141-afde-da351a0993d1",
    "conversationStartedAt": "2026-07-30T13:01:23.980Z",
    "lastMessageAt": "2026-07-30T13:01:50.614Z",
    "messages": [
      {
        "id": "9d7aeeb2-fda1-4caa-8db7-a793d9a9b1b2",
        "type": "human",
        "createdAt": "2026-07-30T13:01:17.905Z",
        "data": {
          "content": "Hi There"
        }
      },
      {
        "id": "06cee681-bbce-4a4d-8765-954a9f1504f6",
        "type": "ai",
        "createdAt": "2026-07-30T13:01:23.909Z",
        "data": {
          "content": "{\"output\":{\"text\":\"Hello! Welcome to Joey’s Cove. How can I assist you with your home search or any questions you have about the community today?\",\"ui_elements\":null}}"
        }
      }
    ],
    "createdAt": "2026-07-30T13:01:23.912Z",
    "updatedAt": "2026-07-30T13:13:21.154Z"
  }
]

When a user clicks an earlier conversation, set sessionId to chat.sessionId and render the past messages in your chat window.


Step 4: Real-Time Streaming Chat Endpoint

To send a message and stream AI responses in real-time, call the AI Backend streaming endpoint:

Request:

http
POST https://ai-backend.bildhive.com/v1/ai-agent-conversations/YOUR_AI_AGENT_ID/chat?token=YOUR_INSTANCE_TOKEN
Content-Type: application/json
X-Stream: true

{
  "userInput": "Can you show me 4-bedroom floor plans?",
  "sessionId": "sess_98234120938",
  "contactId": "64fa821c9e42100018f98a21"
}

Note: For a brand new conversation, omit sessionId (or pass null). The streaming API will return a new sessionId in the first event chunk.

Server-Sent Events (SSE) Stream Protocol

The server returns a continuous text stream formatted as standard SSE lines prefixed with data: :

txt
data: {"sessionId":"sess_98234120938","outputBlocks":[{"output":{"text":"Here are our available 4-bedroom floor plans:"}}]}

data: {"part":{"type":"tool-input-start"}}

data: {"part":{"type":"tool-result"}}

data: {"outputBlocks":[{"output":{"text":"Here are our available 4-bedroom floor plans:\n\n1. **The Oakridge** (2,850 sq ft)\n2. **The Hawthorne** (3,200 sq ft)","ui_elements":[{"type":"card","data":{"title":"The Oakridge","imageUrl":"https://...","price":"$650,000"}}]}}]}

data: {"type":"finish","sessionId":"sess_98234120938","outputBlocks":[{"output":{"text":"...","ui_elements":[...]}}]}

data: [DONE]

Event Object Structure:

  • event.sessionId: The conversation session ID. Save this for subsequent user messages in the same thread.
  • event.outputBlocks: Array of response blocks containing:
    • block.output.text: Progressive markdown response text.
    • block.output.ui_elements: Array of interactive cards or media cards returned by the AI agent.
    • block.showStartNewChatButton: Boolean flag indicating if the agent suggests starting a fresh conversation.
    • block.newChatTarget: Optional topic/handoff target data.
  • event.part.type === 'tool-input-start': Signals that the AI agent is currently executing a tool or querying live database records (show a "Thinking..." indicator).
  • event.part.type === 'tool-result': Signals that tool execution is complete.
  • event.type === 'finish': The response stream has completed.
  • event.type === 'handoff': The conversation has been handed off to a live agent.
  • event.type === 'error': Stream encountered an error.

Step 5: Complete Vanilla JavaScript Headless Client

Here is a lightweight, dependency-free JavaScript client that handles authentication, lead registration, history fetching, and streaming responses:

javascript
/**
 * Bildhive AI Chat Headless Client
 */
class BildhiveAIChatClient {
  constructor({ instance, agentId, environment = 'production' }) {
    this.instance = instance;
    this.agentId = agentId;
    this.apiBase = environment === 'development'
      ? 'https://api.bildhive.dev'
      : 'https://api.bildhive.com';
    this.aiBase = environment === 'development'
      ? 'https://ai-backend.bildhive.dev'
      : 'https://ai-backend.bildhive.com';
    
    this.contact = null;
    this.sessionId = null;
    this.abortController = null;
  }

  // 1. Check returning contact from email
  async checkReturningContact(email) {
    const res = await fetch(`${this.apiBase}/v1/contacts?token=${this.instance}&email=${encodeURIComponent(email.toLowerCase())}`);
    if (!res.ok) throw new Error('Contact lookup failed');
    const contact = await res.json();
    this.contact = contact;
    return contact;
  }

  // 2. Register a new lead
  async registerLead({ fullName, email, phone, consent = true, smsConsent = false }) {
    const timeZone = Intl?.DateTimeFormat?.()?.resolvedOptions?.()?.timeZone;
    const headers = { 'Content-Type': 'application/json' };
    if (timeZone) headers['X-Timezone'] = timeZone;

    const res = await fetch(`${this.apiBase}/v1/contacts?token=${this.instance}`, {
      method: 'POST',
      headers,
      body: JSON.stringify({
        fullName,
        email: email.toLowerCase(),
        phone: phone || undefined,
        source: 'CUSTOM AI CHAT WIDGET',
        sourceUrl: window.location.href,
        consent,
        smsConsent,
        agentId: this.agentId
      })
    });

    if (!res.ok) {
      const err = await res.json();
      throw new Error(err.message || 'Registration failed');
    }

    const contact = await res.json();
    this.contact = contact;
    window.localStorage.setItem('lead_email', contact.email || '');
    return contact;
  }

  // 3. Fetch past conversation history
  async getChatHistory(limit = 20, start = 0) {
    if (!this.contact?.id) throw new Error('No active contact');
    const query = new URLSearchParams({
      token: this.instance,
      contact: this.contact.id,
      _limit: limit,
      _start: start,
      _sort: 'updatedAt:desc'
    }).toString();

    const res = await fetch(`${this.apiBase}/v1/ai-agent-conversations?${query}`);
    if (!res.ok) throw new Error('Failed to load history');
    return await res.json();
  }

  // 4. Send message and stream AI response
  async sendMessage(userInput, { onToken, onThinking, onFinish, onError, onHandoff }) {
    if (!this.contact?.id) throw new Error('Contact must be registered before chatting');

    if (this.abortController) this.abortController.abort();
    this.abortController = new AbortController();

    const payload = {
      userInput,
      sessionId: this.sessionId,
      contactId: this.contact.id
    };

    try {
      const res = await fetch(`${this.aiBase}/v1/ai-agent-conversations/${this.agentId}/chat?token=${this.instance}`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'X-Stream': 'true'
        },
        body: JSON.stringify(payload),
        signal: this.abortController.signal
      });

      if (!res.ok) throw new Error('Failed to initiate stream');

      const reader = res.body.getReader();
      const decoder = new TextDecoder();
      let buffer = '';

      while (true) {
        const { done, value } = await reader.read();
        if (done) break;

        buffer += decoder.decode(value, { stream: true });
        const lines = buffer.split('\n');
        buffer = lines.pop() || '';

        for (const line of lines) {
          const trimmed = line.trim();
          if (!trimmed || !trimmed.startsWith('data:')) continue;
          const dataPayload = trimmed.slice(5).trim();
          if (dataPayload === '[DONE]') continue;

          try {
            const event = JSON.parse(dataPayload);
            if (event.sessionId) this.sessionId = event.sessionId;

            if (event.type === 'finish') {
              if (onFinish) onFinish(event);
            } else if (event.type === 'error') {
              if (onError) onError(event.error || 'Stream error');
            } else if (event.type === 'handoff') {
              if (onHandoff) onHandoff(event.data);
            } else {
              if (event.part?.type === 'tool-input-start' && onThinking) onThinking(true);
              if (event.part?.type === 'tool-result' && onThinking) onThinking(false);

              if (event.outputBlocks && event.outputBlocks.length > 0) {
                const text = event.outputBlocks.map(b => b.output?.text || '').join('\n\n');
                const uiElements = event.outputBlocks.flatMap(b => b.output?.ui_elements || []);
                if (onToken) onToken({ text, uiElements });
              }
            }
          } catch (e) {
            console.error('Error parsing SSE chunk:', e);
          }
        }
      }
    } catch (err) {
      if (err.name === 'AbortError') {
        console.log('Stream aborted by user');
      } else {
        if (onError) onError(err.message);
      }
    }
  }

  // 5. Cancel active stream
  stopStreaming() {
    if (this.abortController) {
      this.abortController.abort();
      this.abortController = null;
    }
  }
}

Example Usage:

javascript
// Initialize Client
const chatClient = new BildhiveAIChatClient({
  instance: '634dd0254f594a11fca146b7',
  agentId: '6a0b672ce473781dabff250a'
});

// 1. Register or continue
await chatClient.registerLead({
  fullName: 'Alice Walker',
  email: 'alice@example.com',
  phone: '555-987-6543'
});

// 2. Stream message
await chatClient.sendMessage('What communities have homes under $500k?', {
  onToken: ({ text, uiElements }) => {
    console.log('Streaming text:', text);
    console.log('Cards / UI elements:', uiElements);
  },
  onThinking: (isThinking) => {
    console.log('AI is searching records:', isThinking);
  },
  onFinish: (result) => {
    console.log('Conversation complete:', result);
  }
});

Lead Capture, Security & User Lifecycle

1. Lead Registration Workflow

When a new visitor interacts with the widget, they are greeted with the registration screen before starting the conversation:

  • Full Name (Required)
  • Email Address (Required, validated)
  • Phone Number (Optional)
  • Email Consent Checkbox (Required)
  • SMS Consent Checkbox (Optional if phone is provided)
  • Terms & Privacy Checkbox (Required)

Upon submission, the lead is registered directly into your Bildhive project's contact CRM (/v1/contacts).

2. Returning User Experience

  • When a lead successfully registers or logs in, their email is stored in window.localStorage under the key lead_email.
  • On subsequent visits to your website, the widget automatically recognizes the returning user and provides a streamlined "Welcome back" continuation screen, allowing them to jump straight into conversation or browse past chat history.

3. Built-In Spam & Bot Protection

The registration form contains automated safeguards against spam submissions:

  • Honeypot Trap: An invisible dummy input field (website) is included. Automated bots filling in all available fields are immediately blocked.
  • Human Speed Threshold: Submissions completed in under 6 seconds from form display are rejected with a warning prompt to prevent robotic form blasters.

4. Duplicate Phone Conflict Handling

If a visitor registers with a phone number that is already associated with an existing contact in your database, the widget displays a confirmation dialog asking them to confirm before linking their conversation, preventing accidental data collisions.


Framework Integration Recipes

Plain HTML / Static Sites / CMS (WordPress, Webflow, Squarespace)

Simply add the <script> tag before </body> and place <bildhive-chat-widget> anywhere on the page (e.g., in your global site footer or custom code injection settings).


React / Next.js (App Router & Pages Router)

In React or Next.js, declare the custom element inside a client component and use useEffect or next/script to load the loader script:

tsx
'use client';

import { useEffect, useRef } from 'react';
import Script from 'next/script';

export default function AIChatWidget() {
  const widgetRef = useRef<HTMLElement | null>(null);

  useEffect(() => {
    if (typeof window === 'undefined') return;

    customElements.whenDefined('bildhive-chat-widget').then(() => {
      const widget = widgetRef.current as any;
      if (!widget) return;

      widget.headerLogo = { url: '/brand-logo.svg' };
      widget.aiProfileImage = { url: '/ai-avatar.png' };
      widget.privacyPolicy = { label: 'Privacy Policy', link: '/privacy' };
      widget.termsOfUse = { label: 'Terms of Use', link: '/terms' };
    });
  }, []);

  return (
    <>
      <Script
        src="https://cdn.bildhive.com/scripts/CustomWebsiteWidgets/widgetLoader.js"
        strategy="lazyOnload"
      />
      {/* @ts-ignore custom element JSX tag */}
      <bildhive-chat-widget
        ref={widgetRef}
        instance="YOUR_INSTANCE_TOKEN"
        agent-id="YOUR_AI_AGENT_ID"
        position="right"
        offset="20px"
        header-text="AI Sales Assistant"
        banner-background="#0f766e"
        button-background="#0f766e"
        icon-background="#0f766e"
      />
    </>
  );
}

Vue 3 / Vite

If using Vite and Vue 3, instruct the Vue compiler to treat <bildhive-chat-widget> as a custom Web Component rather than a Vue component:

javascript
// vite.config.js
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';

export default defineConfig({
  plugins: [
    vue({
      template: {
        compilerOptions: {
          // Treat all tags starting with bildhive- as custom elements
          isCustomElement: (tag) => tag.startsWith('bildhive-')
        }
      }
    })
  ]
});

Then in your Vue template:

vue
<template>
  <bildhive-chat-widget
    ref="widgetRef"
    instance="YOUR_INSTANCE_TOKEN"
    agent-id="YOUR_AI_AGENT_ID"
    position="right"
    offset="20px"
  />
</template>

<script setup>
import { ref, onMounted } from 'vue';

const widgetRef = ref(null);

onMounted(async () => {
  // Dynamically load loader script if not present in index.html
  if (!customElements.get('bildhive-chat-widget')) {
    const script = document.createElement('script');
    script.src = 'https://cdn.bildhive.com/scripts/CustomWebsiteWidgets/widgetLoader.js';
    script.async = true;
    document.head.appendChild(script);
  }

  await customElements.whenDefined('bildhive-chat-widget');
  if (widgetRef.value) {
    widgetRef.value.headerLogo = { url: '/logo.svg' };
    widgetRef.value.aiProfileImage = { url: '/ai-avatar.png' };
    widgetRef.value.privacyPolicy = { label: 'Privacy Policy', link: '/privacy' };
    widgetRef.value.termsOfUse = { label: 'Terms of Use', link: '/terms' };
  }
});
</script>

Troubleshooting & FAQ

1. "The API add-on is not enabled for this project"

  • Cause: Your Bildhive project subscription does not currently include the API Add-on.
  • Resolution: The project owner or billing manager should enable the API add-on in the Bildhive Admin dashboard under Settings > Projects > Edit > Add-ons.

2. Content Security Policy (CSP) & CORS Domains

If your website uses strict Content Security Policy headers, make sure the following domains are whitelisted:

DirectiveAllowed DomainPurpose
script-srchttps://cdn.bildhive.com (or .dev)Loading the widgetLoader.js bundle.
connect-srchttps://api.bildhive.comContacts CRM and duplicate phone validation.
connect-srchttps://ai-backend.bildhive.comReal-time AI chat stream & conversation endpoints.
img-srchttps://*.digitaloceanspaces.comDefault avatar and icon assets.

3. Z-Index and Layering

The widget automatically positions its host container at z-index: 999999 and position: fixed. If you have modals or navigation drawers with a higher z-index, you can adjust their CSS stacking context accordingly.

4. Maximized Window Mode

When the user clicks the "Maximize" toggle in the header, the widget expands to 95vw / 90vh centered on screen with backdrop focus, offering an expansive view for detailed product recommendations and image lightbox browsing.