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.