Conversations
What a visitor sees in the support panel, and how a thread behaves from the first message to the rating.
Thread lifecycle
| Moment | What happens |
|---|---|
| The panel opens with no history | The empty state shows. Nothing is stored about the visitor yet |
| First message | Creates the customer and a conversation. The message becomes the conversation’s title in the inbox. With telemetry on, the page it was sent from is kept as Started on |
| Later messages | Join the open conversation |
| The conversation is resolved or closed | The rating card appears above the composer. The composer stays |
| The next message | Starts a new conversation. The old one stays above a New conversation divider |
| A message on a conversation your team archived or snoozed | Unarchives it and clears the snooze |
- A visitor has one open widget conversation at a time.
- A visitor is one browser, kept by a token in
localStorage. With a signed identity, a visitor is one person on every device. - The panel shows up to 10 recent conversations and the newest 200 messages across them. A date line separates days.
- A teammate resolves a conversation in the inbox. The agent resolves finished ones itself while Resolve conversations automatically is on under Agent → Behavior, and says goodbye first.
- The same switch lets the agent close spam. A closed conversation looks resolved to the visitor.
- With Review the inbox nightly on under Agent → Behavior, a nightly pass resolves answered conversations that are still open. It is on by default.
- An email from the same customer continues their email thread, never the widget thread.
Empty state
A panel with no history shows the agent’s picture and name, How can we help you with Acme? with your workspace name, and three rows.
| Row | Sends |
|---|---|
| Ask a question | I have a question |
| Report a bug | I want to report a bug |
| Make a suggestion | I have a suggestion |
- A row sends its message at once, as the visitor.
- The rows are disabled while the email question shows.
- No prop hides them.
Email question
The composer asks for an email before the visitor writes. It shows only when all of these hold:
requireEmailistrue. That is the default.- Ask for an email is on under Agent → Behavior. That is the default.
user.emailis empty.- This browser has not given an address yet.
What the visitor sees:
- A tab reading Enter your email rises over the composer, and the message box becomes an email field.
- The arrow turns on once the text looks like an address. Enter also submits. Nothing checks that the address exists.
- Saved shows for a moment, then the message box returns.
- Files cannot be attached while the question shows.
The address is stored in this browser under median:email:<id>. It rides every later message and lands on the customer’s record, unless user.email is set. A send from MedianContactForm stores it the same way, so the widget stops asking. reset() removes it.
Replies
| The visitor sees | Meaning |
|---|---|
| The agent’s name with an AI badge | The AI wrote the reply |
| A teammate’s name and face | A person on your team wrote it. A teammate when they have no name |
| Three dots in a bubble | A reply is being written |
| Waiting for the support team above the composer | The agent handed the thread to a person, or a teammate took it over. Clears when a teammate replies |
| An image with no text, sent as the visitor | The agent asked to see the page. See Context and diagnostics |
| A red line above the composer | A send failed. The messages are under Attachments |
- The header shows your workspace name, Support online, and the faces of the agent and up to two teammates who replied. While the panel connects, it shows Support and Connecting.
- The AI answers first, unless the thread is waiting on a person.
- One AI turn arrives as up to four messages, with dots between them. Each pause is 0.7 to 2.5 seconds, longer before a longer message.
- If the visitor writes while the AI is answering, the answer starts over with the new message included.
- A teammate’s dots disappear 12 seconds after their last keystroke.
- Your team sees when the visitor is typing, never the text. The widget sends the signal at most every 2.5 seconds and stops it after 8 seconds without a keystroke. Before the first message and on a resolved conversation, the signal is ignored and your team sees nothing.
Unread replies
A reply is unread until the visitor has the panel open in a visible tab.
| Signal | Shows | Turn it off |
|---|---|---|
| Count on the launcher | Unread replies, 9+ above nine | launcher="hidden" removes it with the launcher |
| Card over the launcher | The sender’s name and face, and the first two lines of the newest reply | messagePreview={false}, or Preview replies under Agent → Behavior |
| Tab title | (2) Your page title |
titleCount={false} |
| Sound | A short chime at 45% volume | No switch |
- Pressing the card opens the panel.
- The cross on the card hides it in this browser. Screen readers announce it as Dismiss. The count stays until the panel opens. A newer reply brings a new card.
- The card sits below the launcher when the launcher was dragged near the top of the window.
- With
launcher="hidden", there is no count and no card. ReadunreadCountfrom useMedianSupport for a badge of your own. - Median stores read state per conversation. A reply read on one device is read on every device that shares the thread.
- Your own title comes back once the replies are read. If your router changes the title while the count shows, the count moves to the new title.
- The sound plays for each new written reply, with the panel open or closed. Replies that land together play once.
- History loaded with the page never plays a sound, and neither does the thread that arrives on sign in.
- Browsers keep the sound silent until the visitor has interacted with the page.
Email copies of missed replies
When the visitor is not watching the panel, Median emails the replies they missed. All three must be true:
- The visitor has an email address, from
user.email, the email question, or the contact form. - Email support is set up and turned on. See Email.
- The visitor has opened the panel at least once, or wrote in through the contact form. A client that never reports presence, such as one built on the REST API, is never emailed.
| Step | Timing |
|---|---|
| The open panel reports presence | When the panel opens, every 45 seconds after, and when the tab becomes visible or regains focus. Only while the tab is visible |
| Median checks after a reply lands | 2 minutes later |
| The visitor counts as away | No presence in the last 90 seconds |
- The missed replies go out in one email, from your support address, with the subject
Re: <conversation title>. Without a title, the subject isRe: your message. - The sender name is
Ada at Acmewhen one writer wrote every reply in it, andAcmeotherwise. - Replies already read on any device, replies already emailed, and replies older than the visitor’s last message are left out.
Ratings
When a conversation is resolved, a card above the composer asks How did we do? with five stars.
- A star saves the rating the moment it is pressed. Pressing another star changes it.
- After a star, the card asks Anything we could improve? with an optional box, up to 1,000 characters.
- The button reads Done while the box is empty and Send once it has text. Sent words are added to the same rating. An empty box never removes earlier words.
- Thanks for the feedback. shows, then the card folds away.
- Skip closes the card without a rating. A skip is remembered in this browser only. A rating stops the card on every device.
- If saving fails, the card shows Could not submit your rating. Try again.
- The next conversation gets its own card when it ends.
Ratings roll up into Satisfaction in Analytics.
Pages and highlights
One of your tools can offer the visitor a page and point at something on it. Writing those tools is on Pages and highlights.
| The visitor sees | When |
|---|---|
| Take me there under a reply | The reply carried a destination and you passed onNavigate |
| Take me there above the composer | A teammate ran the tool, or approved it after the reply. Shown for 5 minutes after the run |
| A ring around an element, scrolled into view | A highlight arrived on its own, or the visitor pressed a button that carried one |
- The page never changes on its own. Pressing the button calls
onNavigatewith the path. - The path must start with
/and stay on the page’s origin. Anything else logsMedian: refused to navigate to {path}, which is not a path on this site. - A button under a reply stays with the reply. The one above the composer goes once pressed.
- On phones and in the support modal, pressing the button closes the panel. The corner panel stays open.
- A highlight on its own is drawn once, as the reply arrives. Highlights older than 5 minutes, and highlights in history, are not drawn.
- The ring waits up to 4 seconds for the element, stays for about 2.6 seconds, then fades. It sits over your page, takes no clicks, and changes none of your styles.
- A selector the browser cannot parse logs
Median: a tool asked to highlight {selector}, which is not a valid CSS selector. - Without
onNavigate, no button shows. In development the console says so.
Attachments
| Limit | Value |
|---|---|
| Message length | 4,000 characters |
| Files per message | 6 |
| File size | 25 MB each |
| Refused types | text/html, application/xhtml+xml, image/svg+xml, text/xsl, application/xslt+xml |
- Attach with Add an attachment (the plus button), by dropping files on the composer, or by pasting. The box reads Drop to attach while a file is over it.
- A file over 25 MB shows Larger than the 25 MB limit on its chip. A refused type shows This kind of file cannot be sent. Either one blocks sending until it is removed.
- The composer does not count files. A seventh file fails on send.
- A failed send keeps the draft and its files. Text typed while a message was sending stays in the box.
- What the agent reads from files is on Built-in abilities.
A failed send shows one of these above the composer:
| Code | Message |
|---|---|
empty_message |
Enter a message or attach a file. |
message_too_long |
Messages have to be 4000 characters or fewer. |
too_many_attachments |
Messages can carry 6 files at most. |
attachment_too_big |
"photo.png" is larger than the 25 MB limit. |
attachment_wrong_type |
"page.html" is a kind of file we cannot accept. Send it as a screenshot or a plain text file. |
attachment_missing |
"photo.png" did not finish uploading. Try sending it again. |
rate_limited |
Too many requests. Wait a moment and try again. |
Any other error Median raises shows its own message. A tab running an old widget build shows This page is out of date. Refresh to get the latest version. Anything else, including a failed upload, shows Something went wrong on our end. Please try again.
Keyboard
| Key | Where | Does |
|---|---|---|
| Enter | Message box | Sends |
| Shift+Enter | Message box | Adds a line |
| Enter | Email field | Saves the address |
| Escape | Anywhere on the page while the panel is open | Closes the panel |
- Enter while an input method is composing picks the candidate and does not send.
- Opening moves focus into the panel, not onto the composer, so a phone does not raise its keyboard. Closing returns focus to the launcher, or to the element that opened the panel.
- The thread is a polite live region. Screen readers announce new replies without moving focus.
Placement and size
| Part | Size |
|---|---|
| Launcher | 3rem circle, 1rem from the edge, bottom right until moved |
| Panel | 28rem wide, 40rem tall |
| Expanded panel | 34rem wide, 52rem tall |
| Viewport 40rem (640px) wide or less | Full screen |
- The panel never grows past the viewport, keeping 1rem clear on each side.
- Expand the panel in the header switches to the expanded size, and Shrink the panel switches back. The choice lasts until the page reloads.
- Visitors can drag the launcher, or the open panel by its header. It snaps to the nearest side and keeps its height as a fraction of the window, so a resize keeps it on screen.
- On a phone, the panel follows the on-screen keyboard, the page behind it does not scroll, and there is no expand control. Only the launcher drags.
- MedianSupportModal opens in the middle of the screen instead. Colors, fonts and motion are on Theming.
Browser storage
<id> is the widget’s median_pk_ id.
localStorage key |
Holds | Cleared by reset() |
|---|---|---|
median:session:<id> |
A random token that names this browser’s visitor | Yes |
median:email:<id> |
The address from the email question or the contact form | Yes |
median:rating-skips:<id> |
The last 20 conversations whose rating card was closed, by Skip or after finishing | No |
median:teaser:<id> |
When the reply on the last dismissed card was sent | No |
median:launcher-spot |
The launcher’s side and height, shared by every widget on the site | No |
- The widget sets no cookies.
- If
localStorageis blocked, each value lasts the page load. A reload then starts a new visitor. reset()removes the session and email keys for every widget id in the browser. See Identity.
Rate limits
| Scope | Counts | Rate | Burst |
|---|---|---|---|
| Organization | Messages, contact form sends, ratings, feedback notes and crash reports | 120 a minute | 30 |
| Organization | New conversations | 30 a minute | 15 |
| Organization | File uploads, one per file | 300 a minute | 60 |
| Organization | Typing, presence and identity checks | 1,200 a minute | 300 |
| One browser | Sends, file uploads and identity checks | 30 a minute | 15 |
Over a limit, the send fails with Too many requests. Wait a moment and try again. above the composer. The draft stays. REST API limits are on Errors and limits.