Saltar al contenido principal

Integración del SDK web

El SDK web de Joryio te permite rastrear el comportamiento de los usuarios, enviar eventos y gestionar perfiles de usuario en tu sitio web.

Instalación​

Mediante npm/yarn​

npm install @joryio/web-sdk
# o
yarn add @joryio/web-sdk

Mediante CDN​

Los archivos del SDK se sirven desde un único host global, https://sdk.joryio.com, sea cual sea la región de tu espacio de trabajo. Los eventos y las llamadas a la API siguen yendo al host de API de tu región (la opción apiEndpoint): solo la ubicación del archivo es global.

<!-- Recomendada: la 1.x más reciente. Recibe correcciones, nunca un cambio incompatible. -->
<script src="https://sdk.joryio.com/web/1/joryio.min.js"></script>

<!-- Fijada: una versión exacta que no cambia nunca. Actualiza cambiando la URL. -->
<script src="https://sdk.joryio.com/web/1.3.3/joryio.min.js"></script>
RutaQué sirve
/web/1/<file>La versión 1.x más reciente (recomendada)
/web/<version>/<file>Una versión exacta, p. ej. /web/1.3.3/: su contenido no cambia nunca

Archivos: joryio.min.js (el SDK), joryio-sw.js (el service worker de web push) y joryio.umd.js (la compilación UMD sin minificar).

Cuándo usar cada opción
  • /web/1/: la mayoría de los sitios. Las correcciones y las funciones nuevas de 1.x llegan automáticamente, y un cambio incompatible (2.0) nunca llega por esta ruta.
  • Fijada (/web/1.3.3/): sitios que quieren controlar de forma determinista cuándo se actualiza el SDK. El archivo no cambia nunca, así que el navegador lo guarda en caché durante mucho tiempo.

Actualizar una integración fijada. Cuando Joryio publique una versión nueva del SDK, aumenta el número en tu fragmento:

<!-- Antes -->
<script src="https://sdk.joryio.com/web/1.3.3/joryio.min.js"></script>
<!-- Después -->
<script src="https://sdk.joryio.com/web/1.4.0/joryio.min.js"></script>

El navegador trata la URL nueva como un archivo nuevo y ejecuta el SDK nuevo.

Se ha movido desde el host de la API

Hasta octubre de 2026 el archivo del SDK se servía desde el host de la API, en https://api-eu1.joryio.com/sdk/web/.... Esa ruta ya no existe: sustitúyela por https://sdk.joryio.com/web/1/.

Snippet asíncrono​

Pega este código en tu <head>. Carga el SDK sin bloquear la página, y las llamadas hechas antes de que se cargue el archivo se encolan y se ejecutan en orden. Cualquier método público puede llamarse así, y el propio init puede llegar más tarde - por ejemplo, desde el callback de tu banner de consentimiento.

<script>
!function(){var e=window.joryio=window.joryio||[];if(!e.initialize)if(e.invoked)window.console&&console.error&&console.error("Joryio snippet included twice.");else{e.invoked=!0,e.methods=["init","track","identify","addAlias","setAttributes","setAttribute","incrementAttribute","unsetAttribute","addToArray","removeFromArray","reset","flush","optOut","optIn","updateConsent","getTrackingConsent","isUserOptedOut","setSdkAuthenticationToken","setSdkAuthenticationErrorCallback","getAnonymousId","getUserId","ready","getIdentity","getSessionInfo","getUTMData","updateUTM","syncInAppCampaigns","getInAppCampaigns","displayInAppCampaign","dismissInAppMessage","trackInAppConversion","ecommerce","wipeData","destroy"],e.keep=["init","optOut","optIn","updateConsent","wipeData"],e.factory=function(t){return function(){var n=Array.prototype.slice.call(arguments);if(n.unshift(t),e.length>=1e3)for(var i=0;i<e.length;i++)if(e.keep.indexOf(e[i][0])<0){e.splice(i,1);break}return e.push(n),/^(get|is)[A-Z]/.test(t)?void 0:e}};for(var t=0;t<e.methods.length;t++){var n=e.methods[t];e[n]=e.factory(n)}e.load=function(e){var t=document.createElement("script");t.type="text/javascript",t.async=!0,t.src="https://sdk.joryio.com/web/1/joryio.min.js";var n=document.getElementsByTagName("script")[0];n.parentNode.insertBefore(t,n)},e.load()}}();
joryio.init({ sdkKey: 'jry_sdk_web_YOUR_KEY' });
</script>
  • La cola guarda como máximo 1.000 llamadas. Más allá se descartan las más antiguas - nunca init, optOut(), optIn(), updateConsent() ni wipeData().
  • Una llamada en cola no puede devolver un valor. Un getter como getUserId() o isUserOptedOut() devuelve undefined hasta que el SDK termina de cargar. Para leer un ID, o para usar user, push o el tracker que devuelve ecommerce(), usa ready: joryio.ready(function (sdk) { sdk.getAnonymousId(); }).
  • Un init fallido no pierde nada. Si init falla (falta la clave del SDK o no es válida - el motivo se registra en la consola), window.joryio sigue siendo la cola. Vuelve a llamar a joryio.init({...}) con la clave correcta y el SDK arranca con todas las llamadas hechas hasta entonces: primero las decisiones de privacidad y después el resto en orden, incluida una baja.
  • Las llamadas hechas con el seguimiento desactivado se descartan. Un track(), una escritura de atributo, un addAlias() o un trackInAppConversion() en cola, hecho mientras el visitante estaba excluido o el consentimiento denegado - por la elección guardada, la configuración de init o una decisión encolada antes - se descarta, y un optIn() o updateConsent('granted') posterior en la cola no lo recupera. identify() y las bajas se conservan. Lo mismo vale para una llamada encolada antes de un optOut() o updateConsent('denied') en cola, o antes de un wipeData() encolado antes de init. Un callback de ready() encolado con el seguimiento desactivado, o antes de un optOut() o updateConsent('denied') en cola, se ejecuta igualmente, pero las llamadas a track(), las escrituras de atributos, los alias, las conversiones y las altas de suscripción que hace mientras se ejecuta también se descartan - salvo que el propio callback levante lo que desactivó el seguimiento, para el resto de su ejecución: su propio optIn() levanta una exclusión, su propio updateConsent('granted') levanta una denegación, ninguno levanta el otro, y nada dentro de él levanta un optOut() o una denegación encolados después. Un wipeData() encolado antes de init no limita un callback de ready() encolado antes que él (el callback se ejecuta para el visitante nuevo). Las lecturas y las bajas funcionan, y la instancia del SDK que recibió se comporta con normalidad después.
  • Las decisiones de privacidad se aplican primero. Un optOut(), optIn() o updateConsent() en cola, y un wipeData() encolado antes de init, se aplican dentro de init, antes de que envíe nada - así ningún Session Start, sincronización in-app o registro push sale con el estado que reemplazan:
<script>
/* the snippet above, then: */
joryio.updateConsent('denied'); // applied inside init, before anything is sent
joryio.init({ sdkKey: 'jry_sdk_web_YOUR_KEY' });
</script>

Inicio rápido​

1. Inicializar el SDK​

Primero, inicializa el SDK con tu clave de SDK. Puedes encontrarla en el dashboard de Joryio, en Configuración → Apps.

import JoryioSDK from '@joryio/web-sdk';

// Inicializa el SDK
const joryio = new JoryioSDK({
sdkKey: 'jry_sdk_web_your_sdk_key_here',
enableDebug: false, // Activa el registro de depuración en desarrollo
});

El paquete tiene un mapa exports: una versión ES module para import y una CommonJS para require. Los bundlers (webpack, esbuild, Rollup, Vite) usan la versión ES module para ambos, así que una app que importa el SDK en un archivo y lo requiere en otro sigue ejecutando un solo SDK. La importación por defecto es la clase tanto en bundlers como en Node ESM (SSR, Vitest, scripts .mjs), import { JoryioSDK } from '@joryio/web-sdk' también funciona, y TypeScript le da el mismo tipo con moduleResolution node16, nodenext o bundler.

Cuando cargas desde la CDN en vez de npm, la misma clase está disponible como window.Joryio:

const joryio = new Joryio({
sdkKey: 'jry_sdk_web_your_sdk_key_here',
});

2. Identificar usuarios​

Envía los números de teléfono con + y el código de país

Envía siempre los números de teléfono en formato internacional completo (E.164): +, el código de país y luego el número - por ejemplo +972501234567. Un número sin + se lee con el país predeterminado para números de teléfono del espacio de trabajo; si no hay ninguno, o los dígitos se pueden leer de dos formas, se guarda sin verificar y no se usa para mensajes. Cómo se tratan los números de teléfono

Identifica a los usuarios cuando se registran o inician sesión:

// Identifica un usuario
joryio.identify('user_123');

// Establece los atributos de perfil por separado
joryio.setAttributes({
firstName: 'John',
lastName: 'Doe',
plan: 'premium',
signupDate: '2024-01-15',
});

3. Rastrear eventos​

Rastrea las acciones y el comportamiento de los usuarios:

// Rastrea un evento personalizado
joryio.track('Product Viewed', {
product_id: 'prod_123',
product_name: 'Premium Plan',
price: 99.99,
currency: 'USD',
});

// Rastrea vistas de página
joryio.track('Page Viewed', {
page: '/pricing',
title: 'Pricing Page',
category: 'Marketing',
});

Opciones de configuración​

OpciónTipoPredeterminadoDescripción
sdkKeycadenaObligatorioTu clave de SDK del dashboard de Joryio
apiEndpointstringhttps://api-eu1.joryio.com/apiEl host de la API de tu región. Basta con el nombre del host; se añade /api
userIdstring-Empezar con un ID de usuario conocido
anonymousIdstringgeneradoUsar tu propio ID anónimo
enableDebugbooleanofalseActiva el registro de depuración en la consola (logLevel: 'debug'). Un logLevel explícito tiene prioridad
batchFlushIntervalnúmero5000Frecuencia de envío al servidor de eventos por lotes (milisegundos). Se encolan localmente y se envían cada 5 segundos para reducir solicitudes de red. Los valores por debajo de 1000 se elevan a 1000.
batchSizenumber50Eventos por solicitud, y el tamaño de cola que provoca un envío antes del temporizador. Nunca más de 500 por solicitud (el límite de la API)
sendImmediatelybooleanofalseEnvía cada evento de inmediato, sin lotes (no recomendado para producción)
sessionTimeoutnúmero1800000Tiempo de espera de sesión en milisegundos (predeterminado: 30 minutos). Tras este período sin actividad comienza una sesión nueva - se comprueba en cada track() y cuando la pestaña vuelve a ser visible.
trackSessionStartbooleanotrueUn evento «Session Start» por cada sesión nueva. Una recarga que retoma la sesión no envía ninguno.
trackPageViewsbooleanofalseRastrea automáticamente «Page Viewed» al cargar la página
captureUTMbooleanotrueCaptura automáticamente parámetros UTM de la URL para atribución de campañas
resetSessionOnNewCampaignbooleanofalseInicia una sesión nueva al cambiar los parámetros UTM (útil para analítica de sesiones por campaña)
trackDevicePropertiesbooleanotrueIncluye información de dispositivo en Session Start
persistQueuebooleanotrueConserva en localStorage los eventos en cola para que sobrevivan a recargas de página
maxQueueSizenumber1000Máximo de eventos que guarda la cola local mientras los envíos fallan; por encima se descartan los más antiguos (con una advertencia en el registro cuando el registro está activo)
maxRetriesnumber3Reintentos inmediatos de una solicitud (5xx, 429, error de red). Un lote que sigue fallando se conserva y se reintenta más tarde
retryBackoffMsnumber1000Espera inicial antes de reintentar, en milisegundos; se duplica con cada fallo, hasta 5 minutos
requestTimeoutnúmero10000Tiempo de espera en milisegundos de cada solicitud a la API (eventos, atributos, in-app, push)
trackPagePropertiesbooleantrueIncluir el referrer y la página de aterrizaje en los eventos "Page Viewed"
respectDoNotTrackbooleantrueTratar el ajuste Do Not Track del navegador como una exclusión
logLevelcadena-'error', 'warn', 'info' o 'debug'. No se registra nada salvo que se configure esto o enableDebug
trackingConsentcadenasin definir'granted', 'denied' o 'pending'. Déjalo sin definir si tu sitio no tiene banner de consentimiento. Consulta Privacidad y consentimiento
optOutbooleanofalsetrue desactiva el seguimiento del visitante y se recuerda entre cargas de página, como optOut(). false no levanta una desactivación guardada - solo optIn() lo hace
enableSdkAuthenticationbooleanfalseAdjuntar y renovar un JWT que emite tu backend. Consulta Autenticación del SDK
sdkAuthenticationTokenstring-El primer JWT, usado cuando enableSdkAuthentication es true
pluginsarray[]Plugins con hooks beforeTrack / afterTrack (solo web)
inApp.enabledbooleantrueActivar o desactivar la mensajería in-app
inApp.evaluationModecadena'differential-sync'Cuándo se obtienen las campañas in-app: 'differential-sync' (al cargar, en identify(), en reset(), en una sesión nueva, al cambiar un atributo observado y cada inApp.syncInterval) o 'session-start-only' (solo al cargar, en identify(), en reset() y en una sesión nueva)
inApp.syncIntervalnúmero0 (desactivado)Volver a obtener las campañas in-app cada N milisegundos en modo 'differential-sync'. Los valores por debajo de 30000 se elevan a 30000; se pausa mientras la pestaña está oculta
inApp.allowHtmlJsInAppMessagesbooleanofalsePermite mensajes in-app en HTML. El mensaje se muestra en un iframe aislado con una Content-Security-Policy estricta: solo se ejecuta el script del puente del SDK (tus <script>, los manejadores on* en línea y los <iframe> no), así que los botones y formularios funcionan con data-joryio-action / data-jry-form. Requiere activación porque muestra HTML escrito a mano. El mensaje nunca puede navegar su propio iframe: los enlaces y las redirecciones pasan por el SDK, que los abre en tu página (solo enlaces http(s) y relativos a la raíz); si el iframe navega de todos modos, el mensaje se cierra. Los mensajes nativos se muestran en cualquier caso. Mensajería in-app
inApp.displayOptions.defaultPositionstring'center''top', 'center' o 'bottom': dónde aparece un mensaje de tipo Custom. Los mensajes modal, banner, slide-up y de pantalla completa conservan su propia posición
inApp.displayOptions.defaultAnimationstring'fade''fade', 'slide' o 'none': cómo entran los mensajes. Se omite cuando el sistema del visitante pide menos movimiento
inApp.displayOptions.backdropColorstring'rgba(0, 0, 0, 0.5)'Color de la capa oscura detrás de los mensajes modal y de pantalla completa
inApp.displayOptions.closeButtonColorstringel color del texto del mensajeColor del botón de cierre
inApp.displayOptions.zIndexnumber999999Orden de apilamiento de los mensajes (la capa oscura queda uno por debajo)
inApp.onMessageDisplay / onMessageClick / onMessageDismissfunction-Callbacks para cada paso - consulta Callbacks

Procesamiento por lotes y vaciado de eventos​

Los eventos se agrupan localmente y se envían al servidor por lotes para optimizar el uso de red:

  • Vaciado automático: los eventos se envían cada batchFlushInterval milisegundos (5 s de forma predeterminada).
  • Vaciado por tamaño: se envían de inmediato al acumularse batchSize eventos (50 de forma predeterminada).
  • Vaciado manual: llama a joryio.flush() para enviar de inmediato los eventos en cola.
  • Al salir de la página: los eventos en cola se entregan al navegador con fetch(..., { keepalive: true }), sin cookies y con la cabecera de autenticación del SDK. Solo se entrega lo que cabe en el límite keepalive del navegador (64 KB) - un único presupuesto para toda la página, se disparen los eventos de salida que se disparen; el resto sigue en cola para la siguiente vista de página. Sin conexión, o después de que falle el último envío, no se entrega nada: los eventos siguen en cola en lugar de salir en una solicitud que probablemente fallará.
  • Un envío fallido nunca bloquea la cola: cada solicitud lleva como máximo batchSize eventos (nunca más de 500 ni unos 900 KB). Una solicitud que la API rechaza por mal formada (400, 413, 422) se divide hasta descartar el único evento defectuoso; los errores de red, 429 y 5xx conservan todos los eventos y reintentan con espera exponencial; un 402 (la ingesta de eventos está pausada para tu espacio de trabajo), un 403 o una clave no válida detienen el envío de eventos durante esa vista de página y los conservan. Tras un 402 también esperan las escrituras de perfil - salvo las retiradas, que la API sigue aceptando y que se siguen enviando.
  • Marcas de tiempo: los eventos llevan una marca de tiempo ISO 8601 en UTC con milisegundos (2026-10-07T09:30:00.123Z), como en todos los SDK de Joryio.
  • success: false es un fallo: una respuesta 2xx cuyo cuerpo dice success: false - para un lote, identify, atributos, un alias, un cambio de suscripción o un registro push - se reintenta y nunca se da por hecha. En un lote solo se conservan los eventos que la API indica en failedIndices.
  • Rechazado para siempre significa descartado: los eventos que la API indica en rejectedIndices se descartan y no se reintentan. track() descarta un evento antes de encolarlo (con una línea de log) cuando su nombre no tiene 1-255 caracteres o sus propiedades superan los límites de la API: más de 50 KB serializadas, más de 200 claves o un anidamiento de más de 5 niveles.
  • Un usuario por solicitud: una solicitud nunca mezcla eventos de dos usuarios, así que los eventos en cola de un usuario que cerró sesión no retienen los del siguiente (consulta Autenticación del SDK).
  • Las escrituras de perfil son duraderas y ordenadas: las escrituras de atributos, addAlias() y los cambios de suscripción y de grupo de suscripción se guardan en localStorage hasta que la API los confirma, así que una navegación o una pestaña cerrada no los pierde, y al salir de la página los que esperan se entregan también a fetch(..., { keepalive: true }). Un cambio más reciente del mismo canal, grupo o atributo sustituye a uno anterior que aún espera, de modo que un cambio anterior nunca llega después de uno más reciente.
  • Una sola regla de reintento para toda escritura (incluido identify()): los errores de red, los tiempos de espera agotados, 429, 5xx y 402 nunca cuentan para el límite y se reintentan con una espera creciente; un 404 ("aún no hay perfil") se reintenta como máximo 5 veces y cualquier otro rechazo reintentable (success: false, un 401 de autenticación del SDK para el usuario actual) como máximo 10 veces; 400, 403, 409, 413 y 422 descartan la escritura con una línea de log.
  • Límite de cola: la cola local guarda como máximo maxQueueSize eventos (1000 por defecto). Si se supera el límite, por ejemplo tras mucho tiempo sin conexión, se descartan los más antiguos (con una advertencia en el registro cuando el registro está activo).
// Envía eventos de inmediato en vez de agruparlos
const joryio = new JoryioSDK({
sdkKey: 'jry_sdk_web_...',
sendImmediately: true // Envía cada evento de inmediato
});

// O configura el comportamiento de los lotes
const joryio = new JoryioSDK({
sdkKey: 'jry_sdk_web_...',
batchFlushInterval: 10000, // Vacía cada 10 segundos
batchSize: 20 // O al acumularse 20 eventos
});

// O vacía manualmente en cualquier momento
joryio.track('Important Event', {...});
joryio.flush(); // Envía ahora

Gestión de sesión​

Las sesiones rastrean la actividad continua del usuario y se gestionan automáticamente:

  • Tiempo de espera de sesión: 30 minutos de inactividad de forma predeterminada (configurable mediante sessionTimeout).
  • Comienza una sesión nueva cuando:
    1. El usuario carga la página por primera vez.
    2. Transcurre el período de espera sin eventos.
    3. El usuario llama a joryio.reset() (por ejemplo, al cerrar sesión).
    4. Se detecta una campaña nueva, si resetSessionOnNewCampaign está activado.

Configurar el tiempo de espera de sesión:

const joryio = new JoryioSDK({
sdkKey: 'jry_sdk_web_...',
sessionTimeout: 3600000 // 1 hora en milisegundos
});

// O un tiempo de espera de sesión más corto
const joryio = new JoryioSDK({
sdkKey: 'jry_sdk_web_...',
sessionTimeout: 600000 // 10 minutos
});
tip

Las sesiones se gestionan automáticamente según la actividad del usuario. Cada evento rastreado reinicia el temporizador de inactividad.

Rastreo de Session Start​

De forma predeterminada, el SDK rastrea automáticamente un evento «Session Start» al comenzar cada sesión nueva. Este evento:

  • Puede usarse como disparador en campañas y flujos de journey.
  • Incluye todo el contexto de sesión (parámetros UTM, referente, página de destino y, si trackDeviceProperties está activado, datos del dispositivo).
  • Se guarda en el perfil del usuario como cualquier otro evento, de modo que los segmentos y la analítica pueden contar sesiones por usuario.

Ejemplo de evento Session Start:

// Se rastrea automáticamente cuando el usuario visita tu sitio
{
event: "Session Start",
properties: {
utm_source: "google", // Si hay parámetros UTM
utm_medium: "cpc",
utm_campaign: "spring_sale",
referrer: "https://google.com",
landing_page: "https://example.com/..."
},
userId: "user_123", // Si está identificado
anonymousId: "anon_456",
sessionId: "sess_789"
}

Datos automáticos de sesión​

El SDK web enriquece los eventos Session Start con datos de dispositivo y entorno:

  • $user_agent
  • $timezone
  • $screen_width / $screen_height
  • $viewport_width / $viewport_height
  • $language / $languages
  • $platform
  • $browser
  • $device_id
  • country (ISO-3166-1 alfa-2, derivado de la IP al iniciar la sesión)

Casos de uso:

  1. Journeys de bienvenida: usa Session Start como disparador para llegar a los usuarios cuando llegan a tu sitio.

  2. Segmentos basados en sesiones: los eventos Session Start se guardan por usuario, por lo que las condiciones de segmento basadas en eventos pueden contarlos; por ejemplo, «realizó Session Start al menos 10 veces» (usuarios avanzados) o «no realizó Session Start en los últimos 7 días» (reinteracción).

  3. Atribución de campañas: filtra la analítica por Session Start y agrupa por utm_campaign para ver qué campañas generan más sesiones.

Desactivar el rastreo de Session Start:

const joryio = new JoryioSDK({
sdkKey: 'jry_sdk_web_...',
trackSessionStart: false // Desactiva los eventos automáticos de inicio de sesión
});

Mensajería in-app​

El SDK muestra los mensajes in-app por ti. Las campañas elegibles aparecen solas y las impresiones, los clics y los descartes se registran automáticamente.

Tokens de entrega (delivery tokens)​

Cuando el backend sirve una campaña elegible, emite también un token de entrega firmado y de corta duración. El SDK lo devuelve al informar una impresión, un clic o un cierre, y el servidor verifica la firma antes de registrar nada.

No tienes que hacer nada: el SDK se encarga por ti. Se documenta porque el servidor puede configurarse para exigirlo. Por defecto, un informe sin token sigue aceptándose - el servidor lo registra como un cliente anterior a los tokens - y solo cuando el backend se ejecuta con IN_APP_REQUIRE_DELIVERY_TOKEN=true ese mismo informe se rechaza:

POST /v1/in-app/track (no deliveryToken)
{ "success": false, "error": "A delivery token is required" }

Un token presente pero que no se verifica - falsificado, manipulado o caducado - se rechaza siempre, sea cual sea la configuración.

El token es lo que hace que una impresión sea fiable: sin él, cualquiera que tenga la SDK key - que viaja dentro de cada app y cada página - podría informar impresiones y clics de una campaña que nunca se mostró, y tus informes los contarían.

Los sitios que cargan el paquete alojado desde https://sdk.joryio.com/web/1/joryio.min.js lo reciben automáticamente.

Dos tipos de contenido​

ContenidoQué esCómo se renderiza
NativoDatos estructurados: titular, texto, imagen, botonesElementos DOM normales, insertados como nodos de texto. Sin iframe y sin ejecución de scripts.
HTMLMarcado y CSS escritos por el autor, guardados tal cual (solo se quitan <base> y <meta http-equiv="refresh">)Un iframe aislado en tu página, con una Content-Security-Policy nueva para cada mensaje, donde solo se ejecuta el script puente del SDK.

Permitir mensajes HTML​

Los mensajes HTML están desactivados por defecto. Tus propios scripts no se ejecutan en ellos: Joryio guarda el marcado tal cual, pero cada mensaje recibe una Content-Security-Policy nueva con un nonce nuevo, así que solo se ejecuta el script puente del SDK: los <script> y los manejadores on* del autor quedan inertes, y el iframe no puede hacer llamadas de red (connect-src 'none') ni enviar formularios (form-action 'none'). Pero ese iframe puede ejecutar scripts en el origen de tu página, así que el riesgo que queda es un fallo en esa política, como un navegador que no la aplique; por eso activarlo es una decisión de tu propio equipo:

const joryio = new JoryioSDK({
sdkKey: 'jry_sdk_web_YOUR_KEY',
inApp: {
allowHtmlJsInAppMessages: true, // por defecto: false
},
});

Dejarlo desactivado no desactiva la mensajería in-app. Los mensajes nativos siguen mostrándose, porque son datos escritos en nodos de texto del DOM, sin ningún intérprete. Las campañas HTML se omiten (con una advertencia en el registro cuando el registro está activo). Una campaña omitida no consume ninguna impresión y sigue siendo elegible.

Si tu Content Security Policy prohíbe scripts en línea o contenido en iframes, déjalo desactivado y crea tus campañas como mensajes nativos.

Aplicar estilos a mensajes nativos desde tu propio CSS​

Un mensaje nativo es DOM real en tu página, no un iframe, así que puedes darle estilo como a cualquier cosa tuya. El renderizador expone anclajes estables:

.joryio-inapp-native /* la tarjeta */
.joryio-inapp-native h2 /* titular */
.joryio-inapp-native p /* cuerpo */
.joryio-inapp-native img /* imagen */
.joryio-inapp-native button.primary /* primer botón */
.joryio-inapp-native button.secondary /* el resto */
.joryio-inapp-close /* cerrar */
.joryio-inapp-backdrop /* capa de atenuación */
.joryio-inapp-modal / -banner / -slideup / -fullscreen /* por tipo */

Prefiere las variables a los selectores. Todo lo que una campaña puede fijar se lee de una custom property, así que definirlas en :root te da un estilo de casa que una campaña todavía puede anular para un mensaje concreto:

:root {
--joryio-inapp-bg: #0A1240;
--joryio-inapp-fg: #FFFFFF;
--joryio-inapp-primary: #00C8B7;
--joryio-inapp-primary-fg: #041028;
--joryio-inapp-radius: 18px;
--joryio-inapp-font: 'Inter', system-ui, sans-serif;
--joryio-inapp-size: 15px;
--joryio-inapp-align: start; /* start | center | end */
--joryio-inapp-title-weight: 700;
}

La precedencia, de mayor a menor:

  1. lo que la campaña fija en Style (optional) - escrito inline en la tarjeta
  2. tus valores --joryio-inapp-*
  3. los valores por defecto del SDK, que son colores del sistema

Así, una campaña que no fija nada hereda tu estilo de casa, y una que fija un fondo gana solo para ese mensaje. Sin !important en ningún sitio.

Si aun así usas los selectores, ten en cuenta que la hoja de estilos del SDK se inyecta en el momento de mostrar el mensaje y por tanto queda después de la tuya, así que gana los empates. Añade especificidad - .joryio-inapp .joryio-inapp-native {…} - en vez de una regla de una sola clase.

La dirección se resuelve sola: la tarjeta lleva dir="auto", así que un mensaje en hebreo o árabe se alinea a la derecha dentro de una página de izquierda a derecha.

Tipos de mensaje​

  1. Modal - Centro de la pantalla con fondo atenuado
  2. Banner - Parte superior de la página
  3. Slide-Up - Notificación pequeña desde abajo
  4. Full-Screen - Mensaje a pantalla completa
  5. Custom - Tu página decide la ubicación

Opciones de visualización​

const joryio = new JoryioSDK({
sdkKey: 'jry_sdk_web_YOUR_KEY',
inApp: {
displayOptions: {
defaultPosition: 'bottom', // Custom-type messages only
defaultAnimation: 'slide', // 'fade' (default), 'slide' or 'none'
backdropColor: 'rgba(10, 18, 64, 0.6)',
closeButtonColor: '#ffffff',
zIndex: 2147483000,
},
},
});

Callbacks​

Cada callback recibe el mensaje; su campaña es message.campaign (message.campaign.id, message.campaign.name).

const joryio = new JoryioSDK({
sdkKey: 'jry_sdk_web_YOUR_KEY',
inApp: {
onMessageDisplay: (message) => console.log('shown', message.campaign.id),
onMessageClick: (message, action) => console.log('clicked', message.campaign.id, action), // action: the button id
onMessageDismiss: (message, action) => console.log('dismissed', message.campaign.id, action), // e.g. 'close_button'
},
});

Los callbacks son avisos: el SDK sigue dibujando el mensaje.

Un callback que lanza un error se registra y se ignora: el SDK sigue registrando la impresión, el clic o el cierre, y limpia.

Cuando dos disparadores se activan a la vez (el mismo evento dos veces, por ejemplo), el mensaje se muestra - y se cuenta - una sola vez.

Control manual​

await joryio.syncInAppCampaigns(); // fetch campaigns now
const campaigns = joryio.getInAppCampaigns(); // what is currently eligible
await joryio.displayInAppCampaign(campaignId); // show one now
joryio.dismissInAppMessage(campaignId); // close it
await joryio.trackInAppConversion(campaignId, eventName); // record a conversion
MétodoDescripción
syncInAppCampaigns(attributeChanges?)Pide ahora al servidor las campañas de este visitante. No hace nada mientras el seguimiento está desactivado o el consentimiento está pendiente
getInAppCampaigns()Las campañas elegibles en este momento, tal como se sincronizaron
displayInAppCampaign(campaignId)Mostrar ahora una de esas campañas (su límite de frecuencia sigue aplicándose)
dismissInAppMessage(campaignId)Cerrar un mensaje visible (se registra como dismissed)
trackInAppConversion(campaignId, eventName)Registrar una conversión para una campaña

Cuándo un mensaje cuenta como mostrado​

Un mensaje cuenta como mostrado - se envía in_app.displayed y se consume el límite de frecuencia de la campaña - solo cuando la persona pudo verlo de verdad. El tiempo que debe estar en pantalla es IN_APP_DISPLAY_DWELL_MS (1000 ms), la misma constante en todos los SDK de Joryio.

  • Un mensaje aparece solo cuando la página ha terminado de cargar y está inactiva, y nunca mientras la página se está descargando (una redirección, un enlace que se sigue, la pestaña que se cierra).
  • Cuenta como mostrado después de estar 1 segundo en pantalla con la pestaña visible. Si la pestaña se oculta antes, el segundo vuelve a empezar cuando está visible.
  • Si la página se descarga antes - una redirección tras iniciar sesión, un enlace de cierre de sesión - no se envía nada, el límite no se consume y el mensaje se vuelve a mostrar en la página siguiente.
  • Un clic o un cierre antes cuenta como visto: primero se envía displayed y después el clic o el descarte. Quitar el mensaje desde el código (dismissInAppMessage, reset()) antes no envía nada.
  • onDisplay se llama cuando el mensaje cuenta como mostrado, junto con el envío - no cuando se pone en pantalla. El paquete exporta IN_APP_DISPLAY_DWELL_MS.

Dos interruptores independientes deciden si se recoge algo: el consentimiento (la respuesta a tu banner de cookies) y la desactivación que elige el propio visitante. El SDK recoge datos solo cuando el consentimiento está concedido y el visitante no ha desactivado el seguimiento - cualquier «no» prevalece.

joryio.updateConsent('granted'); // or 'denied' or 'pending'
joryio.optOut(); // remembered across page loads until optIn()
joryio.optIn();

joryio.ready(function (sdk) {
sdk.getTrackingConsent(); // 'granted' | 'denied' | 'pending'
sdk.isUserOptedOut(); // the opt-out switch only
});
EstadoQué hace el SDK
Consentimiento granted (o sin definir, sin una denegación guardada)Recoge y envía con normalidad
Consentimiento pendingRecoge localmente y no envía nada - ni Session Start, ni sincronización in-app, ni registro push - hasta que updateConsent('granted') lo libera. updateConsent('denied') lo descarta. Lo retenido se guarda en localStorage, así que una recarga no lo pierde: eventos e identify() (hasta maxQueueSize eventos), escrituras de atributos (hasta 500) y cambios de alias y suscripciones (hasta 200). Las bajas son la excepción: se envían igualmente. Mientras tanto, el service worker de web push no informa de entregas ni clics
Consentimiento deniedNo recoge nada y vacía lo que haya en cola. El service worker deja de informar de entregas y clics
optOut()No recoge nada, vacía los eventos en cola y las escrituras pendientes de atributos, alias y altas de suscripción (las bajas se siguen entregando), y se guarda para sobrevivir a una recarga. El service worker de web push sigue mostrando notificaciones pero deja de informar de entregas y clics
Do Not TrackSe trata como una desactivación mientras respectDoNotTrack sea true (el valor predeterminado)

Cómo se comportan los dos interruptores:

  • denied se recuerda. Sigue vigente en las siguientes páginas hasta updateConsent('granted') o un trackingConsent: 'granted' explícito en la configuración de init. Dejar trackingConsent sin definir mantiene una denegación guardada - por eso la opción no tiene valor predeterminado.
  • pending vale para una página. Nunca se guarda y nunca prevalece sobre una denegación guardada.
  • El consentimiento nunca levanta una desactivación. updateConsent('granted') no deshace optOut(); solo optIn() lo hace. optOut() y optIn() nunca cambian el consentimiento guardado.
  • El perfil registra la desactivación. Cuando cambia el interruptor de desactivación - también con optOut: true en init - el SDK escribe tracking_opted_out (true o false) en el perfil, con los ID del visitante que tomó la decisión, y lo reintenta según la regla de reintento. No se envía nada mientras el interruptor no cambia. true se envía incluso con el consentimiento denied o pending; false espera hasta que se permita el seguimiento.
  • Las retiradas siempre se entregan. Una baja (SubscriptionStatus.UNSUBSCRIBED, removeFromSubscriptionGroup()), el marcador tracking_opted_out y una baja del registro push se envían incluso con el visitante desactivado, con el consentimiento denied o pending, y después de wipeData(). Todas las demás escrituras se detienen en esos casos.
  • Una sesión que empezó con el seguimiento desactivado recibe su único Session Start cuando se concede el consentimiento o el visitante lo vuelve a activar.
  • identify() con el seguimiento desactivado (consentimiento denegado, o el visitante se excluyó) no envía nada ni escribe nada en ningún perfil. El ID de usuario se guarda solo en este dispositivo, para que una baja o una exclusión posterior llegue a esa persona y no a un perfil anónimo; el identify() normal se ejecuta cuando el seguimiento vuelve a estar permitido. reset() y wipeData() lo olvidan. Al conceder el consentimiento o volver a activar el seguimiento, el identify() guardado se ejecuta antes del Session Start de una sesión que empezó entretanto, así que esa sesión es del nuevo usuario. El ID guardado se usa en todas las retiradas: una baja, una baja de grupo, el marcador tracking_opted_out y push.unsubscribe(). Un identify() con el seguimiento permitido lo sustituye, y un userId explícito en la configuración de inicio tiene prioridad sobre él.
  • Todas las pestañas abiertas lo siguen. Una exclusión, un cambio de consentimiento, un reset() o un identify() hechos en una pestaña de tu sitio se aplican al instante en sus otras pestañas abiertas, no en su próxima recarga. Un cierre de sesión en una pestaña es un reset() completo en las demás: pasan al nuevo visitante y a la nueva sesión, y las escrituras que hicieron para el usuario saliente siguen siendo suyas. Un consentimiento en una pestaña también libera una pestaña que espera en pending. Otra pestaña o script que llama a localStorage.clear() nunca levanta una exclusión ni una denegación: la pestaña la conserva y la vuelve a guardar. Las escrituras de perfil y los tokens de usuarios que cerraron sesión que cada pestaña guarda en localStorage se fusionan; ninguna pestaña los sobrescribe. También se sigue al usuario que un identify() guarda para las retiradas mientras el seguimiento está desactivado. Un updateConsent('granted') u optIn() explícito justo después de ese localStorage.clear() es más reciente que la decisión guardada y gana en todas las pestañas. Una escritura de perfil que otra pestaña ya entregó, o reemplazó por una más reciente, esta pestaña no la envía nunca, y wipeData() en una pestaña descarta también las escrituras pendientes de las demás (salvo las retiradas).
  • Con el snippet asíncrono, estas llamadas surten efecto antes de que init envíe nada - consulta Snippet asíncrono.

Borrar los datos locales​

await joryio.wipeData();

Borra todo lo que el SDK guarda sobre el visitante en este navegador: la identidad (un ID anónimo nuevo, sin ID de usuario), los eventos en cola y un identify() pendiente, las escrituras pendientes de atributos, alias y suscripciones, el estado in-app y el historial de impresiones, la sesión, los datos UTM, el ID del dispositivo y la suscripción push (también se da de baja en Joryio; sigue de baja hasta que se vuelva a llamar a push.subscribe()). Las retiradas que aún esperan - una baja, el marcador de desactivación, la baja del registro push - se siguen entregando, con los ID con los que se hicieron. Conserva la desactivación y una denegación de consentimiento, y no desactiva a nadie - para eso está optOut(). El seguimiento continúa como un visitante anónimo nuevo, en una sesión nueva con su propio Session Start.

Referencia de API​

Inicializar​

const joryio = new JoryioSDK(config)

Inicializa el SDK con tu configuración. El constructor devuelve un singleton: al construirlo una segunda vez devuelve la instancia existente, que también devuelve JoryioSDK.getInstance() (o null antes de inicializar).

El SDK nunca lanza errores hacia tu página. Una sdkKey ausente o mal formada se registra una vez, sea cual sea el nivel de registro, y esa instancia ignora todas las llamadas; un argumento null donde se necesita un valor se registra y se ignora.

Identificar​

joryio.identify(userId)

Asocia un ID de usuario con la sesión actual.

Parámetros:

  • userId (cadena): identificador único del usuario.

Ejemplo:

joryio.identify('user_123');
joryio.setAttributes({
name: 'John Doe',
plan: 'premium',
});

Rastrear​

joryio.track(eventName, properties?)

Rastrea un evento personalizado con propiedades opcionales.

Parámetros:

  • eventName (cadena): nombre del evento.
  • properties (objeto, opcional): propiedades del evento.

Ejemplo:

joryio.track('Plan Upgraded', {
from_plan: 'basic',
to_plan: 'premium',
});

Para las compras usa el asistente de e-commerce, que envía las claves que leen los informes de ingresos (orderId y total en un evento Order Completed):

joryio.ecommerce({ currency: 'USD' }).purchase({
orderId: 'order_789',
items: [{ productId: 'SKU123', name: 'Blue Shirt', price: 49.99, quantity: 3 }],
value: 149.99, // sent as `total`
});

Consulta Seguimiento de e-commerce para todos sus métodos. Cada método comprueba sus argumentos obligatorios - un productId, un orderId, un value de pedido finito, un array items - y si falta uno registra una advertencia y no envía nada (nunca un pedido vacío o de valor cero). Nunca lanza una excepción en tu página. ecommerce() acepta una opción, currency ('USD' por defecto).

Rastrear vistas de página

Activa el rastreo automático de vistas de página:

const joryio = new JoryioSDK({
sdkKey: 'jry_sdk_web_...',
trackPageViews: true
});

Añadir un alias​

joryio.addAlias(aliasLabel, aliasName)

Añade un alias etiquetado a este visitante. Funciona también antes de identify(): la solicitud lleva el ID anónimo (y el ID de usuario una vez identificado), y el alias se asocia al perfil al que estos correspondan.

Para vincular un visitante anónimo con su ID de usuario, llama a identify(). En el primer inicio de sesión el perfil anónimo pasa a ser el perfil del usuario, con todo su historial; si el usuario ya tiene un perfil, el anónimo se fusiona con él. El alias() sin etiqueta se eliminó en la versión 1.3.0.

Ejemplo:

joryio.addAlias('crm', 'crm_98765');

Restablecer​

joryio.reset()

Cierra la sesión del usuario actual. El visitante recibe un ID anónimo nuevo y una sesión nueva; se borran los mensajes in-app del usuario anterior y se olvida su token de autenticación del SDK; sus escrituras de atributos, alias y suscripciones aún no confirmadas se siguen enviando, con sus propios ID - nunca con los del siguiente visitante; se borran los datos UTM (incluida la atribución de primer y último contacto); se obtienen las campañas in-app para el nuevo visitante; y una suscripción web push existente se registra de nuevo con el nuevo ID anónimo, de modo que las notificaciones del usuario anterior dejan de llegar a este navegador.

La identidad nueva está en vigor en cuanto reset() vuelve. La promesa que devuelve solo cubre el envío de lo que ya estaba en cola (cada evento en cola conserva los ID con los que se registró), así que reset(); identify(nextUser) es seguro sin await.

Ejemplo:

// Al cerrar sesión el usuario
function handleLogout() {
joryio.reset();
// ... otra lógica de cierre de sesión
}

Obtener el ID anónimo​

joryio.getAnonymousId()

Devuelve el ID anónimo actual: el ID que Joryio asigna a cada visitante antes de identificarlo. Se genera en la primera inicialización, se conserva en localStorage y permanece estable durante la vida del visitante anónimo entre cargas de página (reset() lo genera de nuevo al cerrar sesión). Es el anonymousId exacto que se adjunta a cada evento que envía track(), por lo que puedes usarlo para vincular un evento del lado del servidor o una entrada de registro de consentimiento con el mismo perfil.

Devuelve:

  • string: el ID anónimo (siempre presente).
Léelo después de cargar el SDK

Con el fragmento asíncrono, window.joryio es una cola de comandos hasta que termine de cargar el SDK, y una llamada en cola no puede devolver un valor. Lee el ID en la instancia resuelta tras la carga o desde ready() (más abajo).

Ejemplo:

joryio.ready(function (sdk) {
const anonId = sdk.getAnonymousId();
// adjúntalo a tu propio registro de consentimiento o evento de servidor
fetch('/consent', { method: 'POST', body: JSON.stringify({ anonymousId: anonId }) });
});

Obtener el ID de usuario​

joryio.getUserId()

Devuelve el ID de usuario identificado actual, o null si el visitante sigue siendo anónimo; es decir, si no se ha llamado a identify().

Devuelve:

  • string | null

Ejemplo:

joryio.ready(function (sdk) {
const userId = sdk.getUserId(); // null hasta llamar a joryio.identify(...)
});

Ready​

joryio.ready(callback)

Ejecuta callback(sdk) cuando el SDK esté cargado e inicializado. Es la forma segura de leer un valor, como getAnonymousId() / getUserId(), que un stub de fragmento en cola no puede devolver antes de cargarse. callback recibe la instancia del SDK; si ya está cargado, se ejecuta de inmediato.

Ejemplo:

joryio.ready(function (sdk) {
console.log('anon:', sdk.getAnonymousId(), 'user:', sdk.getUserId());
});

Obtener datos UTM​

joryio.getUTMData()

Obtiene los parámetros UTM actuales, de primer contacto y de último contacto.

Devuelve:

  • Objeto con datos UTM current, firstTouch y lastTouch.

Ejemplo:

const utmData = joryio.getUTMData();
console.log(utmData.current?.utm_source); // "google"
console.log(utmData.firstTouch?.utm_campaign); // "awareness_campaign"
console.log(utmData.lastTouch?.utm_campaign); // "conversion_campaign"

Actualizar UTM​

joryio.updateUTM()

Actualiza manualmente los parámetros UTM desde la URL actual. Es útil para aplicaciones de una sola página que cambian URL sin recargar la página.

Ejemplo:

// React Router
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';

function App() {
const location = useLocation();

useEffect(() => {
joryio.updateUTM();
}, [location]);
}

// Vue Router
router.afterEach(() => {
joryio.updateUTM();
});

Atributos​

joryio.setAttributes(attributes)
joryio.setAttribute(key, value)
joryio.incrementAttribute(key, by?)
joryio.unsetAttribute(key)
  • setAttributes({ plan: 'pro', city: 'Athens' }) define varios a la vez; setAttribute('plan', 'pro') define uno.
  • Un valor null o undefined elimina el atributo, igual que unsetAttribute(key).
  • Un valor que JSON no puede transportar - NaN, Infinity, un BigInt, una función, un objeto que se referencia a sí mismo - se descarta con un aviso en el log: esa clave se omite y el resto de la llamada sigue adelante. Lo mismo vale para las propiedades de eventos. Un valor defectuoso nunca retiene otras escrituras.
  • incrementAttribute('logins', 1) suma a un número en el servidor (1 por defecto), así que dos pestañas nunca sobrescriben el recuento de la otra.
  • Las escrituras cercanas en el tiempo se envían en una sola solicitud. Esperan a que un identify() pendiente llegue antes al servidor, se guardan en localStorage hasta que se confirman, y una escritura fallida se reintenta según la regla de reintento.
  • Una escritura conserva la identidad con la que se hizo: una hecha justo antes de identify() se envía con ese ID de usuario una vez que el identify ha llegado al servidor; una del usuario A que aún espera cuando se llama a identify('B') o reset() sigue yendo a A.

Atributos de matriz​

joryio.addToArray(key, value)
joryio.removeFromArray(key, value)

Manipula atributos de matriz de forma eficiente.

Parámetros:

  • key (cadena): nombre del atributo.
  • value (cualquiera): valor que se añadirá o eliminará.

Ejemplo:

// Añade etiquetas al usuario
joryio.addToArray('tags', 'vip');
joryio.addToArray('tags', 'premium');
// Resultado: tags = ['vip', 'premium']

// Añade un duplicado (no hace nada; evita duplicados)
joryio.addToArray('tags', 'vip');
// Resultado: tags = ['vip', 'premium'] (sin cambios)

// Elimina una etiqueta
joryio.removeFromArray('tags', 'vip');
// Resultado: tags = ['premium']

// Casos de uso frecuentes
joryio.addToArray('interests', 'technology');
joryio.addToArray('purchasedProducts', 'prod_123');
joryio.addToArray('featureFlags', 'beta-access');

Comportamiento:

  • addToArray() solo añade el valor si aún no existe, para evitar duplicados.
  • addToArray() crea una matriz nueva si el atributo no existe.
  • removeFromArray() elimina todas las instancias del valor.
  • Los cambios se sincronizan automáticamente con el backend, tanto para usuarios identificados como anónimos.

Las llamadas sobre un mismo atributo conservan su orden: dos llamadas a addToArray() sobre la misma clave llegan ambas al servidor, y un addToArray() seguido de un removeFromArray() de la misma clave se envían uno tras otro, nunca en la misma solicitud.

Flush​

await joryio.flush()

Envía ahora lo que haya en cola en lugar de esperar al siguiente lote. También espera a un envío que ya esté en curso.

flush() se salta la espera de reintento del propio SDK, pero nunca un Retry-After que haya pedido la API.

Información de identidad y sesión​

joryio.getIdentity() // { userId, anonymousId, attributes }
joryio.getSessionInfo() // { sessionId, sessionStart, lastActivity, isNewSession }

attributes contiene lo que definió esta página (no se lee del servidor).

joryio.getTrackingConsent() // 'granted' | 'denied' | 'pending'
joryio.isUserOptedOut() // true after optOut(), until optIn()

isUserOptedOut() refleja solo la desactivación del propio visitante - ni el consentimiento ni Do Not Track. Consulta Privacidad y consentimiento. Con el snippet asíncrono, léelos dentro de ready().

Destroy​

await joryio.destroy()

Envía lo que haya en cola, detiene todos los temporizadores, quita los listeners de la página y descarga los plugins. Un new JoryioSDK(config) posterior crea una instancia nueva.

Web push​

Web push necesita un service worker en tu propio origen (los navegadores lo exigen). Aloja en la raíz de tu sitio un archivo llamado joryio-sw.js con una sola línea:

importScripts('https://sdk.joryio.com/web/1/joryio-sw.js');

Después pide permiso desde un gesto del usuario, como un clic en un botón:

if (joryio.push.isSupported() && joryio.push.getPermissionState() === 'notDetermined') {
button.onclick = () => joryio.push.subscribe();
}
MétodoDescripción
push.isSupported()Si este navegador admite web push (hace falta HTTPS, un service worker y la Push API)
push.getPermissionState()'granted', 'denied', 'notDetermined' o 'unavailable' - los mismos nombres en todos los SDK de Joryio
push.prepare(options?)Registra el service worker y obtiene la clave de antemano, para que el subscribe() posterior pida permiso al instante. Nunca pide permiso
push.subscribe(options?)Pide permiso (si hace falta), suscribe y registra este navegador en Joryio. Devuelve la suscripción o null
push.getExistingSubscription(options?)La suscripción actual, sin pedir permiso
push.unsubscribe(options?)Elimina la suscripción del navegador y la da de baja en Joryio. Sigue de baja en las visitas siguientes hasta que se llame de nuevo a subscribe(). Nunca se rechaza; devuelve false cuando no había nada que eliminar

options acepta swPath (por defecto '/joryio-sw.js') y scope (por defecto '/') si alojas el worker en otro sitio.

No hace falta llamar de nuevo a subscribe() en visitas posteriores. Si el permiso ya está concedido, el SDK revisa la suscripción del navegador al cargar y la registra de nuevo si el navegador la cambió, si cambió la clave de tu espacio de trabajo (se vuelve a suscribir sin pedir permiso) o después de reset(), salvo que se haya llamado a unsubscribe(). Cada registro lleva el ID de dispositivo estable del SDK. No se registra nada mientras el seguimiento esté excluido o el consentimiento pendiente. La baja del registro de unsubscribe() es una retirada: se envía incluso entonces, se guarda hasta que la API responde y se reintenta según la regla de reintento. Registrar de nuevo el mismo navegador cancela una baja del registro aún no enviada.

El service worker muestra cada push con su título, texto, icono, imagen, badge, acciones, tag (un push más reciente con el mismo tag sustituye al que está en pantalla) y requireInteraction (permanece hasta que se cierra), informa de su entrega y registra el clic. Lee estas claves del data del push, los mismos nombres que leen los SDK de Android e iOS:

ClaveEfecto
trackingIdSe usa para el acuse de entrega y el seguimiento de clics. No aparece en los envíos de prueba, que no se cuentan
jry_deep_linkLa página que abre un clic (la raíz del sitio si falta)
jry_image_urlImagen grande, en los navegadores que la muestran
jry_api_originAdónde envía el worker el acuse y el clic
jry_campaign_idEl ID de la campaña, o el del recorrido en un envío desde un recorrido. Informativo

Autenticación del SDK​

Opcional. Cuando está activa, cada solicitud lleva un JWT que emite tu backend, en la cabecera X-Joryio-Auth, y el SDK lo renueva cuando la API lo rechaza. El token prueba quién es un visitante identificado: los visitantes anónimos siguen funcionando sin él.

Tu backend firma el JWT con una clave que registras en Joryio, con un sub igual al ID de usuario que pasas a identify(). Un token incorrecto, caducado o ausente recibe 401 { error: 'sdk_authentication_error', reason: 'missing' | 'invalid' | 'expired' | 'sub_mismatch' }.

const joryio = new JoryioSDK({
sdkKey: 'jry_sdk_web_YOUR_KEY',
enableSdkAuthentication: true,
sdkAuthenticationToken: await mintJoryioJwt(),
});

// Fires on a 401 sdk_authentication_error. Return a fresh token (or call
// setSdkAuthenticationToken); the failed request is then retried once.
joryio.setSdkAuthenticationErrorCallback(async (info) => {
// info = { reason, endpoint, userId }
return await mintJoryioJwt();
});

// Replace the token at any time, e.g. before it expires
joryio.setSdkAuthenticationToken(await mintJoryioJwt());
MétodoDescripción
setSdkAuthenticationToken(token)Define o sustituye el JWT. Se ignora (con una advertencia) salvo que enableSdkAuthentication sea true
setSdkAuthenticationErrorCallback(callback)callback(info) se ejecuta cuando se rechaza un token; devuelve un token nuevo o una promesa de uno
  • El callback se ejecuta en un 401 de autenticación del SDK para una solicitud del usuario actual mientras enableSdkAuthentication esté activo - incluso antes de establecer ningún token (reason: 'missing'). Nunca se ejecuta para una solicitud de un usuario que ya cerró sesión, así que no puede hacer que tu app cierre la sesión de la persona que la está usando ahora.
  • Un reintento inmediato. La solicitud fallida se reintenta una vez con el token nuevo. Si no hay callback, no devuelve token o el reintento también falla, no se descarta nada: los eventos siguen en cola y se reintentan con la espera normal, con el token que haya entonces. Una escritura de perfil se reintenta como máximo 10 veces.
  • El token de un usuario que cerró sesión se conserva para lo que dejó pendiente. En reset() - o en identify() de otro usuario - el token deja de ser el de la sesión pero sigue ligado a los eventos y escrituras en cola del usuario saliente, se guarda con ellos en localStorage para que una recarga no lo pierda, y se borra en cuanto el último de ellos se entrega o se descarta. Un token que configures para el usuario entrante antes de llamar a identify() se reconoce por su sub y sigue siendo suyo. Establecer un token cuyo sub es otro usuario aparta del mismo modo el token al que sustituye, así que el orden no importa: lo que dejó el usuario saliente sigue llevando su token. Renovar el token del mismo usuario no aparta nada. Un token apartado se conserva al menos un minuto y como máximo 30 días. Un token cuyo sub no es el usuario actual se conserva así para ese usuario también cuando nadie ha iniciado sesión, hasta que identify() lo convierte en el de la sesión; wipeData() borra un token así.
  • Un usuario por solicitud. Los eventos nunca se agrupan a través de un cambio de usuario. reset() quita el token de la sesión; sigue con el trabajo en cola del usuario saliente. Los eventos y escrituras que el usuario saliente ya había puesto en cola siguen enviándose con su propio token, así que un cierre de sesión normal no pierde nada. Los eventos, un identify() y las escrituras de perfil de un usuario que ya no es el actual, rechazados con cualquier 401 de autenticación del SDK (missing, sub_mismatch, expired o invalid), se descartan - no se volverá a establecer un token para ese usuario - así que nunca retienen lo del siguiente usuario. Una solicitud lleva solo un token cuyo sub es el usuario de esa solicitud: el token actual cuando es suyo, si no el token apartado para él, si no ninguno - así un token que configuras para el siguiente usuario antes de identify() nunca sale en las solicitudes del usuario actual. Las solicitudes anónimas no llevan token.

Preferencias de suscripción​

El objeto user, de solo escritura, actualiza las suscripciones de marketing de un visitante. Los cambios esperan a que un identify() pendiente llegue al servidor, llevan el ID de usuario y el ID anónimo, y se reintentan si el contacto aún no existe. Un cambio hecho antes de identify() se envía con el ID de usuario una vez que el identify ha llegado al servidor, así que la autenticación del SDK lo acepta. Los cambios se guardan en localStorage hasta que se confirman y se entregan en orden: un cambio más reciente de un canal o grupo sustituye a uno anterior que aún espera. Una baja se entrega incluso con el visitante desactivado o con el consentimiento denied.

import { SubscriptionStatus } from '@joryio/web-sdk';

joryio.user.setSubscription('email', SubscriptionStatus.OPTED_IN);
joryio.user.setSubscriptions({
email: SubscriptionStatus.OPTED_IN,
sms: SubscriptionStatus.UNSUBSCRIBED,
});

joryio.user.addToSubscriptionGroup('newsletter-list-id', 'email');
joryio.user.removeFromSubscriptionGroup('newsletter-list-id', 'email');
MétodoDescripción
user.setSubscription(channel, status)Un canal
user.setSubscriptions(preferences)Varios canales, como { channel: status }
user.addToSubscriptionGroup(groupId, channel?)Unirse a un grupo de suscripción (lista); channel es 'email' por defecto
user.removeFromSubscriptionGroup(groupId, channel?)Salir de un grupo de suscripción

Canales: email, sms, whatsapp, push, viber. Estados: SubscriptionStatus.OPTED_IN (consentimiento explícito, por ejemplo double opt-in confirmado), SubscriptionStatus.SUBSCRIBED (consentimiento simple) y SubscriptionStatus.UNSUBSCRIBED. Con la versión de CDN, pasa las cadenas 'optedIn', 'subscribed' o 'unsubscribed'.

Exportaciones del paquete​

ExportaciónQué es
default / JoryioSDKLa clase del SDK (window.Joryio en la versión de CDN)
SubscriptionStatusLos estados de suscripción de arriba
User, PushManager, EcommerceTrackerLas clases detrás de joryio.user, joryio.push y joryio.ecommerce(), para los tipos; no las construyes tú
createEcommerceTracker(sdk, config?)Lo mismo que sdk.ecommerce(config)
SDK_VERSIONLa versión del SDK como cadena, por ejemplo '1.3.3'
TiposInitConfig, InAppOptions, InAppDisplayOptions, InAppCampaign, InAppMessage, InAppTrigger, Plugin, TrackingConsent, PushPermissionState, PushSubscribeOptions, SubscriptionChannel, SubscriptionPreferences, UTMParameters, UTMData, EcommerceProduct, EcommerceCartItem, EcommerceOrder, EcommerceConfig, SdkAuthFailureInfo, SdkAuthFailureReason, SdkAuthFailureCallback

installSnippetQueue se exporta para las pruebas del propio SDK; no lo llames.

Datos automáticos de sesión​

El SDK web enriquece automáticamente los eventos Session Start con datos de dispositivo y entorno. Estos valores se guardan en el perfil de usuario y los registros de dispositivo:

  • $user_agent
  • $timezone
  • $screen_width / $screen_height
  • $viewport_width / $viewport_height
  • $language / $languages
  • $platform
  • $browser
  • $device_id
  • country (ISO-3166-1 alfa-2, derivado de la IP al iniciar la sesión)

Propiedades de evento​

Propiedades estándar​

Todos los eventos almacenados incluyen estas propiedades. El SDK adjunta $device_id y la canalización de ingesta de Joryio añade las demás al recibir el evento:

  • $device_id: ID de dispositivo estable por navegador (adjuntado por el SDK).
  • $session_id: ID de sesión actual.
  • $anonymous_id: ID de usuario anónimo (antes de identificarlo).
  • $app_id: ID de tu app.
  • $app_name: nombre de tu app.
  • $platform: siempre «web» en el SDK web.
  • $is_identified: indica si el usuario está identificado.

El prefijo $ está reservado: no lo uses para tus propias propiedades.

Propiedades personalizadas​

Puedes añadir cualquier propiedad personalizada a tus eventos:

joryio.track('Video Played', {
video_id: 'vid_123',
video_title: 'Product Demo',
duration: 120,
autoplay: false,
// Cualquier otro dato personalizado
});

Prácticas recomendadas​

1. Inicializar pronto​

Inicializa el SDK lo antes posible en tu aplicación:

// En el archivo principal de tu app
import JoryioSDK from '@joryio/web-sdk';

const joryio = new JoryioSDK({
sdkKey: process.env.JORYIO_SDK_KEY,
enableDebug: process.env.NODE_ENV === 'development',
});

2. Rastrear eventos significativos​

Céntrate en rastrear los eventos que importan para tu negocio:

// Bien: eventos específicos y accionables
joryio.track('Trial Started', { plan: 'premium' });
joryio.track('Feature Used', { feature: 'export', format: 'csv' });

// Evita: eventos demasiado genéricos
joryio.track('Button Clicked'); // Demasiado genérico

3. Usar nombres coherentes​

Usa una convención de nombres coherente para eventos y propiedades:

// Bien: nombres claros y coherentes
joryio.track('Subscription Upgraded', {
from_plan: 'basic',
to_plan: 'premium',
billing_cycle: 'monthly',
});

// Evita: nombres incoherentes
joryio.track('upgraded_subscription', {
FromPlan: 'basic',
'to-plan': 'premium',
});

4. Gestionar el ciclo de vida del usuario​

Gestiona correctamente la identificación de usuarios y las sesiones:

// Al iniciar sesión
function handleLogin(userId, userInfo) {
joryio.identify(userId);
joryio.setAttributes({
email: userInfo.email,
name: userInfo.name,
});
}

// Al cerrar sesión
function handleLogout() {
joryio.reset();
}

// Al registrarse
function handleSignup(userId, userInfo) {
joryio.identify(userId);
joryio.setAttributes(userInfo);
}

Solución de problemas​

Los eventos no aparecen​

  1. Comprueba tu clave de SDK: asegúrate de que comienza por jry_sdk_web_ (la clave de una integración de tienda también funciona). La clave de una app móvil (jry_sdk_ios_, jry_sdk_android_) se rechaza: el SDK registra el motivo y queda inactivo.
  2. Comprueba la consola: activa el modo de depuración para ver registros detallados.
  3. Verifica la inicialización: asegúrate de que new JoryioSDK(config) se ejecuta antes de llamar a otros métodos.

Errores de CORS​

Los endpoints de rastreo del SDK responden con Access-Control-Allow-Origin: *, por lo que no hace falta incluir dominios en una lista de permitidos. Si sigues viendo errores de CORS, comprueba que apiEndpoint apunte a la URL base correcta (https://api-eu1.joryio.com/api) y que una extensión del navegador o un proxy que elimina encabezados CORS no esté bloqueando la solicitud.

Problemas de rastreo de sesión​

El SDK usa localStorage para conservar las sesiones. Asegúrate de que:

  • Tu sitio se sirva por HTTPS (obligatorio para contextos seguros).
  • Los usuarios no hayan desactivado localStorage.
  • No estés llamando a reset() sin querer.

Siguientes pasos​