Developers / Integration guide

Add Pernoex to your product.

Use the widget, MCP server or API to let people ask questions and get answers from your indexed docs. Every answer can include the source it used.

Three UI modes: Floating (a chat bubble), Embedded (always-visible, in your layout), and Inline Explainer (highlight text, “Ask AI”). For IDE setup, see MCP setup.

Floating mode

The default setup is a chat button in the corner of your page. Add this before the closing </body> tag:

<!-- before </body> -->
<script
  src="https://cdn.pernoex.com/widget.js"
  data-api-key="YOUR_API_KEY"
></script>

For self-hosted instances, add data-api-url pointing at your endpoint.

Embedded mode

Put the chat inside a container on your page. This works well for help pages, sidebars and custom layouts. The container needs a defined height.

<div id="chat-container" style="height: 600px;"></div>
Pernoex.init({
  apiKey: 'YOUR_API_KEY',
  mode: 'embedded',
  container: '#chat-container',
  showHeader: true,
  borderRadius: '12px'
});
Configuration options
OptionTypeDefaultDescription
apiKeystringrequiredYour project's public API key
modeenumfloatingfloating · embedded · inline
containerselectornoneRequired for embedded mode
showHeaderbooleantrueShow or hide the header in embedded mode
borderRadiusstring'0'CSS radius for the container
showAvatarsbooleantrueShow user/bot avatars next to messages
themeobjectnoneColor & style overrides (see Theme)
startPillsstring[]noneOverride the empty-state suggestion pills

Inline Explainer

Let users highlight text and ask a follow-up question. Pernoex opens a sidebar, answers from your knowledge base and keeps the original text visible. Turn it on in the dashboard or control it with selectors in code.

Pernoex.init({
  apiKey: 'YOUR_API_KEY',
  inline: {
    enabled: true,
    triggerSelector: 'article, .docs-content',
    excludeSelector: 'nav, .code-block',
    showFollowUp: true
  }
});
OptionTypeDefaultDescription
enabledbooleanfalseOverride the dashboard setting
triggerSelectorstringnoneLimit where text selection triggers the explainer
excludeSelectorstringnoneAreas to exclude from selection
positionenumautoauto · above · below
showFollowUpbooleanfalseAllow follow-up questions in the sidebar

Suggestion pills

On the empty state, the widget can show three suggested questions based on the current page. A visitor on /pricing can see pricing questions. Pass startPills to set your own questions.

MCP setup (IDE)Growth+

Connect your docs to AI code editors with the Model Context Protocol. Pernoex works with VS Code, Cursor, Claude Code, JetBrains and Windsurf. Generate a developer key (dk_live_) from your project's Surface page. The default limit is 200 requests/day per IP.

// .vscode/mcp.json — or .cursor/mcp.json
{
  "servers": {
    "pernoex": {
      "url": "https://mcp.pernoex.com/mcp",
      "headers": { "Authorization": "Bearer dk_live_…" }
    }
  }
}

Each IDE has its own config file. The server URL and Bearer header stay the same.

IDEConfig locationTransport
VS Code.vscode/mcp.jsonHTTP /mcp
Cursor.cursor/mcp.jsonHTTP /mcp
Claude Codeclaude mcp add …HTTP /mcp
JetBrainsSettings → MCP ServersHTTP /mcp
Windsurf~/.codeium/windsurf/…SSE /sse

Sessions have a 30-minute idle timeout and reconnect automatically; each developer key supports up to 10 concurrent sessions.

MCP tools

ToolKeyReturns
pernoex_askdk_live_A written answer from your indexed docs
pernoex_searchsk_live_Ranked source passages with relevance scores, without an AI-written answer

Theme customization

Pass a theme object with any combination of color overrides — all optional. Set background: 'transparent' for seamless embedding, or fontFamily: 'inherit'to use your page's font.

theme: {
  background: '#1a1a2e',
  assistantBubbleBg: '#16213e',
  userBubbleBg: '#0f172a',
  linkColor: '#818cf8',
  fontFamily: '"Inter", sans-serif'
}
PropertyApplies to
backgroundMessages area & window — use 'transparent' to blend in
assistantBubbleBg / TextColorAssistant message bubble and text
userBubbleBg / TextColorUser message bubble and text
linkColorLinks inside messages
inputBg / inputBorderColorInput field background and border
sendButtonBg / ColorSend button background and icon
fontFamilyGlobal font — pass 'inherit' for your page's stack

JavaScript API

After init(), control methods are on the global Pernoex object, and lifecycle callbacks can be passed to init.

Pernoex.open() · Pernoex.close() · Pernoex.toggle()
Pernoex.isOpen() · Pernoex.clearMessages()
await Pernoex.explain("text to explain")

// callbacks
{ onReady, onOpen, onClose, onMessage, onError }
Widget features
  • Copy for AI Agent — formats the full thread (question, answer, sources) for pasting into Cursor, VS Code or Claude Code.
  • Per-message & code-block copy — copy any single response, or just a code block without surrounding markdown.
  • Image lightbox — images in source content open full-screen on click.
  • Host font inheritance — set fontFamily: 'inherit'to match your page's type.

SSE events

Streamed responses connect over Server-Sent Events. For custom integrations, these are the event types you'll receive from /api/v1/chat/stream:

EventPayloadDescription
session_contextobjectSession metadata including conversation_id
tool / tool_completeobjectCustom action invoked, and its result
textstringStreamed answer chunk — append to the response
sourcesarraySource citations, each with title, URL and score
confidence0–1Answer confidence — below 0.5 may signal a content gap
doneStream complete — close the connection

REST APIScale

Query your knowledge base server-side with a secret key (sk_live_). Never expose secret keys in client-side code. Rate limit: 1,000 requests/minute.

# ask a question, get an answer with sources
curl -X POST https://api.pernoex.com/v1/query \
  -H "Authorization: Bearer sk_live_YOUR_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "query": "How do I set up webhooks?" }'

Use /v1/search to retrieve ranked documents without generating an answer.

Custom ActionsGrowth+

Let the AI call your APIs during a conversation. It can check an order, reset a password or look up an account across chat, Slack and voice. Auth headers are stored server-side and never exposed; every call is logged in your audit trail. Use confirmation_required: true for write operations. Growth: up to 5 per project. Scale: up to 25.

{
  "name": "check_order_status",
  "endpoint": "https://api.yourapp.com/orders/lookup",
  "method": "POST",
  "parameters": [{ "name": "order_id", "required": true }],
  "confirmation_required": false
}

Slack

Add Pernoex to your workspace so your team or customers can tag @Pernoex in any thread and get answers from your docs, with clickable source links. The bot reads full thread context, including images, and supports DMs. On Scale, use your own Slack app with a custom name, avatar and description.

Framework examples

Load the script and call init() on mount. In React, clean up with Pernoex.destroy() on unmount.

// React
useEffect(() => {
  const s = document.createElement('script');
  s.src = 'https://cdn.pernoex.com/widget.js';
  s.setAttribute('data-manual-init', 'true');
  s.onload = () => window.Pernoex.init({ apiKey, mode: 'embedded', container });
  document.body.appendChild(s);
  return () => window.Pernoex?.destroy();
}, []);

Identity verification (HMAC)Growth+

Confirm a user is who your server says they are. Compute an HMAC-SHA256of the user's details server-side and pass only the result to the widget. The identity secret must never appear in client-side code.

// server-side (Node)
const payload = [userId, email, name].join(':');
const hmac = crypto.createHmac('sha256', secret)
  .update(payload).digest('hex');

Webhook signingScale

Event webhooks, such as conversation completed, handoff triggered and content gap detected, are signed so you can verify they came from Pernoex. Each request carries an X-Pernoex-Signature header: t=…,v1=…, where v1 is the HMAC-SHA256 of timestamp.payload. Compare with a constant-time check.

CSP & domain restrictions

The widget renders inside a Shadow DOMboundary — your CSS can't affect it and its styles can't leak into your page. Styles inject via adoptedStyleSheets, so style-src 'unsafe-inline' is not required. You can also choose which domains may load the widget from your project settings. Unlisted origins are rejected.

# Content Security Policy
script-src 'self' https://cdn.pernoex.com;
connect-src 'self' https://api.pernoex.com;
style-src 'self'; font-src 'self' data: https://fonts.gstatic.com;

Troubleshooting

SymptomFix
origin_not_allowedAdd the domain (or localhost:PORT) to your allowed domains list
Embedded container emptySet an explicit height; confirm the element exists before init()
401 on MCPCheck the dk_live_ key and the Bearer header format
429 Too Many RequestsDeveloper keys allow 200 req/day per IP; contact support to raise it
Generic explanationsEnsure the knowledge base is indexed and has sufficient credit balance