Brand the chat — colours and logo
Match a customer's brand on a share link or embedded widget: one colour, a logo, and CSS only when the brand guide demands it.
list_sharesconfigure_sharePrinciple — give one colour, not a palette. Foreground colours are derived from it on save, so every combination stays readable. Picking your own text colours is how you ship a button nobody can read.
Branding lives on the share, not the agent: the same agent can be a white-label widget on one site and a plain link somewhere else. So get the token first.
list_shares(agent_name="mortgage-advisor")
# → [{ token: "RuqY…", label: "Website widget", config: { theme_color: "#F7B331", … } }]
configure_share(
agent_name="mortgage-advisor",
token="RuqY…",
theme_color="#F7B331",
logo_url="https://cdn.example.com/brand/mark-512.png",
)Only the fields you pass change — the rest of the configuration is left alone.
Colour: pass one, get four
theme_color is the brand's primary colour, #RGB or #RRGGBB. On save the platform derives the
foregrounds by WCAG contrast and stores them:
| Derived | Used for |
|---|---|
theme_color_fg | text on the brand colour — send button, user bubbles, unread badge |
theme_color_text | the brand colour used as text, on a light background |
theme_color_text_dark | the same, on a dark background |
A light brand colour therefore gets dark text on it rather than white. #F7B331 with white text
measures 1.83:1 — below even the 3:1 floor for UI components, meaning the label is genuinely
unreadable; the derived dark foreground measures 10.21:1.
Do not compute a palette yourself and do not try to set the text colours — they are server-derived and your values are overwritten. Passing one colour is the whole job.
Clearing it (theme_color="") drops the derived values too and returns the widget to the default
blue. Leaving it unset does the same — the built-in tokens are already a matched pair.
Logo: aspect ratio decides where it can be used
Pass an absolute URL. The logo appears in two places with different constraints:
- Header bar — height-aligned, width free. Any aspect ratio works, including a wide wordmark.
- Collapsed bubble — square, because the logo is the shape of the bubble. Only ratios between 0.74 and 1.35 are used here; a wide wordmark falls back to the platform icon instead of being squeezed into a strip too small to read.
So for a wide wordmark, the practical answer is two files: the wordmark for the header, a square mark for the bubble. If the customer only has a wordmark, the header still shows their brand and the bubble shows a neutral icon — better than an illegible sliver.
Do not crop a wordmark to square. Cropping leaves half a word, which is no longer their trademark.
When the brand guide overrides everything
Some customers have a strict palette that will not follow ours in either theme. custom_css is
injected after the brand variables, so a plain declaration already wins — no !important
needed. (It used to be required, because the colours were set as inline styles. They are not any
more.)
⚠️ Read this before writing any CSS
You must write two blocks:
:rootfor light,html[data-theme="dark"]for dark.
:rootmatches in both themes and outranks the platform's dark rules (same specificity, and yours comes later). So a palette written only under:rootmeans the visitor's light/dark toggle changes nothing at all —data-themeflips, no variable follows it.This has already happened in production: an agent put a full dark palette under
:root, the page looked great, and the theme button became a dead control that nobody noticed for weeks. The page looks correct, which is exactly why this goes unreported.If the customer genuinely wants one theme only, set the share's
themefield tolightordarkinstead. That hides the toggle rather than breaking it.
Correct shape — copy this and replace the values:
/* LIGHT — the light values go here, not the dark ones */
:root{
--acc: #6b21a8; /* brand primary — backgrounds, focus rings */
--acc-fg: #ffffff; /* text on --acc: YOU own the contrast here */
--acc-text-l: #581c87; /* brand colour used as text, light theme */
--bg: #fdfbff;
--surface: #ffffff;
--text: #1e1b2e;
--border: #e6e0f0;
}
/* DARK — required whenever you touched --bg / --surface / --text above */
html[data-theme="dark"]{
--acc: #a855f7;
--acc-fg: #1a0b2e;
--acc-text-d: #c084fc; /* note: -d, the dark-theme variant */
--bg: #120c1d;
--surface: #1c142b;
--text: #f0e9ff;
--border: #32254a;
}That is the full set worth overriding — the widget consumes few colours on purpose.
Which variables need the dark block? The ones that describe the surface, because they invert
between themes: --bg, --surface, --text, --border. --acc is the brand colour and normally
stays the same in both, though the example brightens it for the dark theme because a deep purple on
a near-black background is hard to see.
Two more things to hold on to:
custom_csscarries no readability guarantee.theme_colordoes. Reach for CSS only for what the derivation cannot express, and keeptheme_colorset as the baseline.- Setting
--acc-text-land--acc-text-dto the same value defeats their purpose. They exist precisely so the brand colour can be readable as text against opposite backgrounds.
configure_share returns a warnings array when it detects the single-:root mistake — read it.
The console shows the same warning next to the CSS editor.
css_url loads an external stylesheet instead, for customers who keep their own theme file.
Also on the share
input_mode—text(default) orvoice, which opens straight into hold-to-talk. Needs a speech-to-text backend enabled on the platform.launcher— the bubble's hover tooltip.theme— forcelight/dark, or leave empty to follow the visitor's system setting. Prefer empty: the widget sits inside the customer's page and should match it.
Verify
Open the chat_url from list_shares and look at the send button. If the label is hard to read,
theme_color is not set and something is overriding it — check custom_css for a hard-coded
color.