<clutchcall-chat> web component. It mints a conversation-scoped
token. It opens a real-time session (QUIC first, WebSocket fallback). It drives
the same visual flow / step engine as ClutchCall Voice: announcements,
waits with queue position, forms, and skill queues. It also supports AI text
agents and human handoff.
Chat is widget- and JS-first today. There is no polyglot SDK for chat.
Embed the web component and configure it, or speak the
envelope protocol directly from a custom client
over raw MoQT. Server-side token minting uses your normal control-plane
credentials.
Add chat to a webpage
Add the widget to a page in two steps. First, mint a conversation-scoped token on your server. Never ship your API key to the browser. Then render the element with that token.1
Mint a scoped token server-side
Exchange your server credential for a short-lived token. The token is bound
to one conversation namespace (
chat/<org>/<conv>). Do this in a backend
route. Give the result to the page.string
required
Your organization or workspace id. It scopes the conversation namespace.
string
The id of the flow that the conversation runs. You build the flow in the
console with the same step engine as voice. Omit this field to use your
default flow.
number
default:"900"
The token lifetime in seconds. When the token expires, the widget mints a
new one through your endpoint.
2
Render the widget with the token
Load the widget loader once. Then place the element and pass the minted
token. The code below is the complete, self-contained embed.
index.html
support) runs
on connect. It greets, collects, and queues, exactly as you built it in the
console.
Present a form
Forms are flow-driven. A step in your flow presents a form. The step blocks until the visitor submits a validform_result. The widget renders the fields
and does client-side validation. It returns the structured result to the
engine. You write no front-end code. Add a form step to the flow that the token
points at:
flow: support (excerpt)
form envelope. The widget renders it. On submit, the
widget publishes a form_result envelope. The flow validates the result before
it advances. The captured values are available to downstream steps and to the
agent who picks up.
For a custom (non-widget) client: you receive the
form envelope on the
server track. You then publish a matching form_result on the client track
yourself. See the envelope protocol.Route to a skill / queue
Aqueue step puts the conversation into a skill queue. It announces the queue
position while the visitor waits. Human agents, an AI text agent, or both can
staff the same skill. Routing picks a handler that is eligible and available.
Attach an AI text agent
Point a queue step, or the whole flow, at an AI text agent to answer automatically. The agent is the same agent runtime as voice, in text mode. It has the same tools, knowledge, and DAG. Text turns drive it instead of audio.message envelopes. It can
raise a typing indicator between turns. When it hits a tool or a policy that
requires a person, it escalates. See the next recipe.
Hand off to a human
Escalation is a flow transition. The AI agent, or aswitch on its outcome,
routes the live conversation into a human skill queue. The conversation
keeps its id. When the human accepts, they see the visitor’s history, the
captured form, and the AI transcript.
flow: AI first, human on escalate
1
The AI agent decides a human is needed
Any tool, guardrail, or explicit “talk to a person” intent transitions the
flow to the
to-human label.2
The conversation queues to a human skill
The engine offers it on the agent console. A human agent can hold several
concurrent chats, so pickup is fast. The engine re-offers unanswered
offers per your RONA policy.
3
The human takes over in place
The engine relays the agent’s messages onto the same
server track that
the widget subscribes to. The visitor’s chat window never reconnects and
never loses scrollback.The full agent-side experience lives in the console, not the widget. This
includes offer acceptance, the multi-chat workstation, and what the human sees
on pickup. Media escalation from a text chat to a voice/video call is on the
roadmap. Today, handoff is text-to-text.
Send a file attachment
Attachments ride out of band. Upload the raw bytes over HTTPS. The response is an attachment meta. Then publish a normalmessage envelope that references
the meta. The bytes never travel through the real-time session. Only the small
meta does.
On the widget, this is zero-code. The paperclip button, drag-and-drop, and
a multi-file picker (
<input accept>) upload and attach for you. The recipe
below is for custom clients that speak the
envelope protocol directly.1
Upload the bytes, get an attachment meta
POST the raw file bytes to the upload endpoint with the session token.
This is the same conversation-scoped token that the widget or client holds.
Put the file name in the x-cc-filename header. Set Content-Type to the
file’s MIME type. The endpoint permits only an allowed MIME list and a
maximum size. It rejects all other uploads.header
required
The original file name. The meta keeps it, and the download URL contains
it.
header
required
The file’s MIME type. It must be on the allowed list, or the endpoint
rejects the upload.
string
The attachment id. It is unique in the conversation.
string
The file name as uploaded.
number
The byte size that the engine recorded.
string
The stored MIME type.
string
The download URL for the bytes. It resolves to
GET /v1/chat/attachment/<conv>/<id>/<name>. Object storage sits behind
it.2
Publish a message envelope that carries the meta
Send a normal
message envelope. Set data.attachments to an array of the
metas that you received. text is optional. An attachment-only message
can leave it empty. To attach several files, upload each file, then list
all the metas.The engine emits a
chat.file.uploaded event { role, count }. It records
"[file: <names>]" in the transcript. Thus an attached AI text agent knows
what files the parties shared, although it cannot read the bytes.React to a message
Everymessage envelope carries a mid. This is an author-owned message
id, unique in the conversation. The engine stamps it and relays it. Reactions,
and quoted replies via reply_to, address a message by its mid. To react,
publish a reaction envelope with data { op, emoji, target_mid }.
On the widget, this is zero-code. The hover quick-reaction strip and the
emoji picker publish these envelopes for you. Reaction pills show live counts.
The recipe below is for custom clients.
'add' | 'remove'
required
Add the reaction, or remove one that you added before.
string
required
The emoji to attach, e.g.
"👍", "🎉", "❤️".string
required
The
mid of the target message.chat.reaction
event { role }. Reaction envelopes are their own kind. The full set is
message | typing | form | form_result | reaction | event. See the
envelope protocol.
Custom theme
Match the widget to your brand. Use attributes for the common options. Use CSS::part() selectors for deep styling. The widget is a real custom element, so
your page CSS can style its shadow parts.
'light' | 'dark' | 'auto'
default:"auto"
Color scheme.
auto follows the visitor’s prefers-color-scheme.string (CSS color)
The primary accent color for the launcher, the buttons, and the visitor
bubbles.
number
The corner radius in pixels for the panel and the bubbles.
'bottom-right' | 'bottom-left'
default:"bottom-right"
The docked corner for the floating launcher.
Mount programmatically
You can skip the declarative markup and create the widget from JavaScript. Use this for SPAs, consent-gated loading, or a panel that opens from your own button. The element exposesopen(), close(), and a config object.
For a fully custom UI with no widget, drop to the wire. Subscribe to the
server track. Publish client envelopes yourself over raw MoQT.
Widget & API documents the message, typing,
form, and event envelope shapes.Related
Widget & envelope protocol
Every widget attribute and the JSON envelope wire for custom clients.
Chat overview
How conversations, tracks, flows, and agents fit together.
End-to-end recipes
Full builds: a support widget with AI-to-human handoff and lead capture.
Deployment models
Where the engine runs and how to make chat highly available.

