Add one custom element to a web page to put a support or sales chat on it. The visitor gets an AI text agent. The agent can answer, collect a form, and hand off to a human. All of this runs over a single QUIC connection, with a WSS fallback. The same visual flow builder that your voice IVR uses drives it. You build the conversation once as a flow of steps. The flow runs the same when the caller reaches you by phone or by the chat bubble. The chat modality is widget-first. You embed a web component and configure it with attributes. You author everything else in the flow builder: routing, AI or human handling, forms, and queueing. The engine serves it. For a custom client, you can speak the envelope protocol directly over raw MoQT. There is no separate polyglot SDK for chat today (see Honest limits).

When to use it

Support / sales chat

Put a chat bubble on your marketing site or app. It greets visitors and answers with an AI text agent. It escalates to a human when necessary.

One flow, phone + chat

Reuse the announcements, forms, and skill queues that you built for voice. Voice and chat share the step engine. Chat is a text-mode surface over it.

Lead capture

Present a form in the middle of a conversation. The flow blocks until the visitor returns a validated result. You get structured intake, and the visitor does not leave the chat.

Concurrent human agents

One human agent handles several conversations at the same time from the console. Routing is skill-based. If the agent misses an offer, the engine offers the conversation again.

Files & reactions

Visitors and agents attach files (images and documents). They react to messages with emoji or a quote-reply. The AI text agent sees uploaded files as context.

Embed the widget

The web component tag is your brand name in lowercase, plus -chat. The element that you place on the page is <clutchcall-chat>.
1

Load the widget script

Add the loader <script type="module"> from your workspace’s widget URL to the page <head>. The script registers the custom element and defines <clutchcall-chat>.
2

Place the element

Put <clutchcall-chat org="…" flow="…"> anywhere in the body. The widget renders a launcher bubble. When the visitor opens it, the widget renders a chat panel.
3

It connects on its own

When the visitor opens it, the widget mints a namespace-scoped token. The widget opens a MoQT session to the relay. The transport is QUIC first, with a WSS fallback. The widget then subscribes to its conversation’s server track. You do not write connection code.
string
required
Your workspace or organization id. It scopes the conversation namespace and the minted token.
string
required
The published flow (step-engine graph) that drives the conversation. The flow contains the announcements, the forms, and the queue-to-skill step. You author it in the flow builder, the same builder that voice uses.
string
default:"auto"
light, dark, or auto. This attribute is cosmetic. It does not change routing.
The widget subscribes to exactly one track (server). It publishes to one track (client). Everything that the visitor sees arrives on that single server subscription: AI replies, human replies, system announcements, and forms. The UI never reconciles multiple streams. See Tracks.

The three tracks

The address of each conversation is chat/<org>/<conv>. Each conversation carries three MoQT tracks. The split keeps the widget’s job simple: publish one track and subscribe to one track. A human agent can join without a change on the widget side. A human agent publishes on agent. The engine relays those envelopes onto server. That is why the widget needs only one subscription. AI replies and human replies both arrive on server. The transport layer does not tell them apart. The role field in the envelope identifies the author.

The envelope protocol

Every MoQT object on a chat track is one JSON envelope. This is the wire contract for a custom client. Speak it if you build your own UI over raw MoQT instead of the widget.
number
required
Envelope version. Currently 1.
number
required
A monotonic sequence number per track. Use it to order envelopes and to find gaps.
number
required
The epoch timestamp in milliseconds. The sender sets it.
string
required
One of message · typing · form · form_result · reaction · event.
string
required
The author. Examples: user, agent (AI or human), system.
object
required
The payload for the given kind: message text, a form schema, a submitted result, or an event body.

Message ids, attachments, and replies

The data of a message envelope carries more fields than text:
string
An author-owned message id. It is unique in the conversation. The engine stamps it and relays it. Reactions and quoted replies address a message by its mid.
array
Zero or more attachment metas for files sent with this message. Each meta is { id, name, size, mime, url }. An attachment-only message can have empty text. See File uploads.
object
A quoted reply. The object { mid, preview } points at the quoted message.

File uploads

Visitors and agents can attach images and documents to a message. The file bytes travel out-of-band, not through MoQT. Upload the bytes over HTTPS. The response is an attachment meta. Then publish a normal message envelope that references the meta.
1

POST the bytes

Send the raw file bytes to the control-plane API with the session token. The server permits only an allowed set of MIME types and a maximum size. It rejects all other uploads.
2

Read the attachment meta

The response is the attachment meta. Store the whole object. You attach it to the message verbatim.
3

Publish a message that references it

Publish a normal message envelope. Set data.attachments to an array of metas. For an attachment-only message, the text can be empty.
Download files from the engine’s download URL at GET /v1/chat/attachment/<conv>/<id>/<name>. Object storage serves the bytes.
On upload, the engine emits a chat.file.uploaded event { role, count }. The transcript records "[file: <names>]". Thus an AI text agent has context about the file, although the bytes never crossed the AI’s text channel.

Reactions

With emoji reactions and quoted replies, each side can annotate a specific message. The mid identifies the target message. A reaction is its own envelope kind:
string
required
add or remove.
string
required
The emoji to apply.
string
required
The mid of the target message.
The engine relays the reaction to the other side. It emits a chat.reaction event { role }.
On the widget, these features work with no code. The built-in UX includes a paperclip attach button, drag-and-drop, a multi-file picker (with accept filtering), an emoji picker, a hover quick-reaction strip, and reaction pills that show counts. The envelope and upload details above are for custom clients and for readers who study the wire. See Widget & API and the Cookbook for procedures.

Same flow engine as voice IVR

Every conversation runs through the same visual flow builder and step engine as voice. You do not learn a second product. The steps that you know from IVR apply directly to text:
  • Announcements — send a scripted message, for example a greeting, business hours, or a policy notice.
  • Waits + queue position — hold the visitor and show their queue position while they wait for an agent. The engine delivers the position as event envelopes.
  • Present a form — emit a form and block until a validated form_result arrives. Use this for lead capture and structured intake.
  • Queue to a skill — route the conversation to a skill queue. This is the same model as an ACD queue on the voice side.
A queue-to-skill step resolves to one of two handlers:
The engine hands the conversation to a text conversation DAG. This is the same agent runtime as voice, in text mode instead of speech. The agent answers and can call tools. It can also decide to escalate, and then it queues the conversation to a human skill.
AI and human handlers both publish through the engine onto server. Thus the widget does not see a mid-conversation AI→human handoff. The visitor keeps the same panel and history. The next envelope carries role: "agent" from a person instead of the model.

Honest limits

There is no polyglot SDK for chat today. The supported surfaces are the embeddable web widget and the agent-side console. You can build a custom client that speaks the envelope protocol over raw MoQT. But that path is JS/widget-first, and only the v field versions it. Treat it as advanced usage, not as a stable client library.

Widget & API

Embed the widget. Configure its attributes. Speak the envelope protocol from a custom client.

Cookbook

Task snippets: embed on a page, present a form, route to a skill, hand off to a human.

Recipes

End-to-end builds: a support widget with AI-to-human handoff, and a lead-capture form.

Agent runtime

The same runtime that the AI text agent uses, here in text mode.

Human handoff

How escalation to a human works across modalities.

Skill queues (ACD)

The skill-based routing model that the queue-to-skill step shares with voice.