Documentation
Everything you need to embed Comni into your product.
Quickstart
Add one script tag to your page. Paste it right before the closing </body> tag and the widget loads automatically — no extra code needed.
<script src="http://localhost:3000/widget-loader.js" data-project="org_xxxxxxxxx" defer></script>Replace org_xxxxxxxxx with your org ID, available in the dashboard under Dashboard → Developer. Use http://localhost:3000 while developing; in production swap it for your domain, e.g. https://app.comni.work.
How it works
The loader script downloads a small app bundle and renders the widget inside a Shadow DOM, so your page's styles never leak in and the widget's styles never leak out. Messages, community chat, issues, polls, and announcements sync in real time between the widget and your dashboard.
widget-loader.jsreadsdata-projectfrom the script tag.- It loads
widget-app.jsfrom the same directory and callsComniWidget.init(). - The widget renders in a floating button at the bottom of your page and talks to Comni through the
/api/widget/*endpoints.
Supported features
- Live chat & DMs
- Community chat
- Support issues
- Announcements & news
- Polls & voting
- Knowledge base
- Realtime sync
- Guest & authenticated users
Embed script
Copy your org ID from the dashboard and paste the script tag into your HTML, right before the closing </body> tag.
<script src="http://localhost:3000/widget-loader.js" data-project="org_xxxxxxxxx" defer></script>That's it — the widget appears as a floating button in the corner of your page. You can find this snippet ready to copy on the Developer page in your dashboard. Swap http://localhost:3000 for your production domain, e.g. https://app.comni.work.
Loader options
Configure the widget entirely with attributes on the loader script tag — no JavaScript required.
| Attribute | Required | Description |
|---|---|---|
data-project | Yes | Your project ID (starts with org_). Identifies which organization's help center to load. |
data-token | No | A signed JWT identifying the visitor. Sent as an Authorization: Bearer header on all widget API calls. |
data-base-url | No | Override where widget-app.js loads from and the API origin. Defaults to the site the loader is served from. |
data-company-name | No | Company name shown in the widget header. Overrides the value set in the dashboard. |
data-logo | No | URL of a logo to show in the widget header. Overrides the value set in the dashboard. |
<script
src="http://localhost:3000/widget-loader.js"
data-project="org_xxxxxxxxx"
data-token="<optional signed JWT>"
data-company-name="Acme Inc"
data-logo="https://acme.com/logo.png"
defer
></script>JavaScript API
Once the widget loads, a global ComniWidget function is available. Calls made before the script loads are queued and replayed, so you can control the widget from your own code (React, event handlers, product tours).
| Command | Description |
|---|---|
ComniWidget("show") | Open the messenger. |
ComniWidget("hide") | Close the messenger. |
ComniWidget("update", {...}) | Update the visitor token, theme, or branding without a page reload. |
ComniWidget("shutdown") | Unmount the widget and reset. Re-boot by calling boot or reloading the loader. |
You can also configure the widget with a global settings object instead of data attributes — useful when the loader script is injected dynamically:
<script>
window.comniSettings = {
projectId: "org_xxxxxxxxx",
token: "<optional signed JWT>",
baseUrl: "http://localhost:3000",
companyName: "Acme Inc"
};
</script>
<script src="http://localhost:3000/widget-loader.js" defer></script>In production, point baseUrl and the loader at your domain, e.g. https://app.comni.work.
Keys
Every organization has two credentials, available on the Dashboard → Developer tab:
Org ID
Your organization identifier. Used as the projectId in your embed and the iss claim in your JWT.
API key
Starts with ct_sk_live_. Signs the visitor JWT (HS256) on your server. Treat it like a password.
Keep your API key secret — it is meant for server-side use only. Never put it in client-side code or commit it to a public repository.
API key
The API key signs the visitor JWT on your server. Sign a short-lived HS256 token with your key and pass it to the loader via window.comniSettings.token (or the data-token attribute). The widget sends it as an Authorization: Bearer <token> header on widget API calls.
// Sign the token on your server (HS256)
const token = jwt.sign(
{
sub: user.id, // your user id
email: user.email, // optional
name: user.name, // optional
iss: "org_xxxxxxxxx", // your Comni org id
iat: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + 60 * 60
},
process.env.COMNI_API_SECRET, // your API key (server-side only)
{ algorithm: "HS256" }
);
// Pass it to the widget
window.comniSettings = {
projectId: "org_xxxxxxxxx",
token: token
};Generate the JWT on your server so your signing key is never exposed in the browser.
Authentication
By default, visitors are treated as guests. To identify your users, sign a JWT on your backend and pass it to the loader with data-token. The widget sends it as an Authorization: Bearer <token> header on widget API calls.
GET /api/widget/members
x-project-id: org_xxxxxxxxx
Authorization: Bearer <signed-jwt>Generate the JWT on your server so your signing secret is never exposed in the browser.
Endpoints
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /api/widget/members | x-project-id header | List members of the widget's organization (id, name, avatar, handle, role). |
| GET | /api/widget/[widget_id]/design | Dashboard session | Get the widget's design configuration (colors, radius, mode, position). |
| PUT | /api/widget/[widget_id]/design | Dashboard session | Update the widget's design configuration. |
Customization
Upload your logo, set brand colors, choose light or dark mode, and position the widget anywhere on screen — all from the Widget → Customize panel, without writing code. Changes save instantly.
- Logo & banner image
- Primary color
- Border radius
- Light / dark mode
- Bottom-left / bottom-right
- Hero title, subtitle & alignment
Configuration
Widget behavior — which features appear and how messages are moderated — is controlled from the Widget → Configure and Widget → Settings panels.
These settings are stored with your widget and applied on every page where the widget is embedded.
Rate limits
All API endpoints are rate-limited per widget to keep the platform healthy. Limits are generous for normal usage; if you need higher limits, contact support.