Reference

Headless SDK

Same public key as the widget, same knowledge, same plan — without the launcher. Put an input on a help page, a docs site or inside your app, and render the answer however you like. Conversations land in your inbox next to widget chats, so ratings, leads and human handoff keep working. The page's domain must be in the bot's Allowed domains, exactly as for the widget.

No build step — one tag

<script async src="https://app.matterchat.co/askbox.js"
  data-key="YOUR_BOT_KEY"
  data-suggestions="Pricing|Refunds|Shipping"></script>

Renders a search-style ask box where the tag sits, in its own shadow root, so your theme and it leave each other alone. Works on Docusaurus, Mintlify, GitBook, Notion-exported sites, WordPress — anywhere you can paste HTML. Options: data-target="#selector" to render elsewhere, data-placeholder, data-accent="#hex", data-unbranded. Pin it with /askbox/<hash>.js from the build if you want Subresource Integrity.

Or a package, for your own UI

pnpm add @matterchat/core   # framework-free
pnpm add @matterchat/react  # or vue, svelte, angular — each exports useMatterChat

One question, one streamed answer

import { createMatterChat } from "@matterchat/core";

const mc = createMatterChat({ publicKey: "pk_live_…" });

const answer = await mc.ask("Do you ship to Canada?", {
  onToken: (_chunk, full) => (output.textContent = full),
});
answer.sources; // [{ n: 1, title: "Shipping", url: "https://…" }]

A help page in React

import { useMatterChat } from "@matterchat/react";

export function AskBox() {
  const { messages, ask, streaming, error } = useMatterChat({ publicKey: "pk_live_…" });
  return (
    <form onSubmit={(e) => { e.preventDefault(); ask(e.currentTarget.q.value); e.currentTarget.reset(); }}>
      <input name="q" placeholder="Ask anything about our product" disabled={streaming} />
      {messages.map((m, i) => (
        <p key={i} data-role={m.role}>
          {m.content}
          {m.sources?.map((s) => <a key={s.n} href={s.url ?? "#"}>[{s.n}]</a>)}
        </p>
      ))}
      {error && <p role="alert">{error.message}</p>}
    </form>
  );
}

useMatterChat keeps one conversation: the question appears at once, the answer grows token by token, and follow-ups remember what was asked. rate(1 | -1) rates the latest answer; reset() starts over. Vue, Svelte and Angular expose the same shape as a composable, a store and signals.

What an answer tells you

textThe answer, as Markdown — render it with the same renderer as the rest of your page. Streams through onToken first.
sourcesPages the answer came from, in citation order.
refusalNothing relevant was found; the bot said so instead of guessing.
lowConfidenceAn answer from thin evidence — a good moment for “was this helpful?”.
handoffThe visitor asked for a person and your team was paged.
humanModeA teammate owns this conversation; their reply arrives in the inbox, text is empty.

ask() throws a MatterChatError with a code: rate_limited, capped (plan limit), not_allowed (domain not listed), insecure_context (the page is plain HTTP — attestation needs HTTPS or localhost), unavailable, network. The hooks catch these into errorand keep the visitor's question on screen.