# Chat Widget (/docs/agents/chat-widget)

> For the complete documentation index, see [llms.txt](https://docs.tandemcx.ai/llms.txt). Any page on this site can be fetched as Markdown by appending `.md` to its URL.

The chat widget is a small script you add to your own website. Visitors get a chat
launcher in the corner of the page; behind it is one of the agents you already
build in **Voice Agents**.

The agent does not change. Its system prompt, knowledge base, tools, skills, and
safety settings all apply exactly as they do on a call, so an agent you have
already tested by phone answers the same way in chat.

The agent behind a widget has to have **Chat** among its
[Channels](/docs/agents/voice-agents/persona#channels). Both channels are on by
default, so an ordinary agent already qualifies; saving a widget against one that has
**Chat** switched off is refused, naming the agent as the thing to change. An agent
built for chat alone is a normal choice here — switch **Voice** off and it needs no
voice, and the call-only settings leave its editor.

## What carries over from the agent, and what does not

| Applies in chat                                     | Does not apply                                       |
| --------------------------------------------------- | ---------------------------------------------------- |
| System prompt and persona                           | Voice, language, and speech settings                 |
| Knowledge base                                      | Turn-taking and interruption settings                |
| Tools — workflows, integrations, MCP, linked agents | Idle timeout and maximum call duration               |
| Skills                                              | Thinking cue                                         |
| Dispositions                                        | Keypress capture                                     |
| Safety — guardrail and steering rules               | Transfer to an outside number or another voice agent |
|                                                     | Caller memory keyed to a phone number                |
|                                                     | Reconnect after a dropped call                       |
|                                                     | Post-call analysis                                   |
|                                                     | Phone number routing                                 |
|                                                     | Goal                                                 |
|                                                     | Vocabulary                                           |

You do not have to keep this table in mind while editing an agent. Every setting in
the right-hand column is marked **Voice only** in the agent editor, on the card or
the field that owns it, so the ones that do nothing in chat are visible where you
would otherwise change them. On an agent built for chat alone — **Voice** switched
off in its [Channels](/docs/agents/voice-agents/persona#channels) — they are not
marked but absent, since every setting left on screen applies in chat.

Two things to know before you write your first widget greeting:

- **The agent is told it is in a text chat**, not on a call, so it will not talk
  about hearing you or the line. It also writes plain text rather than markdown,
  because that is what the panel renders.
- **A returning visitor is remembered, but by browser rather than by phone number.**
  When someone starts a new conversation having had one before, the agent opens
  knowing a short summary of what they talked about and anything left unresolved,
  so they do not have to explain themselves twice. It is told not to open by
  reciting that back at them.

    This is separate from the caller memory a phone agent keeps. Someone who chats
    and then calls is two different people as far as the agent is concerned.

**Every message shows the time it was sent**, under the message, in the visitor's
own locale and timezone — `2:06 PM` or `14:06`, whichever their browser uses.
Hovering shows the full date, for a panel a visitor has left open for a while.

## Coming back to a conversation

A visitor's conversation is not tied to the tab they started it in. It survives a
reload, a click through to another page, closing the tab, and coming back the next
day — because the conversation lives on our servers, and the visitor's browser
keeps only the two ids needed to ask for it.

What that means in practice:

- **A visitor can pick up where they left off** for 24 hours from their last
  message. After that it is read-only: they can still read it, and
  replying starts a new one.
- **They can read it for 90 days** from their last message, which is the same
  period a finished phone call's transcript is kept for.
- **Their browser is what identifies them**, not an account. A visitor who
  switches to a different browser, a different device, or a private window is a
  new visitor with no history. There is nothing to log in to.
- **A visitor's history is per widget.** Someone who has chatted on two different
  sites using Tandem is two unrelated visitors; we do not join them.
- **A message that failed to send is remembered by their browser** and shown when
  they come back, so they can try again rather than wonder whether it arrived.

### Several conversations at once

A visitor is not limited to one conversation. A **back arrow** in the top left of
the panel, before the avatar and the bot name, opens their history: everything they
have talked to this widget about, most recent first, each with the last thing said,
plus **Start a new conversation**. The arrow appears once they have an identity —
that is, once they have sent something — so a first-time visitor is not offered a
list that would be empty.

Selecting a conversation opens it. One they can still reply to opens with the
composer; one that has ended or gone quiet past its window opens read-only, with a
line saying so and a way to start a new one. The panel always opens onto the
conversation they were last in, so history is a deliberate click rather than
something they have to navigate past.

The history list has no back arrow of its own: a visitor leaves it by picking a
conversation, starting a new one, or pressing Escape.

The list holds their 25 most recent conversations.

If a conversation's beginning has aged past what the agent keeps, the panel shows
what remains rather than nothing.

## When a person takes over

A supervisor can take a chat over from
[Live Conversations](/docs/agents/live-conversations#taking-a-chat-over).
Taking over is one-way, so the agent doesn't answer again in that conversation.
This isn't enabled for every organization, and a chat that a workflow answers can't be taken over.
The visitor sees:

- The agent says **I'm going to bring in a colleague who can help you from here. One moment.**
- A notice reads **You're now chatting with [name]**. It appears again if another supervisor picks
  the chat up.
- The panel header shows the person's name in place of the agent's. A named person gets an avatar
  with their initials. If your organization shows a fixed label (**Support team**, say), the
  avatar is a generic person icon instead.
- A typing indicator (three animated dots) shows while the supervisor writes.
- When the conversation ends, the composer is replaced by **This conversation has ended.** A
  conversation that has gone quiet past its window reads **This conversation is closed.** instead.

What name appears is set under **Admin → Organization**. See
[Organization](/docs/overview/account-admin#live-chat-takeover).

## Answering with a workflow

**Answered By** on a widget chooses between the two: an agent replies in its own
words, a workflow follows the script you built. Exactly one applies, and switching
between them does not discard the other's choice, so you can look at both before you
save.

A workflow-backed chat uses the same panel, the same transcript, and the same
conversation history a visitor gets from an agent. What changes is what decides the
reply:

- **The workflow starts when the visitor opens the panel**, before they have typed
  anything. It runs until it reaches a **Send Chat Message** with **Wait for Reply**
  switched on, or until the conversation ends. That is what lets a workflow ask the
  first question: the widget's **Greeting** is a fixed line with nothing behind it, so
  it can greet but it cannot collect an answer.
- **The greeting still greets.** A visitor sees the widget's greeting first and the
  workflow's opening message under it. Leave **Greeting** empty if you want the
  workflow to be the only thing that speaks.
- **Each visitor message resumes the run** at the node that was waiting, stores the
  text in that node's **Store Reply In** variable, and continues from its **Reply
  Received** exit.
- **The conversation ends** when the workflow reaches a **Disconnect** node, when the
  reply timeout on a waiting node elapses with nothing wired to **No Reply**, or when
  the workflow runs out of nodes. The panel then offers to start a new
  conversation.

**Voice nodes do not work in a chat.** Play Message, the recording nodes, and the
transfer nodes all need a live phone call. Reached from a chat they take their
`fallback` exit, so wire that exit or keep them off the path a chat takes.
**Disconnect** is the exception: it works on both, hanging up a call or closing a
chat. See the [node reference](/docs/workflows/reference) for the exits each node
has.

The workflow must be **published**. An unpublished one is listed in the picker but
cannot be chosen.

## What you configure

A widget owns how it looks and where it is allowed to run:

- **Name** — how the widget is listed in Tandem. Visitors never see it, so this is
  the place for something that tells two widgets apart: "Acme Support — checkout
  page".
- **Display Name** — the bot name visitors read in the chat header. Leave it blank
  and the header shows the widget's name instead. Two widgets may share a display
  name, which is the point of the separate field: one persona can answer on several
  pages while each widget stays findable in the list.
- **Description** — one line under it, e.g. "Usually replies in a few seconds".
- **Avatar** — the image in the chat header, as an `https://` URL. Left empty, the
  widget draws the initials of the name in the header; switched off, the header
  shows the bot name alone.
- **Brand color** — your color, as a hex value or from the color picker. Left
  empty, the widget uses its default violet (`#a875ff`).
- **Greeting** — the first message a visitor sees. Left empty, the agent's own
  greeting is used, which is usually written for a phone call.
- **Position** — which corner of the page the launcher sits in: bottom right (the
  default) or bottom left.
- **Icon style** — the glyph on the launcher: a bubble, an envelope, or your own
  image.
- **Allowed origins** — the websites permitted to open the widget. This is the
  security boundary; see [Installing](/docs/agents/chat-widget/installing). It is
  also how you take a widget down: remove an origin and the widget stops loading
  there, with the snippet and the rest of the configuration untouched. A widget
  with no origins at all loads nowhere.

A **live preview** sits beside these fields and updates as you type, so you can see
the panel your visitors will get before you save. It shows colors, the name, the
avatar, the launcher's icon and corner, and the greeting exactly; the panel's size
and its behavior on a phone are not to scale.

### Chatting with the agent in the preview

The preview is a working chat, not a picture of one. Type into it and you are
talking to the agent the widget points at, with the same prompt, knowledge base,
tools, and safety rules a visitor would get. Nothing is sent until you send it.

Three things to know:

- **It tests the agent, not the installation.** The preview runs from Tandem, so it
  skips the public key, the allowed origins, and the rate limits. A widget that
  answers perfectly here can still be silent on your own site, and when it is, the
  cause is almost always the origin list — see
  [Troubleshooting](/docs/agents/chat-widget/troubleshooting).
- **Nothing is saved.** A preview conversation does not appear in Live
  Conversations, is not kept as a transcript, and is not counted in reporting. The
  agent also does not remember it, and will not bring it up in a later conversation.
- **It follows what you are editing, not what you last saved.** Switching the agent
  or rewriting the greeting takes effect on the next message, before you press Save.
  Switching the agent starts the conversation over, since the new one did not say
  any of it.

**Reset** appears once you have said something, and throws the conversation away.
Until you pick an agent, the message box says so and stays disabled.

#### Previewing a workflow

A workflow-backed widget previews the same way, with one difference: the message box
is replaced by a **Start chat** button until the workflow has run its opening move.
Pressing it is the preview's stand-in for a visitor opening the panel — the workflow
runs, says whatever it opens with, and the message box takes the button's place so
you can answer.

It is a button rather than something that happens on its own because a workflow
preview is a **real run**: texts are sent, integrations are called, and variables are
written. Picking a workflow in the target list costs nothing, and nothing runs until
you press Start.

**Reset** puts the button back. The next Start is a fresh run, not a continuation of
the one before it.

### The launcher

The launcher is the bubble in the corner of the page — the only part of the widget
a visitor sees before they click anything. Two things about it are yours to set:

- **Position** moves it, and the panel it opens, to the bottom left or the bottom
  right. Move it if it lands on something your site already puts there, such as a
  cookie banner or a back-to-top button.
- **Icon style** is the mark on it: a speech **bubble**, an **envelope**, or
  **custom** — your own `https://` image, which fills the bubble. A transparent PNG
  on your brand color reads best; a custom icon with no image falls back to the
  bubble rather than showing an empty circle.

The launcher **stays on the page while the chat is open**. It does not disappear
behind the panel: its icon turns into a downward chevron, so a second click on the
same spot puts the chat back down. The cross in the chat header closes it too — both
do the same thing.

The launcher's icon is **separate from the avatar**. The avatar is the picture in
the chat header once the panel is open, and the two are usually different marks: a
photo that reads beside the bot's name is rarely legible at 56px in the corner.

### Making the panel bigger

Beside the cross, the chat header carries an **expand** control. It gives the panel
the full height of the visitor's window and a little more width, which is what a
long reply or a list of options needs; pressing it again puts the panel back to its
usual size. The choice lasts as long as the visitor stays on the page and is not
remembered for their next visit.

On a phone the control is not shown — the panel is already the whole screen there.

### The avatar

The avatar has three states, not two:

- an **image**, when you give it an `https://` URL
- the **initials** of the widget's name, when the avatar is on and the URL is empty
- **nothing at all**, when you switch the avatar off — the header is the bot name
  and its description, flush to the left edge

### Brand color

One color brands the whole widget. It fills:

- the **chat header**, behind the bot name
- the **launcher bubble** in the corner of the page
- the **send button** in the message box
- the visitor's **own messages**

Enter it as a hex value — `#16a34a`, or `16a34a`, or the shorthand `#1a4` — or click
the swatch to pick one. **Reset** puts it back to the default.

**The text color on top is not something you set.** The widget works it out from your
brand color and uses black or white, whichever is readable. A pale brand color with white
text on it is a header your visitors cannot read, and the widget runs on your site, where
you would not necessarily see it.

This means a mid-tone color — many greens, ambers, and teals — gets **black** header
text rather than white, even though white is the more common choice. White on those
colors falls below the accessibility contrast minimum for text that size. The preview
shows you which one you are getting.

What is _not_ configurable yet: the rest of the panel's colors, the corner radius, and
the font.

## On a phone

On a phone-sized screen the chat fills the whole display when a visitor opens it,
including keeping the message box above the on-screen keyboard. Nothing to
configure. It is the same widget.

<Cards>
    <Card
        title="Installing"
        href="/docs/agents/chat-widget/installing"
        description="Add the snippet to your site and allow the origins it runs on."
    />
    <Card
        title="Troubleshooting"
        href="/docs/agents/chat-widget/troubleshooting"
        description="The widget isn't appearing, or a visitor sees an error."
    />
</Cards>
