Branding
A brand is { name, logo, skin }: your organization’s display name, a logo, and the
color/shape skin. Branding is tenant-first: set the tenant brand with
PUT /v1/branding (branding:manage); every user sees it. A tenant may grant individual
users override permission (canOverrideBranding on the user record); a permitted user
can then set a personal brand for their own account via PUT /v1/branding/me (session
auth). Resolution is per-field: user → tenant → platform default (the skin always
resolves as a whole).
// PUT /v1/branding (tenant-wide)
{
"name": "Acme Growers",
"logo": {
"light": "https://cdn.acme.com/logo.png", // https URL…
"dark": "data:image/png;base64,…" // …or an uploaded data-URL (≤ 300 KB)
},
"skin": { /* see below */ }
}GET /v1/branding/theme returns the resolved brand for the caller (name, logo, and
the merge-ready MUI fragment), so the GUI shows each user exactly their view.
A skin puts the Growth Lab GUI in your brand: your colors for the light and dark themes, plus optional corner-radius overrides for the GUI’s surface categories. A skin is a minimal Material UI theme fragment. The GUI fetches it at session init and merges it over the platform default, so everything (buttons, charts, alerts, tiles) picks up your palette in both modes automatically.
Set your skin
PUT /v1/branding/skin with an API key holding the branding:manage scope. Both light and
dark are required (your skin must work in both modes); shape is optional.
{
"light": {
"palette": {
"primary": { "main": "#0055aa" },
"secondary": { "main": "#ffaa00", "contrastText": "#000000" },
"background": { "default": "#fafafa", "paper": "#ffffff" }
}
},
"dark": {
"palette": {
"primary": { "main": "#66aaff" },
"background": { "default": "#101418", "paper": "#181d23" }
}
},
"shape": { "panel": 16, "pane": 12, "widget": 4 }
}Palette: the allowed keys per scheme (anything else is rejected):
primary, secondary, error, warning, info, success (each { main, light?, dark?, contrastText? }), background { default?, paper? }, text { primary?, secondary?, disabled? }, and divider. Colors are hex only (#rgb, #rrggbb, #rrggbbaa).
Omit a key and the platform default applies; Material UI derives hover/shade variants from
main unless you set them.
Shape: border-radius overrides in px (0–48) for the three surface categories:
| Token | Applies to |
|---|---|
panel | Top-level surface cards (workspace, dialogs, sign-in, loading overlay) |
pane | Content areas inside a panel or tab (tile grid, Sources list/map) |
widget | Tiles and small cards |
Omitted tokens keep the GUI’s default geometry.
GET /v1/branding/skin reads back exactly what you stored; DELETE /v1/branding/skin
returns your tenant to the platform default.
How the GUI consumes it
GET /v1/branding/theme (session cookie or any tenant key) returns the resolved brand
for the caller:
{
"name": "Acme Growers",
"logo": { "light": "https://…", "dark": "data:image/png;base64,…" },
"colorSchemes": {
"light": { "palette": { /* your light palette */ } },
"dark": { "palette": { /* your dark palette */ } }
},
"shape": { "panel": 16, "pane": 12, "widget": 4 } // null values = no override
}With no skin set, the palettes are empty and every radius is null, so clients can always
deep-merge the response into a base theme without special-casing. name / logo are
null when neither the user nor the tenant set them.
The Growth Lab GUI:
- Merges
colorSchemesandshapeover the platform theme at session init - Shows
nameand the mode-matchinglogoin the workspace chrome - Lets a user with
canOverrideBrandingedit their personal brand from Settings → Account Settings (PUT /v1/branding/me). Blank fields fall through to the tenant brand; Use tenant brand clears the personal override (DELETE /v1/branding/me)
The two palettes are used as:
| Token | Role |
|---|---|
primary | Brand chrome and main actions (tabs, Next / Create / Save, stepper) |
secondary | Accent: selected tiles, outlined supporting actions (Back, Upload, Run, History), and form controls (switches, radios, checkboxes, sliders) |
Source markers keep their own hint colors; the selected-on-map ring is a fixed high-contrast
red so it still reads against an orange sensor hint.
Resolution is per-field: a permitted user’s personal brand overlays the tenant brand for their session only (name, logo, and the skin as a whole). Other users of the tenant are unchanged. Light/dark mode (user preference or your host page’s hint) selects which palette and which logo apply.