Agentic Workforce ME Developer PortalDocs 1.0 · Widget 0.1.0

Getting started

Installation

Script tag, ESM import, React, Next.js, Vue, Angular and WebView notes.

There are three ways to get the widget onto a page. They all create the same <hive-chat> element and talk to the same API — pick whichever fits your build:

MethodBest forapiUrl
Script tag (/embed/v1/hive-embed.js)CMSs, marketing sites, tag managers, anything without a bundler.Inferred from the script’s origin.
ESM / npm (@hive/embed)Bundled apps (Vite, webpack, esbuild) in any framework.Pass apiUrl (or call setDefaultApiUrl).
React wrapper (@hive/embed/react)React 18/19 and Next.js apps.Pass apiUrl as a prop.

Side by side

The same widget in every stack. Replace the key and origin with the values from your console.

HTML
<script async
  src="https://console.agenticworkforce.me/embed/v1/hive-embed.js"
  data-key="hive_pk_REPLACE_WITH_YOUR_PUBLISHABLE_KEY"></script>

Script tag

The loader is an IIFE. With data-key present it initializes on DOMContentLoaded (or immediately if the body already exists). Add async: the loader finds its own tag by URL even when document.currentScript is null.

Manual init and the pre-load queue

Use data-manual (or omit data-key) and call window.HiveChat.init(options) yourself. If your code may run before the loader has arrived, push into a queue — the loader drains it on boot:

HTML
<script async src="https://console.agenticworkforce.me/embed/v1/hive-embed.js" data-manual></script>
<script>
  // Queue pattern: safe to call before the loader has finished downloading.
  window.HiveChat = window.HiveChat || { q: [] };
  window.HiveChat.q.push([{
    key: 'hive_pk_REPLACE_WITH_YOUR_PUBLISHABLE_KEY',
    locale: 'auto',
    welcome: 'Hi! Ask me anything about our products.',
  }]);
</script>

Inline mode in a container

data-mode="inline" renders the panel into the element matched by data-container (a CSS selector) instead of a floating launcher. The panel fills its container — give the container a height.

HTML
<div id="support" style="height: 600px"></div>

<script async src="https://console.agenticworkforce.me/embed/v1/hive-embed.js"
  data-key="hive_pk_REPLACE_WITH_YOUR_PUBLISHABLE_KEY"
  data-mode="inline"
  data-container="#support"></script>

All data-* attributes are listed in the configuration reference.

ESM import of the Web Component

import '@hive/embed' registers the hive-chat element (idempotent). You can then either create elements in markup or call HiveChat.init. Elements need to know the API origin: pass api-url per element or set a default once.

chat.tsTypeScript
import '@hive/embed'; // side-effect import registers <hive-chat>
import { setDefaultApiUrl } from '@hive/embed';

// Optional: set once so <hive-chat> elements without api-url know the origin.
setDefaultApiUrl('https://console.agenticworkforce.me');

document.body.insertAdjacentHTML(
  'beforeend',
  '<hive-chat key="hive_pk_REPLACE_WITH_YOUR_PUBLISHABLE_KEY"></hive-chat>',
);

Without a bundler you can import the hosted ESM build directly from the platform origin:

HTML
<script type="module">
  import 'https://console.agenticworkforce.me/embed/v1/hive-embed.mjs'; // registers <hive-chat>
</script>

<hive-chat
  key="hive_pk_REPLACE_WITH_YOUR_PUBLISHABLE_KEY"
  api-url="https://console.agenticworkforce.me"
  locale="ar"
  mode="inline"
  theme='{"primary":"#0f766e","radius":14}'
  style="display:block;height:600px"></hive-chat>

React

@hive/embed/react exports a HiveChat component. React is a peer dependency; the wrapper pushes props through element.configure() so objects survive intact, and maps callbacks to the host events. The key prop is named embedKey because key is reserved by React.

support.tsxTSX
import { HiveChat } from '@hive/embed/react';

export function SupportChat() {
  return (
    <HiveChat
      embedKey="hive_pk_REPLACE_WITH_YOUR_PUBLISHABLE_KEY"
      apiUrl="https://console.agenticworkforce.me"
      onMessage={(m) => console.log(m.role, m.text)}
    />
  );
}
PropSignatureDescription
embedKeyembedKey: stringThe publishable key (named embedKey because key is reserved by React).
classNameclassName?: stringClass applied to the <hive-chat> host element.
stylestyle?: CSSPropertiesInline style on the host (size it in inline mode).
onReadyonReady?: (info: { key_id }) => voidMaps to hive:ready — config loaded, widget usable, no session yet.
onSessiononSession?: (info: { visitor_id, identified }) => voidMaps to hive:session — the session was minted (first open / first send).
onOpenonOpen?: () => voidMaps to hive:open.
onCloseonClose?: () => voidMaps to hive:close.
onMessageonMessage?: (m: { role: 'user' | 'agent', text }) => voidMaps to hive:message.
onErroronError?: (e: { code }) => voidMaps to hive:error.

Plus every HiveChat.init option except key (apiUrl, locale, theme, title, subtitle, welcome, placeholder, user, mode, open, storage, visitorId) — see Configuration.

The wrapper does not forward a ref. To call methods (open(), send()) query the element or keep a handle from HiveChat.init:

support.tsxTSX
import { HiveChat } from '@hive/embed/react';
import type { HiveChatElement } from '@hive/embed';

function openChat() {
  document.querySelector<HiveChatElement>('hive-chat')?.open();
}

export function Support() {
  return (
    <>
      <button onClick={openChat}>Need help?</button>
      <HiveChat embedKey="hive_pk_REPLACE_WITH_YOUR_PUBLISHABLE_KEY" apiUrl="https://console.agenticworkforce.me" />
    </>
  );
}

Next.js

@hive/embed registers the <hive-chat> custom element (a class extending HTMLElement) the moment it is imported, so the module must never be evaluated on the server — and in the App Router client components are still server-rendered. Two options, in order of preference:

  1. Script tag via next/script in the root layout — zero npm dependencies, nothing runs on the server, auto-init from data-*. This is what the Next.js example does (with data-manual and a client component calling window.HiveChat.init from useEffect):
    TSX
    // app/layout.tsx — script-tag variant (no npm dependency)
    import Script from 'next/script';
    
    export default function RootLayout({ children }: { children: React.ReactNode }) {
      return (
        <html lang="en">
          <body>
            {children}
            <Script
              src="https://console.agenticworkforce.me/embed/v1/hive-embed.js"
              data-key="hive_pk_REPLACE_WITH_YOUR_PUBLISHABLE_KEY"
              strategy="afterInteractive"
            />
          </body>
        </html>
      );
    }
  2. React wrapper, imported only in the browser. Put next/dynamic with { ssr: false } inside a 'use client' file (Next.js 15 rejects ssr: false in Server Components such as app/layout.tsx), and render that file once from the layout. A plain import { HiveChat } from '@hive/embed/react' at the top of a client component crashes the server render with HTMLElement is not defined.
    TSX
    // app/components/support-chat.tsx
    'use client';
    
    import dynamic from 'next/dynamic';
    
    // @hive/embed registers <hive-chat> (extends HTMLElement) at import time, so it
    // must never be evaluated on the server. Client components are still
    // server-rendered: import the wrapper only in the browser with ssr: false.
    // (next/dynamic with ssr: false is only allowed inside a client component.)
    const HiveChat = dynamic(
      () => import('@hive/embed/react').then((m) => m.HiveChat),
      { ssr: false },
    );
    
    export function SupportChat() {
      return (
        <HiveChat
          embedKey="hive_pk_REPLACE_WITH_YOUR_PUBLISHABLE_KEY"
          apiUrl="https://console.agenticworkforce.me"
        />
      );
    }
    
    // app/layout.tsx (Server Component) — render it once so it survives navigation:
    //   import { SupportChat } from './components/support-chat';
    //   … <body>{children}<SupportChat /></body>

If your app sends a Content-Security-Policy header, allow the platform origin:

TypeScript
// next.config.ts — if you send a CSP, allow the platform origin
const csp = [
  "default-src 'self'",
  "script-src 'self' https://console.agenticworkforce.me",
  "connect-src 'self' https://console.agenticworkforce.me",
].join('; ');

export default {
  async headers() {
    return [{ source: '/(.*)', headers: [{ key: 'Content-Security-Policy', value: csp }] }];
  },
};

Vue

Either call HiveChat.init from onMounted (see the tabs above) or use the element in a template. In the second case tell the compiler that hive-chat is a custom element so it does not warn about an unknown component.

Vue
<!-- App.vue: use the element directly -->
<script setup lang="ts">
import '@hive/embed';
</script>

<template>
  <hive-chat
    key="hive_pk_REPLACE_WITH_YOUR_PUBLISHABLE_KEY"
    api-url="https://console.agenticworkforce.me"
    mode="inline"
    style="display:block;height:600px"
  />
</template>

<!-- vite.config.ts: tell Vue that hive-chat is a custom element -->
<!--
  vue({ template: { compilerOptions: { isCustomElement: (tag) => tag === 'hive-chat' } } })
-->

Angular

Import @hive/embed once (for example in main.ts) and add CUSTOM_ELEMENTS_SCHEMA to any component that uses the tag. Or drive it from TypeScript with HiveChat.init (tabs above) — no schema needed then.

TypeScript
// main.ts (or the component file)
import '@hive/embed';

// support.component.ts
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';

@Component({
  selector: 'app-support',
  standalone: true,
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
  template: `
    <hive-chat
      key="hive_pk_REPLACE_WITH_YOUR_PUBLISHABLE_KEY"
      api-url="https://console.agenticworkforce.me"
      locale="auto"
      style="display:block;height:600px"
      mode="inline"></hive-chat>
  `,
})
export class SupportComponent {}

Native apps and WebViews

The widget is plain web: host a tiny HTML page (below) on one of the key’s allowed origins and load it in a WKWebView / Android WebView / Flutter webview_flutter. Notes:

  • The WebView must send an Origin header, i.e. load the page over https:// from a real host. Pages loaded from file:// or about:blank have an opaque origin and are rejected unless the key’s allowlist contains the wildcard *.
  • Use mode="inline" so the panel fills the WebView; there is no floating launcher to tap in a native shell.
  • Enable JavaScript and DOM storage (Android: settings.domStorageEnabled = true) so the visitor id and thread persist. With storage disabled use storage: "none" and pass a stable visitorId you keep on the device (via HiveChat.init or the visitor-id attribute — the loader has no data-visitor-id).
  • Identified users: mint the identity JWT on your backend and inject it via data-user-token or HiveChat.init({ user: { token } }).
  • SSE streaming works in all current WebViews; nothing to configure.
webview.htmlHTML
<!doctype html>
<html lang="en">
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
  <style>html,body{margin:0;height:100%}</style>
</head>
<body>
  <script async src="https://console.agenticworkforce.me/embed/v1/hive-embed.js"
    data-key="hive_pk_REPLACE_WITH_YOUR_PUBLISHABLE_KEY"
    data-mode="inline"
    data-storage="local"></script>
</body>
</html>