Widget SDK developer guide
This guide is for developers who control the Helpin chat widget from their own code. It covers the client lifecycle, signed-in users, logout, widget events, and the script-tag command API. To install the widget and see the basic options, start with Embed the widget.
Prerequisites
A widget key from Settings → Chat widget.
Your site's origin added to the widget's allowed origins. See Embedding reference.
The SDK loaded with a script tag (
lib.js) or installed withnpm install @helpin-ai/sdk-js.
Two ways to call the SDK
The npm package returns a typed client object. The script tag exposes the same methods as commands on a global function, named helpin by default (change it with namespace).
import { helpinClient } from '@helpin-ai/sdk-js';
const client = helpinClient({
widgetKey: 'your-widget-key',
host: 'https://client.helpin.ai',
autoBoot: false,
});
client?.open();
<script>
helpin('onLoad', function () {
helpin('open');
});
</script>
helpinClient(...) returns null if it can't initialize, so check the result before you use it. With the script tag, helpin(...) calls made before the SDK finishes loading are queued and run in order. Use helpin('onLoad', callback) when your code must wait for the SDK.
Framework packages
For Vue, React, and Next.js apps, Helpin publishes thin wrappers around @helpin-ai/sdk-js. Each package exports createClient(options), which accepts the same options as helpinClient(...) and returns the same client. The package then shares that client with your components and gives you a useHelpin() composable or hook. Everything else in this guide, such as boot(), id(), show() / hide(), and the event listeners, works the same way.
Package | Share the client | Use it in components |
|---|---|---|
|
|
|
|
|
|
|
|
|
Vue
npm install @helpin-ai/vue @helpin-ai/sdk-js
// main.ts
import { createApp } from 'vue';
import { createClient, HelpinPlugin } from '@helpin-ai/vue';
import App from './App.vue';
const client = createClient({
widgetKey: 'your-widget-key',
host: 'https://client.helpin.ai',
});
createApp(App).use(HelpinPlugin, { client }).mount('#app');
<script setup lang="ts">
import { useHelpin } from '@helpin-ai/vue';
const helpin = useHelpin();
</script>
<template>
<button @click="helpin.open()">Chat with us</button>
</template>
createClient() returns null during server rendering. In Nuxt 3, create the client and install HelpinPlugin in a client-only plugin, such as plugins/helpin.client.ts.
React
npm install @helpin-ai/react @helpin-ai/sdk-js
import { createClient, HelpinProvider, useHelpin } from '@helpin-ai/react';
const client = createClient({
widgetKey: 'your-widget-key',
host: 'https://client.helpin.ai',
});
function HelpButton() {
const { open } = useHelpin();
return <button onClick={open}>Chat with us</button>;
}
export function Root() {
return (
<HelpinProvider client={client}>
<HelpButton />
</HelpinProvider>
);
}
Next.js
npm install @helpin-ai/nextjs @helpin-ai/sdk-js
'use client';
import { useMemo } from 'react';
import { createClient, HelpinProvider } from '@helpin-ai/nextjs';
export function Providers({ children }: { children: React.ReactNode }) {
const client = useMemo(
() =>
createClient({
widgetKey: process.env.NEXT_PUBLIC_HELPIN_WIDGET_KEY!,
host: process.env.NEXT_PUBLIC_HELPIN_HOST!,
}),
[],
);
return <HelpinProvider client={client}>{children}</HelpinProvider>;
}
Wrap your root layout in Providers, then call useHelpin() from any Client Component. createClient() returns null on the server, so always call it from a Client Component.
What useHelpin() returns
In all three packages, useHelpin() returns id, track, lead, trackPageView, set, unset, rawTrack, show, hide, open, close, toggle, openMessages, openNewMessage, openConversation, openArticle, and shutdown. trackPageView() is the same as pageview(). If no client is available, for example during server rendering, the methods do nothing.
For boot(), reset(), group(), the event listeners, and the getters, call the client that createClient(...) returned:
client?.onUnreadCountChange((count) => {
// update your own badge
});
Each package also exports usePageView() for tracking route changes yourself. If you use it, set autoPageview: false on the client so each navigation isn't counted twice. In Vue, pass your router: usePageView({ router: useRouter() }). In Next.js, pass the client: usePageView(client).
Control when the widget starts
By default, the widget boots as soon as widgetKey and host are set. To show it only after your own logic runs, for example behind a custom Help button, set autoBoot: false (or data-auto-boot="false"). The widget then stays dormant until you call boot(), show(), open(), openMessages(), or openNewMessage().
Method | What it does |
|---|---|
| Boots or re-boots the widget. |
| Shows the launcher without opening the panel; hides the launcher and closes the panel. |
| Opens, closes, or toggles the chat panel. |
| Opens the conversation list. |
| Starts a new conversation, optionally pre-filled with |
| Opens a specific conversation. |
| Opens a Help Center article inside the widget. |
| Ends the widget session and removes the widget from the page. |
For openArticle, pass the last segment of the article's URL, not the full URL.
Identify signed-in users
Call id(...) after a user signs in. The SDK accepts id, email, first_name / firstName, last_name / lastName, phone, job_title / jobTitle, and a company object with id, name, and created_at.
const identity = await fetch('/api/helpin/identity', { cache: 'no-store' })
.then((response) => response.json());
await client.id(identity);
In this example, /api/helpin/identity is an endpoint in your application. It reads the signed-in user from your own session and returns their identity with an identity_verification proof. The proof has this shape:
{
"version": "v1",
"issued_at": 1767225600,
"expires_at": 1767225900,
"signature": "hex-encoded HMAC-SHA256"
}
Your backend builds the proof by joining these values with newline characters, in this order:
helpin-widget-identity:v1Your widget key
The email, trimmed and lowercased
The user ID you send as
idThe company ID, trimmed, or an empty string
issued_at, in Unix secondsexpires_at, in Unix seconds
Sign that text with HMAC-SHA256, using your installation's signing secret as the key (the secret string itself, not hex-decoded), and hex-encode the result. The proof can be valid for at most 15 minutes, and issued_at can't be more than one minute ahead of Helpin's clock. Send exactly the values you signed.
import hashlib, hmac, time
def sign_identity(secret, widget_key, email, user_id="", company_id=""):
issued_at = int(time.time())
expires_at = issued_at + 300
message = "\n".join([
"helpin-widget-identity:v1", widget_key, email.strip().lower(),
user_id, company_id.strip(), str(issued_at), str(expires_at),
])
signature = hmac.new(secret.encode(), message.encode(), hashlib.sha256).hexdigest()
return {"version": "v1", "issued_at": issued_at,
"expires_at": expires_at, "signature": signature}
Keep the signing secret on your server. It's a different value from the public widget key. Only sign identities taken from your own authenticated session, never values sent by the browser.
Visitor identity verification in Settings → Chat widget controls how Helpin treats identities:
Accept unverified identities treats unsigned identities as unverified visitor claims. A valid proof still marks the identity as verified.
Require server-signed identities rejects identity requests with a missing, expired, or mismatched proof. Anonymous chat still works.
Get the signing secret
You need a workspace admin or owner role.
In Helpin, open Settings → Chat widget and find Identity signing secret.
Use the Reveal (eye) or Copy icon next to the masked secret, and store the secret in your server's secret configuration. Never put it in browser code.
To replace it, select Regenerate secret and confirm. The old secret stops working immediately, so update your server right away. Regenerating the widget key also replaces the signing secret.
Until your server uses the new secret, identities signed with the old one are rejected when Require server-signed identities is on, and treated as unverified otherwise.
Log out
When a user signs out of your app, end their widget session and clear the stored identity:
client.shutdown();
await client.reset(true); // true also replaces the anonymous visitor ID
Listen to widget events
Method | Called when | Callback argument |
|---|---|---|
| The visitor opens the panel from the widget | none |
| The visitor closes the panel from the widget | none |
| The total unread count changes | the new count |
| The visitor enters their email | the email |
| A new conversation is created | the conversation ID |
| A message arrives | the message object |
onOpen and onClose fire only for the visitor's own clicks, not when your code calls open() or close().
client.onUnreadCountChange((count) => {
document.querySelector('#help-badge').textContent = count ? String(count) : '';
});
Track events
Method | Use it to |
|---|---|
| Record a custom event. |
| Record a lead. |
| Link the current user to a company. |
| Record a pageview manually. |
| Record a help article view. |
| Add or remove properties sent with events. |
Helpin records pageviews automatically, including navigation in single-page apps, unless you set autoPageview: false.
Read state
getVisitorId() returns the anonymous visitor ID, isWidgetReady() reports whether the widget has loaded, and getConfig() returns the merged configuration.
Was this article helpful?