Cookbook
Agents & skills · for AI agents

Personaliza el chat — colores y logotipo

Adapta la marca de un cliente en un enlace compartido o un widget integrado: un color, un logotipo y CSS solo cuando la guía de marca lo requiera.

MCP tools:list_sharesconfigure_shareset_pwa_brandingset_custom_domain

Principio — proporciona un solo color, no una paleta. Los colores de primer plano se derivan de él al guardar, por lo que todas las combinaciones permanecen legibles. Elegir tus propios colores de texto es cómo envías un botón que nadie puede leer.

La marca reside en la compartición (share), no en el agente: el mismo agente puede ser un widget de marca blanca en un sitio y un enlace sencillo en otro lugar. Así que obtén el token primero.

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",
)

Solo cambian los campos que pasas; el resto de la configuración se deja intacta.

Renombrar una compartición (share)

label es el nombre en la parte superior de la página de chat y el título de la pestaña del navegador. Es un campo en configure_share como cualquier otro:

configure_share(agent_name="mortgage-advisor", token="RuqY…", label="Pip — reading buddy")

Nunca necesitas crear una compartición de reemplazo y eliminar la antigua para renombrarla. Una nueva compartición significa un nuevo token, por lo que el enlace ya incrustado en el sitio del cliente deja de funcionar: el nombre visible y la dirección no son lo mismo, y solo uno de ellos es seguro cambiar después del lanzamiento.

Un segundo color, solo si la marca tiene uno

La mayoría de las marcas tienen un color y no necesitan nada aquí. Algunas tienen un primario y un destacado: pasa el segundo como highlight_color y se utiliza donde algo debe ser visible sin competir con la acción principal:

configure_share(
  agent_name="pip",
  token="ENhs…",
  theme_color="#0B4230",      # primary: buttons, the current storyline step, user bubbles
  highlight_color="#D9A922",  # highlight: second chart series, citation markers
)

Sus colores de primer plano se derivan al guardar exactamente como los del primario, por lo que la misma garantía de legibilidad se mantiene. Déjalo fuera y nada cambia en ninguna parte: --acc-2 vuelve al primario, que es lo que toda marca de un solo color quiere y no requiere configuración.

No lo extiendas manualmente. Puedes sobrescribir nuestras clases de componentes desde custom_css, y lo lamentarás: esos nombres de clase son nuestros internos, cambian, y cuando lo hacen tu marcado se rompe silenciosamente. Establece los dos colores y deja que los componentes decidan dónde pertenece cada uno.

Color: pasa uno, obtén cuatro

theme_color es el color primario de la marca, #RGB o #RRGGBB. Al guardar, la plataforma deriva los primeros planos mediante el contraste WCAG y los almacena:

DerivadoSe usa para
theme_color_fgtexto sobre el color de la marca — botón de enviar, burbujas del usuario, insignia de no leído
theme_color_textel color de la marca usado como texto, sobre un fondo claro
theme_color_text_darklo mismo, sobre un fondo oscuro

Un color de marca claro por lo tanto obtiene texto oscuro sobre él en lugar de blanco. #F7B331 con texto blanco mide 1.83:1 — por debajo incluso del piso de 3:1 para componentes de UI, lo que significa que la etiqueta es genuinamente ilegible; el primer plano oscuro derivado mide 10.21:1.

No calcules una paleta tú mismo ni intentes establecer los colores de texto — se derivan en el servidor y tus valores se sobrescriben. Pasar un solo color es todo el trabajo.

Borrarlo (theme_color="") elimina también los valores derivados y devuelve el widget al azul por defecto. Dejarlo sin establecer hace lo mismo: los tokens integrados ya son un par combinado.

Logotipo: la relación de aspecto decide dónde se puede usar

Pasa una URL absoluta. El logotipo aparece en dos lugares con diferentes restricciones:

  • Barra de encabezado — alineado en altura, ancho libre. Cualquier relación de aspecto funciona, incluyendo un logotipo de palabra ancho.
  • Burbuja colapsada — cuadrada, porque el logotipo es la forma de la burbuja. Solo se utilizan relaciones entre 0.74 y 1.35 aquí; un logotipo de palabra ancho vuelve al icono de la plataforma en lugar de ser aplastado en una tira demasiado pequeña para leer.

Así que para un logotipo de palabra ancho, la respuesta práctica es dos archivos: el logotipo de palabra para el encabezado, una marca cuadrada para la burbuja. Si el cliente solo tiene un logotipo de palabra, el encabezado muestra su marca y la burbuja muestra un icono neutro — mejor que una franja ilegible.

No recortes un logotipo de palabra a cuadrado. El recorte deja la mitad de la palabra, que ya no es su marca registrada.

Cuando la guía de marca sobrescribe todo

Algunos clientes tienen una paleta estricta que no seguirá la nuestra en ninguno de los temas. custom_css se inyecta después de las variables de marca, por lo que una declaración sencilla ya gana — no se necesita !important. (Antes era necesario, porque los colores se establecían como estilos en línea. Ya no lo son.)

⚠️ Lee esto antes de escribir cualquier CSS

Debes escribir dos bloques: :root para claro, html[data-theme="dark"] para oscuro.

:root coincide en ambos temas y supera las reglas oscuras de la plataforma (misma especificidad, y la tuya viene después). Así que una paleta escrita solo bajo :root significa que el cambio claro/oscuro del visitante no cambia nada en absolutodata-theme cambia, pero no sigue ninguna variable.

Esto ya ha ocurrido en producción: un agente puso una paleta oscura completa bajo :root, la página se veía genial, y el botón de tema se convirtió en un control muerto que nadie notó durante semanas. La página se ve correcta, que es exactamente por lo que no se reporta.

Si el cliente realmente quiere un solo tema, establece el campo theme de la compartición en light o dark en su lugar. Eso oculta el botón de alternancia en lugar de romperlo.

Forma correcta — copia esto y reemplaza los valores:

/* 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;
}

Este es el conjunto completo que vale la pena sobrescribir: el widget consume pocos colores a propósito.

¿Qué variables necesitan el bloque oscuro? Las que describen la superficie, porque se invierten entre temas: --bg, --surface, --text, --border. --acc es el color de la marca y normalmente permanece igual en ambos, aunque el ejemplo lo aclara para el tema oscuro porque un púrpura profundo sobre un fondo casi negro es difícil de ver.

Dos cosas más que tener en cuenta:

  • custom_css no ofrece garantía de legibilidad. theme_color sí. Recurre a CSS solo para lo que la derivación no puede expresar, y mantén theme_color establecido como línea base.
  • Establecer --acc-text-l y --acc-text-d con el mismo valor anula su propósito. Existen precisamente para que el color de la marca sea legible como texto contra fondos opuestos.

configure_share devuelve un array warnings cuando detecta el error de un solo bloque :rootléelo. La consola muestra la misma advertencia junto al editor de CSS.

css_url carga una hoja de estilos externa, para clientes que mantienen su propio archivo de tema.

Los controles interactivos que impulsan tus colores

Cada control de color de acento en el chat lee el mismo par — fondo --acc, texto --acc-fg:

ControlSelector (estable)
Botón de envío del formulario (los formularios que los agentes renderizan con bloques ```ask).ca-form .casub
Filas de opciones del formulario Ask.ca-form label (al pasar el ratón: :hover)
Fila seleccionada del formulario Ask.ca-form label:has(input:checked)
Radio/checkbox del formulario Ask en sí.ca-form input (vía accent-color)
Botón de enviar / burbujas del usuario / insignia de no leído.composer .send, .m.user .content, .sbadge
Botones de preguntas rompedoras de hielo (al pasar el ratón).qs button
Botón de hablar para hablar mientras graba — fondo, etiqueta y el resplandor pulsante.ca-talk.rec
Botón de instalación de PWA.pwabar .go, .pwacard .go

Las filas seleccionadas vienen estilizadas de serie: borde de color de marca, un 10% de matiz de marca detrás de la fila, y el control nativo pintado con accent-color — todo impulsado por --acc, por lo que establecer theme_color es de nuevo todo el trabajo. El resplandor de grabación del botón de voz también se deriva de --acc (vía color-mix al 22%/55% de alfa), por lo que pulsa automáticamente en el color de la marca; sobrescríbelo en custom_css solo para un tratamiento deliberadamente diferente:

/* softer, wider recording glow — both themes if you also touch surfaces */
:root .ca-talk.rec{
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--acc) 15%, transparent),
              0 0 40px color-mix(in srgb, var(--acc) 40%, transparent);
}

Recurre a CSS solo cuando la guía de marca exige un tratamiento diferente:

/* stronger selected state, both themes (remember the two-block rule above) */
:root .ca-form label:has(input:checked){
  border-color: var(--acc);
  background: color-mix(in srgb, var(--acc) 18%, transparent);
  font-weight: 600;
}
html[data-theme="dark"] .ca-form label:has(input:checked){
  background: color-mix(in srgb, var(--acc) 24%, transparent);
}

La regla que los mantiene todos legibles: cualquiera que pintes con --acc obtiene su texto de --acc-fg — nunca un color literal. El fallo clásico es sobrescribir --acc a un amarillo de marca claro en el bloque oscuro y dejar el texto blanco: el botón del formulario se convierte en texto amarillo sobre blanco a 1.8:1 y el cliente informa de que "el botón del formulario es ilegible en modo oscuro". Si sobrescribes --acc en custom_css, posees --acc-fg en el mismo bloque, ambos temas — o mejor, no uses CSS para esto en absoluto: establece theme_color y la plataforma deriva un --acc-fg conforme para ti.

Hacer que el chat sea instalable (PWA)

Las páginas de chat independientes (enlaces /s/…, códigos QR y las URL de alias a continuación) son aplicaciones instalables: los visitantes pueden añadirlas a su pantalla de inicio, con tu propio icono y nombre. Dos perillas, configuradas por agente — lo que se instala en la pantalla de inicio es la página de entrada de un agente, por lo que cada agente es su propia aplicación con su propio icono (un agente de soporte y un agente de ventas preinstaladas como dos aplicaciones diferentes):

set_pwa_branding(
  agent="support",                                                # which agent's install branding
  icon_source_url="https://cdn.example.com/brand/mark-1024.png",  # ONE square master image ≥192×192
  install_prompt="banner",                                        # banner (default) | card | off
)
  • La imagen maestra se obtiene una vez y la plataforma genera todo el conjunto — favicon de la pestaña del navegador, iconos de instalación 192/512 y la variante enmascarada de Android. Los maestros no cuadrados se recortan al centro, así que usa la marca cuadrada mark, no un logotipo de palabra ancho.
  • install_prompt controla lo que ven los visitantes en la página de chat: banner es una barra delgada desechable (Android/Chrome activa el diálogo de instalación del sistema; iOS obtiene un recorrido "Compartir → Añadir a la pantalla de inicio" ya que Apple no ofrece API), card es una tarjeta de primera visita más visible, off desactiva la invitación. Los descartes se recuerdan por visitante.
  • Los iconos son opcionales — sin ellos se utilizan los iconos de la plataforma. Llama solo con el nombre del agente para leer la configuración actual de ese agente.

El enlace que entregas a un humano

Cuando el alias del inquilino y el alias del agente están establecidos, create_share / list_shares devuelven un pretty_urlhttps://chat.agent4.io/t/<tenant-alias>/<agent-alias>. Ese es el enlace para dar a las personas e imprimir en el material: legible, memorable, y sobrevive a la rotación de tokens (la plataforma mantiene perezosamente una compartición detrás de él). La forma /s/<token> se queda para incrustaciones y máquinas.

¿No hay pretty_url en la respuesta? Los aliases no están establecidos — corrige eso en lugar de enviar el enlace de token: alias del agente vía create_agent(alias=…) o PUT /agents/{name}/alias; el alias del inquilino vive en la consola → Configuración. Establecer el alias de un agente es en sí una acción de publicación: la página de alias auto-crea una compartición anónima en la primera visita.

El propio dominio del cliente

La página de chat alojada puede servir en el dominio del cliente — https://chat.client.com/ muestra su página con marca, certificado emitido automáticamente.

set_custom_domain(domain="chat.client.com", agent_alias="menu")
# → { status: "pending", cname_target: "endpoint.agent4.io", last_error: "…" }

La secuencia importa, y es asíncrona:

  1. Dile al cliente que añada un CNAME desde su subdominio al cname_target en la respuesta (endpoint.agent4.io). Solo subdominios — un dominio raíz (client.com) no puede tomar un CNAME; haz que usen chat.client.com, o un proveedor de DNS con aplanamiento de CNAME.
  2. En Cloudflare el registro debe ser solo DNS (nube gris). Los registros proxy se resuelven a las IPs de Cloudflare y la verificación falla — last_error lo dice explícitamente, con las direcciones observadas. Este es el fallo más común; lee last_error antes de adivinar.
  3. La verificación se vuelve a ejecutar automáticamente cada 10 minutos; llama a set_custom_domain de nuevo (mismos argumentos) o comprueba tenant_info() para ver el estado actual. active significa en vivo — la primera visita emite el certificado.

Un dominio por espacio de trabajo. agent_alias vacío aterriza en la página con marca del inquilino; establécelo para aterrizar directamente en el chat de un agente. Los enlaces existentes chat.agent4.io siguen funcionando — esto es una entrada adicional, no un reemplazo — y la identidad del chat es por dominio: el historial de un visitante en chat.agent4.io no los sigue al dominio personalizado.

Requiere un plan que incluya dominios personalizados (403 = el inquilino necesita actualizar).

El modal de búsqueda de conversaciones (Spotlight)

La página independiente y el cliente web tienen una búsqueda de conversaciones ⌘K. Sigue la marca automáticamente — el modal lee las mismas variables que todo lo demás (--surface, --border, --text, --muted, --overlay, --surface-hover, --accent-soft, y el par --acc-text para coincidencias resaltadas) — así que establecer theme_color es ya todo el trabajo aquí también.

Para guías de marca que necesitan más, los nombres de clase son estables y custom_css supera los estilos propios del componente:

ParteSelector
Fondo.ca-ss-mask
El panel.ca-ss
Entrada de consulta.ca-ss input
Fila de resultado / fila seleccionada.ca-ss-item / .ca-ss-item.sel
Título del resultado / fragmento.ca-ss-t / .ca-ss-s
Coincidencia resaltada en un fragmento.ca-ss-s mark
Insignia "relacionado" (coincidencias semánticas).ca-ss-sem
Botón de búsqueda de la barra lateral.sb-search
/* rounder panel, brand-tinted selected row — remember the two-block rule above */
:root{ }
.ca-ss{ border-radius: 20px; }
.ca-ss-item.sel{ background: color-mix(in srgb, var(--acc) 10%, transparent); }
html[data-theme="dark"] .ca-ss-item.sel{ background: color-mix(in srgb, var(--acc) 16%, transparent); }

Como en todas partes: la regla de dos temas se aplica en el momento en que tocas colores de superficie, y las declaraciones simples ganan — no !important.

Controles de formulario y el selector de fecha nativo

Las entradas del formulario Ask (.ca-form input.cain) y la entrada del diálogo (.ca-in) están estilizadas con tokens — superficie, texto, borde, y un anillo de enfoque de acento siguen todos el tema y tu theme_color. Los diálogos del sistema (renombrar/confirmar) comparten el tratamiento de Spotlight: fondo esmerilado, elevación respirante, resplandor de marca en oscuro.

El calendario es híbrido. En escritorio (ratón) la plataforma dibuja su propio calendario — completamente estilizado con tokens, día seleccionado de color de marca, nombres de mes/día de la semana localizados — selectores estables .ca-dp, .ca-dp-d, .ca-dp-d.sel, .ca-dp-d.today si una guía de marca necesita más. En dispositivos táctiles el nativo el selector del SO se mantiene deliberadamente: la rueda móvil supera a cualquier cosa dibujada en una página web, y su panel es UI privada del SO que CSS no puede alcanzar — allí la plataforma controla lo que es alcanzable (esquema de color fijo al tema, el campo de entrada estilizado con tokens, el icono de calendario invertido) y nada más existe para controlar.

Alinea tus selectores de elementos. Un input[type="text"] { … } escrito para el compositor también captura los diálogos y campos de formulario de la plataforma — un estilo de compositor blanco que se filtra en un diálogo de renombrado oscuro es el síntoma clásico, y el lint de CSS ahora lo marca (broad_element_selector). Escribe .composer textarea o #input en su lugar. Los diálogos del sistema están a mayor especificidad, así que el accidente ya no aterriza — pero el lint te lo dice antes de que confíes en un accidente.

También en la compartición (share)

  • input_modetext (por defecto) o voice, que abre directamente en hablar para hablar. Necesita un backend de voz a texto habilitado en la plataforma.
  • launcher — el tooltip de hover de la burbuja.
  • theme — fuerza light / dark, o déjalo vacío para seguir la configuración del sistema del visitante. Prefiere vacío: el widget se encuentra dentro de la página del cliente y debe coincidir con ella.

Verificar

Abre el chat_url de list_shares y mira el botón de enviar. Si la etiqueta es difícil de leer, theme_color no está establecido y algo lo está sobrescribiendo — comprueba custom_css por un color codificado.