Session replay que tu agente de IA puede leer

Un script, y un reporte de bug deja de ser una frase. Llega con la sesión adentro: las llamadas de red con sus cuerpos, la consola, cada clic, y el replay del DOM o el video de la pantalla. Mirala vos, o apuntá un agente de IA por MCP y dejá que la lea entera.

Plan gratis, sin tarjeta. 18 KB gzip en tu página, y no sube nada hasta que alguien reporta un problema.

Paquete npm - un import, SDK completo

import { Espejo } from "@espejo/browser";

const espejo = new Espejo({ key: "pk_live_..." });
espejo.start();

Después, cuando un usuario se choca con algo:

await espejo.report('No me deja guardar el pedido')
// → la URL de la sesión subida

Una grabación, cuatro pistas, un solo reloj

El replay, la red, la consola y las interacciones comparten la línea de tiempo. Un 500 deja de ser una línea de log y pasa a ser el botón que alguien apretó un segundo antes.

espejo · session 4f2a…c81
Red Consola Clics
  • 0:04 clic, “Guardar”
  • 0:04 POST /v1/orders, 500
  • 0:04 Uncaught TypeError
  • 0:06 GET /v1/session, 200
0:00 Replay 0:09
Esquema de la vista de replay, una ilustración, no una grabación.

Dale a tu agente las herramientas para resolver, no solo responder

Conectá las tools y el MCP de Espejo a Claude, Codex, LangGraph o tu propio agente. Puede guiar a tus usuarios, actuar por ellos en tu producto y reportar automáticamente un problema cuando una acción necesita asistencia. Así las preguntas de uso se resuelven en el momento y a tu equipo solo llegan los errores reales, los que requieren una corrección en la aplicación.

El asistente está usando esta página
Workspace

Equipo

Miembros del equipo y sus roles.

+ Invite
☷ MemberRol ⌄StatusMember since
DennisOwneractive22/7/2026
Ana PérezAdminactive28/7/2026
Martín SilvaMemberactive13/8/2026
OwnerAdminMember
AI

La grabación la abre tu agente. No vos.

Espejo corre un servidor MCP en el mismo proceso que la API. Claude Code, claude.ai o cualquier cliente MCP se conecta a POST /mcp con OAuth 2.1 y PKCE; una persona lo aprueba en una pantalla de consentimiento y elige qué proyectos puede ver; y de ahí en más el agente tiene cinco tools de lectura. Puede encontrar la sesión que falló y leer qué pasó adentro sin que nadie abra una consola.

Conectar un agente

$ claude mcp add --transport http espejo https://app.espejo.dev/mcp

Sólo lectura, por política y no por costumbre

Las cinco están detrás de un único scope mcp:read. Una tool sin entrada en la tabla de política no corre, así que agregar un handler nunca alcanza para exponerla: hay que declarar además qué autoridad necesita, en otro archivo, a propósito.

Ninguna clave de máquina entra

La clave de administrador de la plataforma ve todas las cuentas, así que se la rechaza en la puerta del MCP. Cada conexión tiene atrás una cuenta, un consentimiento y un alcance.

Revocable desde la consola

Cada conexión es un permiso que se ve y se revoca, con refresh rotativo y revocación RFC 7009. Se saca el consentimiento y el agente queda afuera.

Cómo funciona el servidor MCP →

Las cinco tools

  • list_recordings

    Las grabaciones que esta conexión puede ver, de la más nueva a la más vieja. Se filtra por proyecto, por rango de fechas, por texto libre, o sólo las que tienen errores.

  • get_recording

    Todo lo que Espejo sabe de una: cuándo corrió, cuánto duró, en qué página y en qué release, sus conteos de error, y qué objetos guardó.

  • recording_events

    Qué pasó adentro. Primero el resumen, requests, 4xx/5xx con el clic que los provocó, errores de consola, y después red, consola, interacciones y navegaciones.

  • recording_frames

    Links firmados y de vida corta a lo que se puede mirar: el video de pantalla, el replay del DOM de rrweb. Son URLs de Espejo, nunca del bucket, y vencen.

  • recording_transcript

    La transcripción guardada de la narración de una grabación, si tiene. Nunca dispara una transcripción. Espejo todavía no las produce.

Instalá con el SDK

Usá el paquete npm si tu app tiene bundler. El script alojado sigue disponible si querés instalar sin build.

Por defecto no sube nada. El script graba en un anillo en memoria con tope de bytes y manda ese pedazo recién cuando se llama a report(). data-mode="always" sube todas las sesiones, existe, pero no es el default. Es la diferencia entre algo que se puede dejar puesto en producción y algo que te quema el bucket en una semana.

Paquete npm - un import, SDK completo

import { Espejo } from "@espejo/browser";

const espejo = new Espejo({ key: "pk_live_..." });
espejo.start();

¿No tenés bundler? Usá el script alojado · Red con cuerpos, consola, clics y navegación, más el botón de reporte y la encuesta de feedback · 18 KB gzip

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

Todo lo anterior, más un replay visual completo del DOM · esa misma reconstrucción es la que le da al heatmap su fondo real

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

Qué queda adentro de la grabación

Todo capturado desde la propia página: no hay nada que instalar ni nada que aprobar.

La red, con los cuerpos

fetch y XHR se parchean desde adentro de la página, así que los cuerpos de request y de response están de verdad. Sin barra amarilla del depurador, sin extensión, sin medio log.

Consola, clics y navegación

Cada log y cada error, cada interacción descrita, qué se clickeó, jamás qué se tipeó, y cada cambio de URL, en el mismo reloj que todo lo demás.

Replay del DOM o video de pantalla

rrweb reconstruye la página tal como estaba. O grabás la pantalla real, con micrófono, desde un click handler. Necesita un gesto del usuario, así que es un botón de tu interfaz.

Multi-tenant desde la primera tabla

Cuentas, proyectos, y una clave pública por proyecto que va directo a tu HTML, igual que un DSN de Sentry. La ruta en el bucket la arma siempre el server, nunca la propone el navegador.

Nuestro bucket, o el tuyo

Por defecto el nuestro, para que un proyecto arranque en un clic. Que las grabaciones queden en tu propio bucket, con tu credencial, llega con el plan Team, para quien necesite que no salgan de su cuenta. R2 y S3.

El botón de reportar, ya puesto

Un botón flotante de “reportar un problema” se monta solo, salvo que lo apagues con data-button="off". O llamás a report() desde tu propia interfaz y te devuelve la URL de la sesión.

Los clics se vuelven un heatmap sobre la página real

Cada clic se captura sin configurar nada y se pinta sobre la página reconstruida desde el DOM · no un screenshot, el markup real con los estilos que vio tu usuario. Dónde se aprieta una pantalla, y dónde no la toca nadie. Una ruta que todavía no tiene snapshot cae a un wireframe neutro.

espejo · heatmap /checkout
menos clics más clics
Esquema de la vista de heatmap · una ilustración, no datos de clic reales.

Convertí sesiones en behaviors

Describís un patrón una vez: llegó al carrito y no compró, clickeó cinco veces el mismo lugar, o empezó un formulario y no lo envió. Espejo cuenta cada sesión que lo matchea y te linkea directo a esas grabaciones. Empezás desde un template o armás el tuyo; cada pantalla se agrupa por ruta normalizada, así /orden/123 y /orden/456 son la misma página.

Las señales se juntan en todos los planes, no se pierde nada mientras estás en el plan gratis. Definir behaviors y ver sus conteos y segmentos es Pro; el catálogo de templates lo ve cualquiera.

Preguntales a tus usuarios cómo les fue, en un toque

Una encuesta chica · caritas, una escala del 1 al 7 o pulgares · aparece una vez por visitante, y un solo toque deja un voto en un dashboard de Satisfacción. Apagada por defecto, y aparte del botón de reportar: esto es un voto, no una grabación.

espejo · feedback scale7
Satisfacción 4.6 / 7 ¿Cómo te fue? 1 2 3 4 5 6 7 ¡Gracias!
Esquema de la encuesta CSAT · una ilustración, no votos reales.

Y atá cada voto a uno de tus usuarios. Un atributo o un solo identify() le dice a Espejo a qué usuario tuyo pertenece un voto o una sesión, así ves el feedback y las grabaciones por persona. Es un id opaco de tu propia base, nunca un email ni PII.

También graba en el celular

La captura es JavaScript corriendo adentro de tu página: fetch y XHR parcheados, la consola, los listeners de eventos, la History API. Corre donde corra la página, así que una sesión graba su red, su consola y cada toque igual en un iPhone que en Android. Los clics son toques, no cambia nada más: la misma grabación, las mismas cinco tools de MCP, los mismos reportes automáticos.

Lo único de escritorio es el video de pantalla. Grabarla necesita getDisplayMedia y un gesto del usuario, y ningún navegador de celular lo expone: ni Safari en iOS, ni Chrome en Android. En el teléfono queda todo lo demás: red con cuerpos, consola, toques, navegación y el replay del DOM.

Bugs que se reportan solos

El mejor reporte de bug es el que nadie tuvo que escribir. Se prende por proyecto y Espejo sube el buffer en el momento en que tu app se rompe, con el usuario todavía en la página, y sin depender de que te avise.

Qué pasa, en orden

  1. Tu app tira un error

    Una excepción no capturada, una promesa rechazada que nadie atrapó, un 500 de tu API. El SDK ya está mirando todo eso: son los mismos hooks que llenan la grabación.

  2. El buffer sube, sin video

    El mismo camino de subida que el botón de reportar, así hay una sola puerta y no dos. Nunca con video: grabar la pantalla necesita un gesto del usuario, y acá no hay nadie para darlo. El replay del DOM sí viaja, si cargaste el bundle dom.

  3. Tu endpoint recibe un POST firmado

    El link de la grabación, el disparador, el extracto del error y los contadores, firmado con HMAC-SHA256 en X-Espejo-Signature para que tu receptor pueda probar que salió de acá. Timeout de 5s, reintentos con backoff, y el último resultado a la vista en la pantalla del proyecto.

  4. Apuntale tu agente de IA por MCP

    La grabación ya es legible por un agente: red con cuerpos, consola, clics, DOM. Un webhook que entrega un link más un servidor MCP que lo sabe leer es la materia prima de un pipeline que se arregla solo, el pipeline lo armás vos. Espejo graba y avisa; no repara.

Leer la documentación de auto-reporte →

Qué lo puede disparar

  • auto:uncaught

    window.onerror, los que rompen una pantalla

  • auto:unhandled_rejection

    una promesa rechazada que nadie manejó

  • auto:console_error

    console.error, para los equipos que lo usan en serio

  • auto:network_5xx

    tu propia API contestó 500

  • auto:network_failed

    el request no salió: sin red, CORS, abortado

  • auto:network_4xx

    apagado por defecto, un 401 o un avatar que falta suele ser ruido

Por qué un render loop no te funde

Tres por carga de página, y ahí se termina

Un componente en loop a 60fps tira 3.600 errores por minuto. El tope de fábrica es tres subidas por carga de página, hay 60 segundos de espera entre una y otra, y la misma firma de error no sube dos veces. Los tres candados, porque cada uno ataja una forma distinta de avalancha.

Gasta el mismo presupuesto que todo lo demás

Los reportes automáticos consumen los topes diarios normales del proyecto. No hay canal privilegiado, así que el peor caso lo acota un número que ya pusiste vos. La pausa que lo protege es la misma de siempre.

Decide el proyecto, no la página

El SDK le pregunta al server qué está prendido. El script puede apagarlo (data-auto-report="off"), nunca prenderlo, tu clave pública es pública, así que un atributo que pudiera activarlo dejaría a cualquiera gastarte la cuota. Si el pedido de configuración falla, los reportes automáticos quedan apagados y el SDK sigue andando.

Lo que nunca llega al bucket

Una grabación termina en un bucket y en la ventana de contexto de un modelo. Un secreto que se escapa acá no se “borra” después, por eso el redactor corre en el navegador, antes de que salga nada.

  • Headers de autenticación

    Authorization, Cookie, Set-Cookie, x-api-key y proxy-authorization, más cualquier header cuyo nombre traiga “token” o “secret”, que ataja los que nadie listó.

  • Secretos adentro de las URLs

    Valores de query y de fragmento que se llamen como un token, password, signature, code, key o credential; el user:pass@ del host; y cualquier cosa con forma de JWT, reconocida por su forma aunque esté sin nombre en medio de un path.

  • Lo que la gente tipea

    De un input se registra que cambió, jamás con qué. En el replay del DOM van enmascarados todos los inputs, no sólo los de contraseña: un número de tarjeta en un campo de texto común no es menos privado por no ser una contraseña.

  • Zonas enteras, cuando vos lo decís

    data-espejo-block deja un hueco en el replay y además calla los clics de adentro. data-espejo-mask conserva la forma y tapa el texto.

Redactar corre antes de truncar, a propósito: un JSON cortado a la mitad ya no parsea, y un redactor que no puede parsear no puede redactar.

Precios

Un plan gratis con el producto entero adentro, y topes en vez de un reloj de prueba.

Team

$9

por mes, 2 asientos incluidos

Topes más altos, tu propio bucket, más gente en la cuenta.

  • Tu propio bucket R2 o S3
  • Webhook firmado para los reportes automáticos
  • Topes y retención más altos
  • $3/mes por asiento extra, se cambia cuando quieras
  • Anual, con unos dos meses gratis
  • PayPal o Mercado Pago
Ver la documentación

Se cobra por asiento, mensual o anual: pagás por la gente que está en la cuenta, no por un escalón de plan que se te queda chico.

Ponelo en una página y rompé algo

Creá un proyecto, pegá el script, reproducí el bug. Después preguntale a tu agente qué pasó.