Content Modules

Building a help center with search and AI chat

Updated 100 views

The help center module covers categories, articles and semantic search — plus a chat widget that answers from your published articles. The widget works in React, and on any website with one script tag.

Categories

TypeScript
const categories = await kookee.help.categories();

categories.forEach((category) => {
  category.slug;
  category.name;
  category.description; // string | null
  category.icon;        // string | null
  category.articleCount;
});

articleCount lets you hide empty categories from navigation rather than showing a dead end.

Listing articles

TypeScript
const response = await kookee.help.list({
  page: 1,
  limit: 10,
  category: 'getting-started',
  locale: 'en',
});

The category parameter takes a category slug, not an ID.

Search matches on meaning, not keywords — a user searching "stop charging me" can find an article titled Managing your subscription.

TypeScript
const results = await kookee.help.search({ query: 'billing', limit: 5 });

results.forEach((result) => {
  result.title;
  result.slug;
  result.category;     // { name, slug }
  result.matchedChunk; // the passage that matched, when available
  result.score;        // how well it matched: 0.4–1, or null
});

matchedChunk is worth rendering. Showing the matching passage under each result usually answers the question without the user opening the article at all.

score is the cosine similarity of that passage. Anything below 0.4 is never returned, so the number is always between 0.4 and 1 — useful for deciding whether to show a "no good match" state instead of a weak result. It is null when the search fell back to plain text matching, whose hits are ordered by date rather than relevance. See Read your help center from your own AI assistant if you are feeding these results to a model of your own.

Note that search() returns an array directly, not a paginated envelope.

A single article

TypeScript
const article = await kookee.help.getBySlug('how-to-reset-password');

article.contentHtml;  // full body, single fetch only
article.views;        // view count, tracked automatically
article.metaTitle;    // string | null
article.metaDescription;

Pass markdown: true and the response also carries contentMarkdown — the same body as Markdown, which is cheaper to put in a prompt than HTML.

Help articles support reactions as well as views. Record one with react():

TypeScript
const { reactions } = await kookee.help.react(article.id, {
  reactionType: 'rocket',
  action: 'add',
});

reactions; // { rocket: 42 }

It takes the article id, not the slug, and returns the updated count for every reaction type. action: 'remove' undoes one.

Views are the passive signal and reactions the deliberate one. An article with heavy traffic that still produces the same support question is the one to rewrite.

Ready-made React components

In a React app, @kookee/react/help renders the category grid, category navigation, article lists, breadcrumbs and the article page on the server. @kookee/react/help/client adds HelpSearchBox, which runs the semantic search above as the visitor types, and HelpfulVote, a "Was this article helpful?" vote stored as the article's yes and no reactions. See Build a help center with the React components.

The AI chat widget

The widget draws on three things:

  1. Your published help center articles. Drafts never reach it, and every answer shows the articles it was built on — see How the assistant picks its sources. Its search returns the passages that match from anywhere in an article, so a long manual is answered from its page 40 as well as its first page.

  2. The page it is running on — title, meta description, schema.org data and a cleaned excerpt. On by default; see Install the site widget on your website.

  3. Tools you register, if you want it to reach live data or act on your site — see Let the assistant use your own data with tools.

The chat, Ask AI, is one of four tabs. The same widget can also carry a Home tab with your news and links, a Help tab with search and your articles, and a Feedback tab where visitors send you ideas and bug reports, switched on in the dashboard — see Add Home and Help tabs to your widget.

Each reply that links at least one of your articles counts as one AI answer against your plan's monthly allowance, shared by all your projects: 50 on Free, 450 on Pro and 1,650 on Ultra. Greetings, replies that found nothing and errors are free. When the allowance is used up, the chat stops answering until the 1st of the month; see Rate limits, plans and error handling.

On any website, with one script tag

No React and no build step required:

HTML
<script async src="https://kookee.dev/chat/latest.js" data-api-key="YOUR_API_KEY"></script>

A ready-made snippet with your project's key is on the Installation tab of the project's Site widget page in the dashboard.

If the project's Allowed Origins (project settings) list any domains, your website's must be among them, or chat requests are rejected; an empty list allows every origin. See Install the site widget on your website for the full walkthrough.

In React

Bash
npm install @kookee/react @kookee/sdk
tsx
import { KookeeChatWidget } from '@kookee/react/chat';
import '@kookee/react/chat.css';

function App() {
  return (
    <KookeeChatWidget
      clientOptions={{ apiKey: 'your-api-key' }}
      position="bottom-right"
      theme="auto"
      primaryColor="#6366f1"
      title="Help"
      greeting="Hi! How can I help you?"
      placeholder="Ask a question..."
      suggestions={['How do I get started?', 'What are the pricing plans?']}
      onSourceClick={(source) => window.open(`/help/${source.slug}`, '_blank')}
    />
  );
}

Do not forget the stylesheet import — without it the widget renders unstyled. Leave onSourceClick out and a clicked source opens inside the widget instead, in a reader; import @kookee/react/content.css and @kookee/sdk/styles/content.css as well, which style the article there — see Open help articles inside the widget. The widget runs in the browser with your normal API key; that key is designed to be public.

Props

  • clientOptions ({ apiKey }, required) — SDK authentication

  • position — 'bottom-right' (default) or 'bottom-left'

  • theme — 'light' (default), 'dark' or 'auto' to follow the system

  • primaryColor — accent color, default '#6366f1'

  • greeting — message shown before the first question

  • placeholder — input placeholder text

  • title — panel header, default 'Support'

  • launcherLabel — optional dismissible label pill next to the launcher button

  • suggestions — quick-start pills

  • tabs — which of 'home', 'ask', 'help' and 'feedback' to show, in order, exactly as given. Usually left out, so the dashboard decides.

  • locale — the widget's language: 'auto', the default, follows the page; a tag such as 'fr' fixes it. See Show the widget in your visitors' language

  • labels — replaces any of the widget's own texts, in the language it picked

  • pageContext — false stops the widget reading the current page. Default true.

  • appContext — an object, or a function returning one, of app state your markup does not expose

  • tools — functions the assistant can call in the visitor's browser

  • onSourceClick — callback when a source chip or an inline citation is clicked. Without it, the article opens inside the widget.

Peer dependencies: react and react-dom 18 or 19, plus @kookee/sdk.

On a single-page app, prefer the function form of appContext — it is read fresh on every message, so the assistant never works from state captured at mount.

Theming

Set theme and primaryColor. Those are the supported ways to restyle the widget. The script-tag widget renders inside a shadow root, so page CSS cannot reach into it (and it cannot leak styles onto your page). The React widget renders in your page and also reads the --kookee-* variables it shares with the React components. See Customize the chat widget's appearance.

Sources

Under each answer the widget shows a Sources row: the articles that answer was built on, collapsing to the first three behind a "+N more" button.

It is narrower than the set of links in the answer, on purpose. An article the assistant read — because it opened it, or because a search returned it and the answer cited it — earns a chip. An article it only listed by title does not, so asking "what articles do you have?" returns a list of working links and no Sources row rather than restating every link twice.

Every linked article stays clickable either way, and onSourceClick fires for both. See How the assistant picks its sources for the full rule and the consulted flag.

Conversation behaviour

The same in React and in the script widget:

  • While an answer is streaming, the send button becomes a stop button. Stopping keeps the text that already arrived, and the server stops generating the rest.

  • The conversation survives page navigation, kept per browser tab.

  • A New chat button in the header starts a fresh conversation.

  • The close button minimizes the panel and keeps the conversation.

  • Expanded mode uses the full height of the window.

  • The optional launcher label pill can be dismissed by the visitor.

Writing articles that get found

Because search matches meaning, open each article by restating the problem in the words a user would use, before introducing your product's terminology. An article that begins "If you want to stop being billed" will be found by far more queries than one that begins "This article describes the subscription management interface."

Which questions to write about is not a guess: the Conversations tab on the Site widget page lists the ones your articles failed to answer — see Read what visitors asked your assistant.

Importing what you already have

If the answers already live in documents, you do not have to retype them. The Import files tab of the Help Center page turns Markdown, plain text, HTML, Word, PDF and image files into draft articles, reads scanned pages with AI, and checks every file for secrets and personal data before the chat can quote it — see Import files into your help center.

Was this article helpful?