@vocantly/widget

中文文档
0.1.26发布于 2026/11/03UNLICENSEDTypeScriptESM + CJS

Drop-in embeddable support chat widget for Vocantly, built on @vocantly/sdk.

原始使用文档
文档目录9 节
  1. The security model (important)
  2. Install
  3. Your backend (one call to provision)
  4. No backend? Anonymous embed with just your public key
  5. API
  6. Pre-chat contact capture
  7. Build
  8. Demo
  9. Known limitation

Vocantly Widget

A drop-in embeddable support chat widget built on the headless @vocantly/sdk. One <script> renders a floating chat bubble + panel; your visitors chat with the AI bot (and human agents) with zero UI work on your side.

dist/vocantly-widget.js is a single self-contained file (~63 KB) — the SDK, its deps, and the UI are all bundled in. No import maps, no build step for the host.

The security model (important)

The widget runs in the browser, so it only ever holds an SDK token (scoped to one app + one visitor, short-lived). Your API key + secret never touch the browser. The flow:

Your backend (holds API key)                     Browser (this widget)
  POST /apps/:id/support-sessions   ── token + conversation_id ──▶  VocantlyWidget.init({...})
  (X-Api-Key + X-Api-Secret)

Your server calls the provisioning endpoint, then hands only { token, conversation_id } to the page.

Install

Option A — script tag (any website, via CDN):

<!-- unpkg / jsDelivr serve the bundled IIFE; global: VocantlyWidget -->
<script src="https://unpkg.com/@vocantly/widget"></script>
<!-- pin a version:  https://unpkg.com/@vocantly/[email protected] -->

Option B — npm (React / Vue / Next / bundlers):

npm install @vocantly/widget
import { init } from '@vocantly/widget';
init({ wsUrl, token, conversationId, /* … */ });

Both ship the SDK + deps bundled in — zero peer dependencies. Usage is the same either way:

<script src="https://unpkg.com/@vocantly/widget"></script>
<script>
  VocantlyWidget.init({
    wsUrl: 'wss://your-realtime/ws',
    token: 'SDK_TOKEN_FROM_YOUR_BACKEND',
    conversationId: 'CONVERSATION_ID_FROM_YOUR_BACKEND',
    apiUrl: 'https://your-backend/api/v1',   // optional — enables message history
    title: 'Support',
    subtitle: 'We reply in a few minutes',
    primaryColor: '#195EFF',
    position: 'bottom-right',                // or 'bottom-left'
    greeting: 'Hi! 👋 How can we help?',
    autoOpen: false,

    // Optional pre-chat contact capture (needs apiUrl):
    collectContact: true,                    // ask name + email before chatting
    requireEmail: false,                     // if true, email is mandatory to start
    contactPrompt: 'Before we start — how can we reach you?',
  });
</script>

Your backend (one call to provision)

// server-side — keep the secret here
const r = await fetch(`${BASE}/apps/${APP_ID}/support-sessions`, {
  method: 'POST',
  headers: { 'X-Api-Key': PUBLIC_KEY, 'X-Api-Secret': SECRET_KEY, 'Content-Type': 'application/json' },
  body: JSON.stringify({ external_user_id: yourUserId, name, email }),
});
const { token, conversation_id } = (await r.json()).data;   // hand these to the browser

No backend? Anonymous embed with just your public key

For a static landing page with unauthenticated visitors and no server, use publicKey instead of token/conversationId. The widget self-provisions an anonymous session — no secret in the browser:

<script src="https://unpkg.com/@vocantly/widget"></script>
<script>
  VocantlyWidget.init({
    publicKey: 'pk_…',                         // your app PUBLIC key (safe to expose)
    wsUrl:  'wss://ws.vocantly.com/ws',
    apiUrl: 'https://api.vocantly.com/api/v1',  // required in this mode
    collectContact: true,
  });
</script>

One prerequisite — allow your site's origin. Anonymous embedding is off by default. Add your domain(s) to the app's allowed_origins (dashboard, or via the API):

PATCH /apps/:id   { "allowed_origins": ["https://acme.com", "https://www.acme.com"] }

The widget generates a stable visitor id (localStorage) and caches {token, conversationId} so a reload resumes the same conversation. Requests are origin-checked server-side and rate-limited.

Provide either publicKey (anonymous) or token + conversationId (backend-provisioned).

API

CallEffect
VocantlyWidget.init(opts)Mount + connect. Returns { open, close, send, destroy }.
VocantlyWidget.open() / .close()Toggle the panel.
VocantlyWidget.destroy()Disconnect + remove from the DOM.

Pre-chat contact capture

With collectContact: true, the widget asks the visitor for their name + (optional) email before chatting and saves it to their customer profile via PATCH /sdk/customers/me (authenticated by the SDK token — no secrets in the browser). Agents then see the visitor's contact details in the inbox. Requires apiUrl to be set. Use requireEmail: true to make email mandatory.

Build

npm run build       # → dist/vocantly-widget.js (minified IIFE, global VocantlyWidget)
npm run build:dev   # non-minified + sourcemap

The build bundles ../vocantly-js-sdk/dist/index.js. If you rebuild the SDK, rebuild the widget.

Demo

npm run serve:demo   # serves this folder on :5173
# open http://localhost:5173/   (redirects to the demo)

Avoid ports Chrome blocks as "unsafe" (e.g. 5060 = SIP → ERR_UNSAFE_PORT). 5173 is safe. The demo is a mock business page. Enter a workspace owner's email/password + a Development App ID and click Boot — it provisions via /apps/:id/support-sessions (dev login stands in for your backend) and mounts the widget. Requires backend :3001, Go realtime :8080, and the app to have the bot enabled + an active FAQ.

Known limitation

Bot replies are currently delivered live but not persisted server-side, so they won't appear in message history on reload (customer/agent messages do). Persisting bot messages is a backend follow-up.

版本与发布记录

@vocantly/widget 共发布 17 个版本,首次发布于 2026/09/26;过去 12 个月发布了 17 个版本。

版本发布日期距今
0.1.26latest2026/11/03今天
0.1.252026/11/021 天前
0.1.242026/10/313 天前
0.1.232026/10/277 天前
0.1.222026/10/268 天前
0.1.212026/10/2410 天前

关于 @vocantly/widget 的常见问题

@vocantly/widget 是什么?

插入式可嵌入支持聊天小部件为voctly,建立在@ voctly /sdk。

如何安装 @vocantly/widget?

在项目目录运行 npm install @vocantly/widget,或使用 pnpm add / yarn add / bun add。

@vocantly/widget 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@vocantly/widget 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@vocantly/widget 可以商用吗?

@vocantly/widget 使用 UNLICENSED 许可。作者声明为 UNLICENSED,即不授予公开使用许可(与公共领域的 Unlicense 不同)。使用前需获得作者授权。以上为概括,不构成法律意见,请以许可原文为准。

@vocantly/widget 还在维护吗?

最新版本 0.1.26 发布于 2026/11/03(今天);至今共发布 17 个版本,其中过去 12 个月发布了 17 个;首次发布于 2026/09/26。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。