Comni
Product
Product
Support inboxTeam tickets & routingCommunity chatLet users help each otherTeam chatAnswer conversations togetherUsersAuto-collected profilesAnnouncementsShare product updatesKnowledge baseAI-ready articlesBrandingCustom colors & domainAnalyticsTrack queries in real timeDeveloperWidget embed & API keysCustom domainHost on your own domainAPI keysJWT auth & tokensPluginsExtend your workspace
View all features
PricingDocsBlog
Sign InGet Started
ComniComni

Community, support, announcements and knowledge in one fast, embeddable widget.

FeaturesPricingDocsBlog

© 2026 Comni. All rights reserved.

PrivacyTerms

Introduction

QuickstartHow it worksSupported features

Installation

Embed scriptLoader optionsJavaScript APIKeys

API

API keyAuthenticationEndpoints

Guides

CustomizationConfigurationRate limits

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.

  1. widget-loader.js reads data-project from the script tag.
  2. It loads widget-app.js from the same directory and calls ComniWidget.init().
  3. 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.

AttributeRequiredDescription
data-projectYesYour project ID (starts with org_). Identifies which organization's help center to load.
data-tokenNoA signed JWT identifying the visitor. Sent as an Authorization: Bearer header on all widget API calls.
data-base-urlNoOverride where widget-app.js loads from and the API origin. Defaults to the site the loader is served from.
data-company-nameNoCompany name shown in the widget header. Overrides the value set in the dashboard.
data-logoNoURL 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).

CommandDescription
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

MethodPathAuthDescription
GET/api/widget/membersx-project-id headerList members of the widget's organization (id, name, avatar, handle, role).
GET/api/widget/[widget_id]/designDashboard sessionGet the widget's design configuration (colors, radius, mode, position).
PUT/api/widget/[widget_id]/designDashboard sessionUpdate 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.