perfox
Get started
Developers · Custom widget

Your UI. Our engine.

Ship chat that looks exactly like your product. Drop in our widget, wrap the headless @perfox/widget-core library in your own components, or build the whole thing from the raw REST + WebSocket API. Three tiers, one conversation engine.

Read the guideHeadless library API
Three ways in

Pick the level of control you want

01

Embed

No code

Drop one <script> tag and theme it with data-* attributes. Our UI, your colors — live in minutes.

Embedding guide
This page
02

Headless library

Your UI

Build your own components in any framework. @perfox/widget-core owns init, async replies, history, voice and upload — you own every pixel.

Headless library API
03

Raw protocol

Full control

No Perfox code at all. Speak the REST + WebSocket protocol directly from any language or runtime.

Build from scratch
Tier 2, live

A real widget, fully custom

The chat below is React we wrote on top of @perfox/widget-core — talking to a live Perfox tenant. Open it, send a message (202 → WebSocket reply), and try the history switcher: every thread is a real, replayable conversation.

How it's built

Two steps to conversation

1 · Install
# npm — bundle it with your app
npm install @perfox/widget-core

# …or the hosted UMD bundle, no build step (window.PerfoxWidgetCore)
<script src="https://acme-api.perfox.ai/widget/v1/widget-core.js"></script>
2 · Instantiate & render
import { PerfoxConversation } from "@perfox/widget-core";
// via CDN:  const { PerfoxConversation } = window.PerfoxWidgetCore;

const chat = new PerfoxConversation({
  api_url: "https://acme-api.perfox.ai",
  site_id: "site_2f9c…",          // Web Chat node → EMBED tab
});

// Render whatever you like from these events:
chat.on("ready",   ({ capabilities, ui }) => mountHeader(ui));
chat.on("history", ({ messages })         => paint(messages));
chat.on("message", (msg)                  => append(msg));  // user echo
chat.on("reply",   (msg)                  => append(msg));  // assistant turn
chat.on("error",   ({ message })          => toast(message));

await chat.init();        // opens the conversation + reply WebSocket
chat.send("Hi there!");   // 202 → the engine correlates the async reply for you
Async reply correlation

POST /send returns 202; the engine matches the WebSocket reply frame by reply_id and self-heals with a history poll if a push is missed.

History & paging

Replays the thread on connect and pages backwards with loadOlder() — no cursor bookkeeping in your UI.

Voice, when enabled

startVoice() / stopVoice() over LiveKit, with partial + final transcript events you can render as live captions.

Upload & identity

upload(file) for attachments and identify(user_context) for HMAC-verified end-users — same API surface, opt-in.

Full method & event reference in the headless library docs. Prefer no library? Build from the raw protocol.

FoxAgent
Connecting…

Connecting…