- The widget — the
<telequick-chat>web component and its config attributes. - The envelope protocol — the JSON wire format on the chat tracks, so you can build a custom client instead of using the widget.
Chat is widget/JS-first today. There is no polyglot chat SDK (no typed
Python/Go/Rust client) — the shipped surfaces are the embeddable web component
and the agent-side console. If you need chat outside the browser widget, build
a custom client against the envelope protocol documented below. That path is
raw and unversioned beyond the
v field — treat it as the low-level contract,
not a supported SDK.Embed the widget
Load the component
Include the widget script once, anywhere on the page. It registers the
custom element and does nothing until you place the tag.
Place the tag
Drop the element where you want the launcher to appear. Only
org is
strictly required; the widget fetches a per-visitor token at runtime.Config attributes
Attributes are set on the tag (kebab-case) or via the DOM property of the same name. Booleans follow HTML rules — present meanstrue.
Workspace / org id the conversation belongs to. Scopes the chat namespace
(
chat/<org>/…) and the minted token.Endpoint on your origin that returns
{ token }. The widget calls it once
per visitor to mint a namespace-scoped token. Prefer this over a static
token so credentials never ship to the browser.A pre-minted chat token, if you would rather fetch it yourself and set it
directly. Mutually exclusive with
token-url. Short-lived — set it before the
session opens.Relay host the widget connects to. Override only for a self-hosted or
region-pinned relay.
Color scheme.
auto follows the visitor’s prefers-color-scheme.Resume a specific conversation id instead of starting a new one. Omit to open
a fresh conversation and let the engine assign the id.
Optional opening line rendered before the flow runs. Display-only — the flow
engine still drives the actual conversation.
bubble floats a launcher button; inline mounts the panel in the tag’s own
box (embed it in a page section).The attribute surface is still settling.
org plus a token source
(token-url or token) is the firm contract; the display attributes above
are convenience wrappers and may change name. Pin the widget script version in
production and re-check this page after upgrades.Programmatic control
Grab the element and drive it from JavaScript for open/close, prefilled context, and lifecycle hooks.event.detail. If you only need to observe or embellish
the widget, use these events rather than a custom client.
The envelope protocol
Every message on a chat conversation — in both directions — is a single JSON envelope, one per transport object. It is the contract you implement when you build a custom client.Protocol version. Currently
1. Reject envelopes with a version you do not
understand.Monotonic sequence number per track. Use it to order and de-duplicate;
gaps mean you missed an object.
Author timestamp, Unix epoch milliseconds.
What the envelope is. Determines the shape of
data (below).Who authored it.
visitor is the end user, agent is a human or AI text
agent, system is the flow engine (announcements, queue updates, forms).Kind-specific payload. See the table below.
data by kind
A chat message.
mid is the author-owned message id (see Message identity and
replies); text is the body; optional
attachments carry file references; optional reply_to
quotes an earlier message. Rendered in the transcript.Typing indicator. Emit
start/stop as the visitor types; render the peer’s
indicator when you receive it.The engine is presenting a form and will block the flow until it receives
a matching
form_result. Each Field has { name, label, type, required, options? }.
Render it and collect input.Your reply to a
form. Send it (role visitor) with the collected values.
The engine validates against the form definition and unblocks — or re-presents
the form on a validation failure.Add or remove an emoji reaction on the message identified by
target_mid. The
engine relays it to the other side and emits a chat.reaction event. See
Reactions.A lifecycle signal from the flow engine. Common
name values: queued
(often with a position), agent_joined, agent_left, resumed (AI resumes
after a human idles out), closed. Treat unknown events as advisory.Message identity and replies
Everymessage envelope carries a mid — an author-owned message id, unique
within the conversation. The engine stamps and relays it, and it is how one
message points at another: both reactions and quoted replies
address a target message by its mid.
Stable identifier for a message, unique within the conversation. Present on
every
message envelope. Use it as the React/render key and as the target for
reactions and quoted replies.Present when the message is a quoted reply.
mid is the message being
quoted; preview is a short snippet of that message for rendering the quote
inline. Omit it for a normal (non-reply) message.On the widget, quoted replies are wired already — no code. The
mid/reply_to
fields matter when you build a custom client or need
to understand the wire.File attachments
A message can carry files. The bytes travel out-of-band over HTTP, not over the chat tracks — you upload the file to the control-plane API first, then publish a normalmessage envelope that references the returned attachment
meta. This keeps large transfers off the realtime tracks.
On the widget this is fully built in: a paperclip attach button, drag-and-drop,
and a multi-file picker. The flow below is for custom clients and for
understanding the wire.
Upload the bytes
POST the raw file bytes to the upload endpoint with the session token. The
filename rides in the x-cc-filename header and the Content-Type is the
file’s MIME type. The response is the attachment meta.Publish a message that references it
Send a normal
message envelope with data.attachments set to an array of
the metas you got back. An attachment-only message is allowed — text may be
empty.The meta returned by the upload and carried in
data.attachments.chat.file.uploaded event
{ role, count }, and the transcript records a [file: names] line so an AI
text agent has context that a file was shared even though it never sees the raw
bytes.
Reactions
A reaction is its own envelopekind. It adds or removes an emoji on a message
identified by that message’s mid. The engine
relays the reaction to the other side and emits a chat.reaction event.
chat.reaction event { role } when a reaction is relayed.
On the widget, reactions are built in — an emoji picker, a hover quick-reaction
strip, and reaction pills with counts — so they work with no code. The envelope
above is for custom clients.
Build a custom client
If you need chat where the web component cannot run, connect directly to the chat tracks over the persistent connection and exchange envelopes yourself.Track layout
A conversation lives under the namespacechat/<org>/<conv> with three tracks:
| Track | Direction | You (custom client) |
|---|---|---|
client | visitor → engine | Publish your envelopes here |
server | engine → visitor | Subscribe — this is your only read |
agent | human agent → engine | Do not touch — the engine relays it to server |
agent track into server, so a client keeps a
single subscription (server) and a single publish (client) regardless of
whether an AI or a human is on the other end.
Open the session
Connect to the relay with your namespace-scoped token. The transport is QUIC
first with a WSS fallback — the same connection the widget uses.
Publish to `client`, subscribe to `server`
Announce/publish
chat/<org>/<conv>/client and subscribe
chat/<org>/<conv>/server. Each transport object you send or receive is
exactly one JSON envelope.Related
Chat overview
How the flow engine, AI text agents, human handoff, forms, and skill queues
fit together.
Chat cookbook
Copy-pasteable recipes: embed on a page, present a form, route to a skill,
hand off to a human.
Chat recipes
End-to-end builds — a support widget with AI-to-human handoff and lead
capture.
CSTA CTI
Drive third-party call control (makeCall, holdCall, transferCall) over the
same engine connection from an ISV/SDK client.