Chat Widget
Put one of your agents on your website as an embeddable chat.
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. 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 — 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. 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.
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 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. 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.
- 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.