Guía para desarrolladores
API de identificación de visitantes
Identifica por código a los usuarios con sesión iniciada para que nunca vean la petición de correo y sus conversaciones se vinculen automáticamente a un contacto.
Cuándo usarla
Si tu sitio web tiene usuarios con sesión iniciada (aplicaciones SaaS, paneles, portales de socios), ya sabes quiénes son. Usa el comando identify para pasar su correo a Sonny y que no tengan que volver a escribirlo.
- Omite por completo la petición de correo para los usuarios con sesión iniciada
- Vincula automáticamente las conversaciones de chat a su ficha de contacto
- Ve al instante el nombre y el correo del visitante en tu bandeja de entrada
- Continúa el historial en cualquier navegador y dispositivo con la identidad verificada
Continuidad entre dispositivos
Identidad de visitante verificada
La identificación básica por correo es cómoda, pero el navegador puede declarar cualquier correo. La identidad verificada añade un JWT de corta duración firmado por tu servidor. Así, Sonny puede usar con seguridad el contacto del cliente como titular de su historial de chat en tu web, para que las mismas conversaciones aparezcan en otro navegador o dispositivo.
Desactivado
El valor predeterminado. No cambia nada para las integraciones ni los visitantes existentes; el historial sigue ligado al ID de visitante del navegador.
Verificación opcional (recomendado)
Los JWT válidos obtienen historial entre dispositivos. Los visitantes sin JWT mantienen la experiencia actual anónima o con identificación por correo.
Exigir prueba para identificar
El chat anónimo sigue funcionando, pero las llamadas de identificación y de atributos personalizados requieren un JWT firmado válido.
El modo de verificación no hace opcionales los campos previos al chat obligatorios. Consulta cómo cumple cada modo un correo obligatorio en datos obligatorios previos al chat.
1. Genera un secreto de firma
Abre Canales → tu canal → Chat en vivo, busca Identidad de visitante segura, elige un modo y genera un secreto. El texto sin cifrar se muestra una sola vez. Guárdalo en el gestor de secretos de tu backend como SONNY_IDENTITY_SECRET. Nunca pongas el secreto de firma en código del navegador, en un fragmento de integración, en una variable de entorno pública ni en tu repositorio de código.
2. Genera un JWT de corta duración en tu backend
Firma con HS256. Sonny requiere user_id, email, iat y exp. El user_id debe ser un ID estable de tu propia base de datos, no una dirección de correo. Los tokens pueden durar como máximo 24 horas; 15 minutos es un buen valor por defecto. Los relojes pueden diferir hasta 60 segundos.
También puedes incluir un objeto traits firmado en el servidor con contexto de soporte de confianza, como merchantName, subdomain, plan, platform y role. Los valores de los atributos deben ser cadenas, números finitos, booleanos o null. Sonny los guarda en la identidad de contacto verificada del sitio web. Los atributos que facilita el navegador siguen sin verificar y nunca se convierten en estos atributos firmados.
import { SignJWT } from 'jose';
export async function createSonnyIdentityToken(user) {
const secret = new TextEncoder().encode(process.env.SONNY_IDENTITY_SECRET);
return new SignJWT({
user_id: String(user.id),
email: user.email,
traits: {
merchantName: user.merchant.name,
subdomain: user.merchant.subdomain,
plan: user.merchant.plan,
platform: user.merchant.platform,
role: user.role
}
})
.setProtectedHeader({ alg: 'HS256' })
.setIssuedAt()
.setExpirationTime('15m')
.sign(secret);
}require 'jwt'
def sonny_identity_token(user)
now = Time.now.to_i
payload = {
user_id: user.id.to_s,
email: user.email,
traits: {
merchantName: user.merchant.name,
subdomain: user.merchant.subdomain,
plan: user.merchant.plan,
platform: user.merchant.platform,
role: user.role
},
iat: now,
exp: now + 15 * 60
}
JWT.encode(payload, ENV.fetch('SONNY_IDENTITY_SECRET'), 'HS256')
endimport os
from datetime import datetime, timedelta, timezone
import jwt
def sonny_identity_token(user):
now = datetime.now(timezone.utc)
return jwt.encode({
"user_id": str(user.id),
"email": user.email,
"traits": {
"merchantName": user.merchant.name,
"subdomain": user.merchant.subdomain,
"plan": user.merchant.plan,
"platform": user.merchant.platform,
"role": user.role,
},
"iat": now,
"exp": now + timedelta(minutes=15),
}, os.environ["SONNY_IDENTITY_SECRET"], algorithm="HS256")use Firebase\JWT\JWT;
function sonnyIdentityToken($user): string {
$now = time();
return JWT::encode([
'user_id' => (string) $user->id,
'email' => $user->email,
'traits' => [
'merchantName' => $user->merchant->name,
'subdomain' => $user->merchant->subdomain,
'plan' => $user->merchant->plan,
'platform' => $user->merchant->platform,
'role' => $user->role,
],
'iat' => $now,
'exp' => $now + (15 * 60),
], $_ENV['SONNY_IDENTITY_SECRET'], 'HS256');
}3. Pasa solo el JWT al widget
Devuelve el JWT desde un endpoint protegido por la sesión normal de tu aplicación. Llama a identify antes o después de init. El widget guarda el JWT solo en memoria: nunca se guarda en localStorage, en cookies ni en una URL.
// Fetch a short-lived JWT from your authenticated backend.
// Your signing secret never reaches this code.
const { userJwt } = await fetch('/api/sonny-identity').then((res) => res.json());
sonny('identify', { userJwt });
sonny('init', { siteId: 'YOUR_SITE_ID' });
// Sonny asks for a fresh token after expiry or an emergency key change.
window.addEventListener('sonny:identity-required', async () => {
const { userJwt } = await fetch('/api/sonny-identity').then((res) => res.json());
sonny('identify', { userJwt });
});Renueva, cierra sesión y rota con seguridad
- Renovar: renueva el JWT antes de que caduque (a los 10 minutos aproximadamente en un token de 15 minutos) y llama a
sonny('identify', { userJwt }). Escucha tambiénsonny:identity-requiredcomo alternativa ante una caducidad o un cambio de la clave de firma. - Cerrar sesión: llama a
sonny('reset'). Esto descarta el JWT en memoria, borra la sesión del widget en el navegador e inicia un nuevo visitante anónimo. - Cambio de cuenta: llama a
sonny('reset')para el usuario A antes de obtener el JWT del usuario B y llamar asonny('identify', { userJwt }). Nunca lleves el token de un usuario a otra sesión iniciada. - Rotación rutinaria: Sonny acepta el secreto anterior durante 24 horas, lo que te da tiempo para actualizar todas las instancias de tu backend. No se puede hacer una segunda rotación rutinaria hasta que termine ese periodo de solapamiento.
- Sospecha de filtración del secreto: elige Reemplazar de inmediato. Las dos claves de firma antiguas dejan de autenticar peticiones nuevas al instante; los widgets conectados piden un JWT nuevo a la página anfitriona.
- Desactivar la verificación: Sonny descarta los secretos de firma actual y anterior y desconecta los widgets verificados. Genera un secreto nuevo antes de volver a activar la verificación.
Los conflictos de identidad no se fusionan automáticamente
Si un ID de usuario estable y un correo apuntan a contactos distintos de Sonny, la verificación devuelve un conflicto en lugar de combinar las fichas de clientes en silencio. Corrige el token o fusiona los contactos en Sonny y vuelve a intentarlo.
Un chat vinculado solo mediante un correo anterior sin firmar no se trata automáticamente como historial verificado. Así se evita que un correo facilitado por el navegador desbloquee las conversaciones de otro cliente.
Inicio de sesión en el centro de ayuda
Conecta el inicio de sesión de clientes que ya tienes a las páginas de ayuda alojadas o con dominio personalizado. Usa el mismo secreto de firma del sitio web y el mismo JWT de corta duración que el widget. La identificación básica por correo y el acceso del equipo de Sonny no conceden acceso de lector verificado.
- Completa antes la configuración de la identidad de visitante verificada de este canal. Activa Verificación opcional o Exigir prueba para identificar en Canales → tu canal → Chat en vivo.
- Implementa en tu aplicación un endpoint autenticado que devuelva
{ "userJwt": "SIGNED_TOKEN" }. Genera el token en tu servidor a partir del cliente con sesión iniciada y de atributos de confianza; nunca aceptes un plan o un rol solicitado desde el navegador. - En Canales → tu canal → Centro de ayuda → Acceso al centro de ayuda, elige Clientes verificados y, si quieres, una Audiencia de clientes. Introduce la página de inicio de sesión de tu aplicación como URL de inicio de sesión de clientes y selecciona Guardar acceso.
- Cuando el cliente inicie sesión, ejecuta el intercambio de abajo, sustituyendo YOUR_SLUG por la Dirección del centro de ayuda. La URL de inicio de sesión por sí sola no basta: tu aplicación debe completar este intercambio.
// Run after your application authenticates the customer.
// This endpoint must use the server's session and trusted customer traits.
const response = await fetch('/api/sonny-identity');
if (!response.ok) throw new Error('Unable to sign in to the help center');
const { userJwt } = await response.json();
const signIn = new URL('https://www.usesonny.com/help/YOUR_SLUG/auth');
signIn.searchParams.set('token', userJwt);
signIn.searchParams.set('return', '/help/YOUR_SLUG');
window.location.assign(signIn);Para un dominio personalizado, pon signIn en https://help.example.com/auth y vuelve a / o a /articles/getting-started. Las rutas de retorno alojadas empiezan por /help/YOUR_SLUG. Los destinos de retorno deben estar en el mismo host.
El intercambio crea una cookie segura y HTTP-only y redirige para quitar el token de la URL. La sesión del lector caduca con el JWT, hasta un máximo de 24 horas. Usa tokens de corta duración y evita registrar la URL del intercambio. El JWT es solo para este intercambio de inicio de sesión, no para enlaces de artículos compartidos.
Prueba el inicio de sesión y el acceso
- Visita un centro privado sin sesión. Sigue su botón de inicio de sesión, entra en tu aplicación y confirma que vuelves al centro de ayuda en el mismo dominio alojado o personalizado.
- Comprueba un artículo permitido y otro denegado en la navegación, en la búsqueda y por URL directa. Un cliente fuera de la audiencia del centro ve un mensaje de acceso; un artículo denegado devuelve «no encontrado».
- Usa Cerrar sesión en el centro de ayuda y comprueba que el contenido privado desaparece. Repite la prueba con un cliente que tenga otros atributos.
Renovar y cerrar sesión
El widget envía automáticamente su JWT actual. Renuévalo cuando Sonny lo pida y llama a sonny('reset') cuando tu cliente cierre sesión. La identidad del widget y la cookie del centro de ayuda son independientes: reiniciar el widget no cierra una sesión del centro de ayuda alojado.
Para cerrar sesión en las páginas alojadas, navega a /help/YOUR_SLUG/auth/logout; en tu dominio personalizado, usa /auth/logout. Conecta esa acción al flujo de cierre de sesión de tu aplicación si necesitas terminar ambas sesiones. Las credenciales no válidas o caducadas tienen acceso de visitante. Con la verificación de identidad desactivada, todos los lectores tienen acceso de visitante.
Cuando cambien los atributos de un cliente, genera un JWT nuevo y vuelve a identificarlo en el widget; repite el intercambio de inicio de sesión para las páginas alojadas. Las cookies de lector existentes conservan los atributos firmados anteriores hasta que caducan. Los cambios en las reglas de audiencia se aplican en la siguiente petición.
Crea audiencias, previsualiza el acceso y resuelve problemas de restriccionesIdentificar por correo no es autenticar
El correo que facilita el navegador mejora el contexto del soporte, pero no autentica al visitante. Un visitante puede inspeccionar y ejecutar JavaScript en su propia página, así que el correo, el nombre y los atributos personalizados nunca desbloquean el historial de otro cliente en Sonny. La identidad verificada requiere el JWT firmado en el servidor que se describe arriba. Mantén la autorización de las acciones de tu propio producto dentro de tu aplicación con sesión iniciada.
Cuando la verificación de visitantes está activada, una insignia Verificado en la bandeja de entrada significa que la conversación se vinculó con un JWT firmado en el servidor. Una insignia Sin verificar significa que ninguna identidad firmada en el servidor autenticó la conversación. Cualquier nombre o correo que se muestre es contexto de soporte facilitado por el visitante, no una prueba de identidad.
Ejemplos de código
La llamada más sencilla: solo pasa el correo del usuario:
// Identify a logged-in user
sonny('identify', {
email: 'jane@example.com'
});Pasa también el nombre del usuario para que los agentes lo vean en la bandeja de entrada:
// Identify with full name
sonny('identify', {
email: 'jane@example.com',
name: 'Jane Smith'
});Añade contexto útil para el soporte durante la identificación, actualízalo más tarde o vigila un valor que cambie mientras la página está abierta:
// Identify with support context
sonny('identify', {
email: 'jane@example.com',
name: 'Jane Smith',
attributes: {
plan: 'starter',
seats: 5,
trial: true
}
});
// Update only the values that changed
sonny('setAttributes', {
plan: 'growth',
seats: 8,
trial: null // Clears this property
});
// Keep a changing value in sync (checks every 10 seconds)
sonny('watchAttributes', () => ({
monthly_usage: window.currentUsage
}), { interval: 10000 });Ejemplo completo con el fragmento asíncrono. Ten en cuenta que identify se puede llamar antes de init: la identidad queda en cola y se envía en cuanto el widget se conecta:
<!-- Sonny widget snippet -->
<script>
(function(w,d,s,o,f,js,fjs){
w['Sonny']=o;w[o]=w[o]||function(){
(w[o].q=w[o].q||[]).push(arguments)};
js=d.createElement(s);fjs=d.getElementsByTagName(s)[0];
js.id=o;js.src=f;js.async=1;fjs.parentNode.insertBefore(js,fjs);
})(window,document,'script','sonny','https://www.usesonny.com/widget.js');
// Identify before init — identity is queued and sent on connect
sonny('identify', {
email: 'jane@example.com',
name: 'Jane Smith'
});
sonny('init', { siteId: 'YOUR_SITE_ID' });
</script>Llama a reset cuando el usuario cierre sesión para borrar su identidad e iniciar una sesión nueva:
// Reset on logout — clears identity and starts a fresh session
sonny('reset');Cómo funciona
- 01
El widget se carga y se conecta
El widget se conecta a Sonny y envía la identidad guardada, si la hay, al unirse.
- 02
La identidad se envía al servidor
Sonny busca un contacto con ese correo en tu espacio de trabajo y lo crea si aún no existe.
- 03
Las conversaciones se vinculan
Las conversaciones sin vincular de la sesión de visitante del navegador actual se vinculan al contacto identificado. La identificación no fusiona el historial entre navegadores o dispositivos.
- 04
Se omite la petición de correo
Como el visitante ya está identificado, no se muestra la petición de correo del widget: ninguna interrupción para el usuario.
Reglas de los atributos personalizados
- Envía como máximo 50 atributos por llamada.
- Un valor puede ser una cadena, un número, un booleano o null. Las cadenas tienen un límite de 1000 caracteres. Pasa null o una cadena vacía para borrar un valor guardado.
- Las claves deben empezar por una letra y contener solo letras, números y guiones bajos, con un máximo de 64 caracteres.
- Estas claves están reservadas y se ignoran:
email, name, id, phone, createdAt, updatedAt.
Referencia de la API
sonny('identify', { email, name?, attributes? })- Identifica al visitante actual. Define el correo y, opcionalmente, el nombre, omite la petición de correo y envía la identidad al servidor. Se puede llamar antes o después de init.
emailstringDirección de correo del visitantenamestring?Nombre visible del visitanteattributesobject?Atributos personalizados que añadir al contacto (consulta la guía de configuración del widget para ver las reglas de claves y valores)
sonny('identify', { userJwt, name?, attributes? })- Identifica de forma segura al cliente con sesión iniciada a partir de un JWT generado en el servidor. El token se queda en memoria y solo se envía en peticiones autenticadas o en cargas del socket.
userJwtstringUn JWT HS256 reciente generado por tu backend autenticadonamestring?Nombre visible del visitanteattributesobject?Atributos personalizados que añadir al contacto verificado
sonny('setAttributes', { ... })- Actualiza los atributos personalizados del visitante identificado. Solo se envían los valores que cambian. Si el visitante aún no está identificado, las actualizaciones esperan y se envían después de identify.
attributesobjectPares clave-valor que definir. Pasa null como valor para borrar un atributo.
sonny('watchAttributes', getter, { interval? })- Llama a tu función getter de forma periódica y sincroniza automáticamente los atributos que cambien. Útil cuando valores como el plan o el uso cambian mientras la página está abierta.
getterfunctionUna función que devuelve el objeto de atributos actualintervalnumber?Cada cuánto comprobarlo, en milisegundos. Por defecto 10000; mínimo 2000.
sonny('reset')- Borra el ID del widget, el correo, el nombre y el historial de conversaciones local del navegador actual, e inicia una nueva sesión de visitante. No elimina el contacto ni sus propiedades guardadas en Sonny. Úsalo al cerrar sesión.
¿Necesitas ayuda?
Consulta la guía de configuración del widget o ponte en contacto con nuestro equipo.
Documentación relacionada
- Configurar el widget
Instala el widget de chat en vivo de Sonny en tu sitio web con una sola etiqueta script.
- Personalizar el widget
Adapta el widget a tu marca con colores, logotipos, mensajes y comportamiento.
- Contactos
Descubre cómo se crean, gestionan, etiquetan y fusionan los contactos en Sonny.
- Historial de chat
Descubre cómo el widget recuerda a los visitantes que vuelven y recupera los mensajes anteriores.