Ir al contenido
Consola

Identificar al usuario

Adjuntá un id opaco (nunca PII) a grabaciones y votos para ver feedback y replays por usuario, desde el script o en runtime. Verlo es Pro. Para tickets, firmá el correo del visitante con el secreto de identidad.

Por defecto cada voto y cada grabación quedan Anonymous, y todo funciona. Identify te deja adjuntar tu id para un usuario, así un voto o un replay se pueden rastrear hasta la persona que lo hizo — y así podés saltar de un puntaje de satisfacción directo a las grabaciones de ese usuario.

Es del todo opcional. Sin esto no se rompe nada; simplemente ves “Anonymous”.

user-ref es un id opaco — el id que tu propia base ya tiene para ese usuario (user-123, a1b2c3). Nunca un email, nunca un nombre, nunca PII.

El motivo es físico: el id viaja dentro del bundle, a la vista en el navegador. Cualquier cosa que pongas ahí es visible para quien abra las dev tools. Si querés nombres o emails reales en el dashboard, eso es un v2 server-side y no va por acá.

Las dos son opcionales; usá la que te sirva. Elegí una.

En el script, cuando el id se conoce al renderizar la página:

<script src="https://app.espejo.dev/sdk/espejo.dom.js"
data-key="pk_live_..."
data-user-ref="user-123"></script>

En runtime, en el momento en que tu usuario loguea (ideal para SPAs, donde la página nunca se recarga):

window.espejo.identify("user-123");
// Al desloguear, limpialo — los votos y grabaciones que siguen vuelven a Anonymous:
window.espejo.identify(null);

En la consola, Satisfaction → Recent votes muestra el user_ref al lado de cada voto. Desde ahí podés filtrar por usuario, y saltar de un usuario directo a sus grabaciones. Un id opaco ata las dos vistas.

La sección Tickets del widget de chat y las tools de tickets del agente le muestran a un visitante sus tickets. Para eso no alcanza un id opaco: Espejo tiene que saber el correo de la persona, y tiene que estar seguro de que es suyo. Es algo distinto de user-ref, y los dos conviven.

Tu backend prueba el correo firmándolo con el secreto de identidad del proyecto. El widget manda el correo y la firma en cada pedido de tickets y en cada mensaje del chat. Sin un par que verifique no hay tickets: el widget oculta la sección y el agente nunca recibe las tools de tickets. Un correo solo no se acepta nunca.

En la consola, abrí Integraciones. Con Soporte conectado, la tarjeta Tickets tiene el bloque Identidad del visitante. Generar secreto lo muestra una sola vez: copialo al entorno de tu servidor (por ejemplo ESPEJO_IDENTITY_SECRET). Después la consola sólo muestra sus últimos cuatro caracteres.

Rotar lo reemplaza. Las firmas hechas con el secreto viejo dejan de valer en el pedido siguiente, así que los visitantes pierden sus tickets hasta que tu backend firme con el nuevo.

En la consola, abrí la pestaña Widget y prendé Tickets en el bloque Menú. Se puede prender cuando Soporte está conectado con Mostrar tickets en el widget prendido. Sin eso, el widget no muestra la sección Tickets, GET /v1/tickets contesta 404 y el agente no recibe lookup_tickets. Mientras no haya secreto de identidad, la fila avisa que nadie va a ver Tickets.

La firma es hex(HMAC-SHA256(secreto, correo)), con el correo recortado y en minúsculas. El resultado son 64 caracteres hexadecimales.

  • Firmá sólo un correo que tu sitio verificó (una dirección confirmada, o una que viene de tu proveedor de identidad). La firma le dice a Espejo que el correo es de esa persona: una dirección firmada que nadie confirmó abre los tickets de quien sea su dueño.
  • Sólo se pueden firmar correos ASCII. Sacá sólo los blancos ASCII de las puntas (espacio, tab, \n, \v, \f, \r), comprobá que lo que queda es ASCII visible y sin espacios, y recién ahí pasalo a minúsculas. Un correo con cualquier otra cosa no tiene identidad: el widget lo ignora y GET /v1/tickets contesta 401.
  • Un dominio con acentos o de otro alfabeto (IDN) se firma en punycode, la forma xn--: ana@münchen.example se firma como [email protected]. Pasale esa misma forma al widget.

Los tres ejemplos de abajo hacen exactamente eso, así que dan la misma firma que comprueba Espejo.

Node

import { createHmac } from "node:crypto";
const trimmed = user.email.replace(/^[\t\n\v\f\r ]+|[\t\n\v\f\r ]+$/g, "");
// Si no es ASCII: sin identidad.
const email = /^[\x21-\x7e]+$/.test(trimmed) ? trimmed.toLowerCase() : null;
const hash = email && createHmac("sha256", process.env.ESPEJO_IDENTITY_SECRET).update(email).digest("hex");

Python

import hashlib, hmac, os, re
trimmed = user.email.strip(" \t\n\v\f\r")
# Si no es ASCII: sin identidad.
email = trimmed.lower() if re.fullmatch(r"[\x21-\x7e]+", trimmed) else None
hash = email and hmac.new(os.environ["ESPEJO_IDENTITY_SECRET"].encode(), email.encode(), hashlib.sha256).hexdigest()

PHP

$trimmed = trim($user->email, " \t\n\v\f\r");
// Si no es ASCII: sin identidad. strtr baja sólo ASCII, con cualquier locale.
$email = preg_match('/^[\x21-\x7e]+$/', $trimmed) ? strtr($trimmed, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz') : null;
$hash = $email ? hash_hmac('sha256', $email, getenv('ESPEJO_IDENTITY_SECRET')) : null;

En el script, cuando la página se arma para un usuario logueado:

<script src="https://app.espejo.dev/sdk/espejo.chat.js"
data-key="pk_live_..."
data-user-email="[email protected]"
data-user-hash="3f5c...e91a"
data-user-name="Ana" defer></script>

Escapá el correo al escribirlo en el atributo, como cualquier valor que ponés en HTML (&, <, >, " y '). La mayoría de los motores de plantillas lo hacen solos; si armás la etiqueta concatenando texto, hacelo vos. Lo mismo vale para el nombre.

data-user-name es opcional y solo sirve para saludar al visitante en Inicio (“Hola, Ana.”). Se usa solo mientras el correo y el hash son válidos, se corta en 40 caracteres y nunca viaja a la API ni se guarda. No se firma: no cambia lo que el visitante puede ver.

En una SPA, después del login y al cerrar sesión. El tercer argumento, el nombre, es opcional:

EspejoChat.identify("[email protected]", "3f5c...e91a", "Ana");
// Al cerrar sesión: Tickets desaparece y el chat arranca una conversación nueva.
EspejoChat.identify(null);

El widget los manda en los headers x-espejo-user-email y x-espejo-user-hash. GET /v1/tickets contesta 401 si faltan o no verifican, y 404 si la sección Tickets no está prendida en el proyecto.

Quien tenga el secreto puede firmar cualquier correo y leer los tickets de esa persona. La firma se calcula donde vive el secreto, en tu servidor, y sólo para el usuario que está logueado. Nunca pongas el secreto en la página, en un bundle ni en una app móvil. Si se filtra, rotalo.

El correo y su firma sí llegan al navegador, y está bien: sólo abren los tickets de esa persona, la misma que ya está logueada. No se adjuntan a grabaciones ni a votos, y Espejo no guarda el correo.

El primer mensaje del chat que llega con un par válido deja esa identidad pegada a la conversación. Un pedido posterior en la misma conversación con otra identidad, o sin ninguna, recibe 409 con el código identity_changed, y nada de esa conversación: el widget arranca una nueva y manda el mensaje otra vez, una sola vez.

El widget arranca una conversación nueva cuando identify cambia de usuario, y también cuando la página carga con otra persona. Junto al id de la conversación guarda, en sessionStorage, una huella de quién la abrió (nunca el correo ni el hash). Si la página carga con otra identidad, o sin ninguna después de una conversación que tenía, descarta la conversación guardada y la tarjeta Continuar de Home.