- A flow that you design once in the console.
- A server endpoint that mints a short-lived, namespace-scoped token.
- The web component that you put on the page.
Chat is web-component + JavaScript first today. There is no native mobile
or polyglot chat SDK. To drive chat from another platform, speak the
envelope protocol over a raw MoQT client. The
server-side token mint shown here is part of the control-plane API.
How a conversation is wired
Every conversation lives under three MoQT tracks atchat/<org>/<conv>:
Messages are JSON envelopes, one per MoQT object:
kind is one of message | typing | form | form_result | event. The full
schema is in Widget & API.
Recipe 1 — Support widget with AI-to-human handoff
A visitor opens your site. The widget greets them with an AI text agent. If they ask for a person, the engine offers the conversation to a human agent. The human agent sees the entire transcript. The human joins the same conversation, with the same tracks, so no history is lost.1
Design the flow in the console
Build a flow named
support in the console’s visual flow builder. This is
the same step engine that powers voice IVR:- Announce a greeting (
"You're chatting with our assistant."). - Queue to skill → AI text agent. Point the step at a text
conversation DAG. This is the agent runtime in text mode. It answers on
the
servertrack. - Escalation branch. When the visitor’s intent is “talk to a human”,
or when the AI calls a
handofftool, queue to skill → human. The visitor keeps chatting on the same conversation. The engine offers it to an available human agent. If the first agent does not pick up, the engine re-offers it (RONA, redirect-on-no-answer).
2
Mint a token on your server
The widget calls a URL on your server to get a short-lived token. Your
server holds the API key. It asks the control-plane API to mint a token
scoped to exactly one conversation’s tracks.The returned token carries an
ns claim of chat/acme/<conv>/*. The
widget can publish and subscribe only in that one conversation.3
Drop the web component on the page
Load the widget script and place the element. The tag is your brand name in
lowercase plus
-chat. The element fetches a token from token-endpoint.
It opens the connection and runs the support flow.index.html
string
required
Your workspace id. It must match the token that the endpoint mints.
string
required
A URL on your server that returns
{ token, conversationId, relayUrl }.
The widget calls it when it first opens.string
The flow to run. If omitted, the widget uses the workspace’s default chat
flow.
'auto' | 'wss'
default:"auto"
auto tries QUIC first and falls back to a secure WebSocket. wss
forces the fallback. Use wss behind proxies that block UDP.string
Header text and launcher placement (
bottom-right, bottom-left).4
Staff the human side
Human agents sign in on the agent portal in the console. Offered
conversations appear with the transcript pre-loaded. An agent accepts.
Their replies flow onto the
agent track. The engine relays that track to
server, and the widget renders the replies inline. Set each agent’s
concurrent chat capacity so one person can cover several conversations.What the handoff looks like on the wire
When the flow crosses the escalation branch, the engine emits anevent
envelope on the server track. The widget can then show a status line. After
that, the human’s messages arrive as ordinary message envelopes:
The human sees the transcript: every visitor turn and every AI turn on the
conversation. The AI agent’s hidden state does not transfer. That state is its
tool-call scratchpad. The human works from the visible conversation, exactly as
the visitor sees it.
Recipe 2 — Lead-capture form that routes by result
In this recipe, the flow presents a form. It blocks until a validated result arrives, and then it branches. High-budget leads go straight to a human sales queue. All other visitors continue with the AI text agent. You write no form HTML. The widget renders aform envelope natively and returns a
form_result.
1
Design the form and the branch
Build a flow named
lead-capture:- Announce a one-line intro.
- Present a form and wait for a validated result. The engine sends a
formenvelope. It does not advance until the widget returns aform_resultthat passes the field validation you set (required fields, email format). - Branch on the result. Route by a field value. Example: if
budget == "$5k+", then queue to skill → human (sales). Otherwise, queue to skill → AI text agent to answer questions and book a demo.
2
Mint the token (flow = lead-capture)
Use the same endpoint as Recipe 1, pointed at the other flow:
3
Embed the widget
landing.html
auto-open="true" opens the panel on load. The form then appears
immediately.The form round-trip
The engine sends aform envelope on the server track. The widget renders
the fields. On submit, the widget publishes a form_result on the client
track. The flow validates the result and continues.
budget, so the flow queues this visitor to the human
sales skill. A $1k–5k visitor continues with the AI text agent instead. The
server enforces validation. If email is malformed or budget is missing, the
flow presents the form again and does not advance.
Drive it from a custom client
Sometimes you cannot use the web component, for example in a native app, a kiosk, or a server-to-server bot. In that case, open the same three tracks over a raw MoQT client and speak the envelope protocol yourself:- Publish
message/form_resultonclient. - Subscribe to
server. - Honor
typing/event/form.
Related
Widget & API
Every widget attribute, the full envelope schema, and a raw-MoQT client.
Chat overview
How chat rides MoQT and shares the flow engine with voice.
Cookbook
Short, single-purpose snippets: embed, form, route, hand off.
Authentication
How short-lived, namespace-scoped tokens are minted and checked.

