Saltar al contenido principal

Gestión de suscripciones

Este documento describe el sistema completo de gestión de suscripciones y consentimiento de Joryio, que gestiona suscripciones por canal, suscripciones basadas en listas/temas, encabezados List-Unsubscribe de RFC 8058, manejo de rebotes y funciones de cumplimiento.

Índice

  1. Resumen
  2. Modelos de datos
  3. Suscripciones de canal
  4. Integración con SDK web
  5. Listas de suscripción
  6. Grupos de suscripción por número (SMS y WhatsApp)
  7. Encabezado List-Unsubscribe (RFC 8058)
  8. Página de preferencias (diseño personalizado)
  9. Manejo de rebotes
  10. Variables de plantilla Liquid
  11. Filtros de segmento
  12. Referencia de API
  13. Configuración
  14. Cumplimiento

Resumen

El sistema de gestión de suscripciones de Joryio ofrece:

  • Suscripciones por canal: rastrea la aceptación/exclusión de email, SMS, WhatsApp, notificaciones push y Viber.
  • Suscripciones basadas en listas: permite que los usuarios se suscriban a temas o listas de correo específicos.
  • Cumplimiento de RFC 8058: encabezados de baja en un clic para mejorar la entregabilidad de email.
  • Manejo de rebotes: gestión automática de rebotes suaves y permanentes.
  • Registro de auditoría: historial completo de cambios de suscripción para cumplimiento.
  • Filtrado de segmentos: dirige usuarios según su estado de suscripción.

Modelos de datos

Suscripción de canal (por usuario)

Cada usuario tiene un estado de suscripción para cada canal de comunicación:

interface ChannelSubscription {
status: 'optedIn' | 'subscribed' | 'unsubscribed';
optInDate?: Date;
optOutDate?: Date;
optInSource?: string; // 'api', 'web_form', 'import', 'manual'
consentText?: string; // Texto de consentimiento mostrado al aceptar
}

interface EmailSubscription extends ChannelSubscription {
bounceType?: 'soft' | 'hard' | null;
bounceCount?: number;
lastBounceAt?: Date;
isValid?: boolean; // false si tuvo un rebote permanente
}

interface UserSubscriptions {
email?: EmailSubscription;
sms?: ChannelSubscription;
whatsapp?: ChannelSubscription;
push?: ChannelSubscription;
viber?: ChannelSubscription;
}

Lista de suscripción

Las listas son temas o categorías a los que los usuarios pueden suscribirse:

interface SubscriptionList {
id: string;
organizationId: string;
workspaceId: string;
name: string;
description?: string;
channels: ('email' | 'sms' | 'whatsapp' | 'push' | 'viber')[];
isPublic: boolean; // Mostrar en el centro de preferencias
type: 'marketing' | 'transactional';
requireDoubleOptIn: boolean;
archivedAt?: Date;
createdAt: Date;
updatedAt: Date;
}

Pertenencia a lista

Rastrea a qué listas está suscrito cada usuario:

interface ListSubscription {
contactId: string;
listId: string;
channel: 'email' | 'sms' | 'whatsapp' | 'push' | 'viber';
status: 'optedIn' | 'subscribed' | 'unsubscribed';
subscribedAt?: Date;
unsubscribedAt?: Date;
optInSource?: string;
}

Suscripciones de canal

Tipos de estado

EstadoDescripción
optedInEl usuario completó la confirmación de doble opt-in.
subscribedEl usuario está suscrito (aceptación simple).
unsubscribedEl usuario se dio de baja.

Actualizar el estado de canal

// Mediante API
await subscriptionsApi.updateChannelSubscription(userId, 'email', {
status: 'unsubscribed',
source: 'preference_center',
reason: 'User requested via preference center'
});

Prácticas recomendadas

  • Registra siempre la fuente de los cambios de suscripción.
  • Guarda el texto de consentimiento cuando los usuarios acepten.
  • Para cumplir el RGPD, usa doble opt-in con usuarios europeos.

Integración con SDK web

El SDK web de Joryio permite gestionar las preferencias de suscripción de usuarios directamente desde tu sitio o aplicación web.

Instalación

<!-- Mediante etiqueta script (servida desde la API de Joryio) -->
<script src="https://api-eu1.joryio.com/sdk/web/latest/joryio.min.js"></script>

<!-- O mediante npm -->
npm install @joryio/web-sdk

Tipos de estado de suscripción

El SDK proporciona tres valores de estado de suscripción:

EstadoDescripción
SubscriptionStatus.OPTED_INEl usuario aceptó explícitamente (por ejemplo, doble opt-in confirmado).
SubscriptionStatus.SUBSCRIBEDEl usuario está suscrito pero no aceptó explícitamente.
SubscriptionStatus.UNSUBSCRIBEDEl usuario se dio de baja.

Definir suscripciones de canal

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

// Inicializa el SDK
const sdk = new JoryioSDK({ sdkKey: 'jry_sdk_web_...' });

// Define la suscripción de un canal (solo escritura por seguridad, sin getters)
sdk.user.setSubscription('email', SubscriptionStatus.OPTED_IN);
sdk.user.setSubscription('sms', SubscriptionStatus.SUBSCRIBED);
sdk.user.setSubscription('push', SubscriptionStatus.UNSUBSCRIBED);
sdk.user.setSubscription('whatsapp', SubscriptionStatus.SUBSCRIBED);

// Define varios canales a la vez
sdk.user.setSubscriptions({
email: SubscriptionStatus.OPTED_IN,
sms: SubscriptionStatus.UNSUBSCRIBED,
whatsapp: SubscriptionStatus.SUBSCRIBED,
push: SubscriptionStatus.OPTED_IN
});

Gestionar grupos de suscripción (listas)

// Añade el usuario a un grupo/lista de suscripción
sdk.user.addToSubscriptionGroup('newsletter-list-id', 'email');
sdk.user.addToSubscriptionGroup('product-updates-id', 'push');

// Elimina el usuario de un grupo/lista de suscripción
sdk.user.removeFromSubscriptionGroup('newsletter-list-id', 'email');

// El parámetro de canal es opcional; el predeterminado es 'email'
sdk.user.addToSubscriptionGroup('weekly-digest-id');

Notas de seguridad

El objeto User del SDK es solo de escritura por motivos de seguridad:

  • No hay método getSubscriptions(): impide que otros sitios/scripts lean datos de suscripción del usuario.
  • No hay métodos getter: todas las operaciones son escrituras unidireccionales al backend.
  • Autenticación con clave de SDK: todas las solicitudes se autentican con tu clave de SDK.
  • El SDK siempre actúa como «el usuario actual»: setSubscription / addToSubscriptionGroup no reciben ID de destino; se aplican a quien el SDK identifica actualmente (el propio ID de un visitante anónimo o el usuario enviado a identify()). Un visitante no puede cambiar mediante el SDK las suscripciones de otro.
  • Con autenticación de SDK activada, los cambios de suscripción requieren un usuario identificado. Un anonymousId se genera en el cliente y no se puede vincular criptográficamente a tu token firmado; una vez activada la autenticación de SDK, que opta por escrituras vinculadas a token, se rechaza un cambio de suscripción solo anónimo. Llama antes a identify(userId) para que el cambio quede vinculado al token. Con la autenticación de SDK desactivada, la aceptación/baja anónima se admite como antes.

Compatibilidad con TypeScript

import {
JoryioSDK,
SubscriptionStatus,
SubscriptionChannel,
SubscriptionPreferences
} from '@joryio/web-sdk';

const sdk = new JoryioSDK({ sdkKey: 'jry_sdk_web_...' });

// Actualizaciones de suscripción seguras por tipos
const preferences: SubscriptionPreferences = {
email: SubscriptionStatus.OPTED_IN,
sms: SubscriptionStatus.UNSUBSCRIBED,
};

sdk.user.setSubscriptions(preferences);

// Selección de canal segura por tipos
const channel: SubscriptionChannel = 'email';
sdk.user.setSubscription(channel, SubscriptionStatus.OPTED_IN);

Ejemplo: página de configuración de preferencias

// En tu página de configuración
function handleSubscriptionToggle(channel, isEnabled) {
sdk.user.setSubscription(
channel,
isEnabled ? SubscriptionStatus.SUBSCRIBED : SubscriptionStatus.UNSUBSCRIBED
);
}

// Uso
handleSubscriptionToggle('email', true); // Suscribirse a email
handleSubscriptionToggle('sms', false); // Darse de baja de SMS

Ejemplo: registro al newsletter

function subscribeToNewsletter(email) {
// Primero identifica el usuario
sdk.identify(email);
sdk.setAttributes({ email: email });

// Después suscríbelo a la lista de newsletter
sdk.user.setSubscription('email', SubscriptionStatus.OPTED_IN);
sdk.user.addToSubscriptionGroup('newsletter-list-id', 'email');
}

Listas de suscripción

Crear una lista

const list = await listsApi.create({
name: 'Weekly Newsletter',
description: 'Our weekly digest of product updates',
channels: ['email'],
isPublic: true, // Mostrar en el centro de preferencias
type: 'marketing',
requireDoubleOptIn: false
});

Gestionar miembros

// Suscribe un usuario a una lista
await subscriptionsApi.subscribeToList(userId, listId, 'email', {
source: 'api',
consentText: 'Weekly newsletter signup'
});

// Da de baja un usuario
await subscriptionsApi.unsubscribeFromList(userId, listId, 'email');

// Operaciones masivas (nunca vuelven a suscribir contactos que se dieron de baja)
await listsApi.bulkAddMembers(listId, contactIds, 'email');
await listsApi.bulkRemoveMembers(listId, contactIds, 'email');

Listas públicas frente a privadas

  • Listas públicas (isPublic: true): se muestran en el centro de preferencias; los usuarios pueden gestionarlas.
  • Listas privadas (isPublic: false): solo las gestionan los administradores; no se muestran a usuarios.

Grupos de suscripción por número (SMS y WhatsApp)

Una lista de suscripción vinculada a un remitente específico - es decir, que incluye un senderId - funciona como un grupo de suscripción por número. Para SMS, el grupo es por número; para WhatsApp, es por WABA (cuenta de WhatsApp Business). Así, un contacto puede darse de baja de los mensajes de un número o WABA y seguir suscrito a otros.

Alcance y almacenamiento de las bajas

  • Las bajas se guardan por grupo. Cada grupo por número registra su propio estado de baja, independiente del consentimiento global del contacto para el canal y de otros números o WABA.
  • Tanto las campañas como los recorridos respetan las bajas. Antes de enviar, el sistema comprueba el estado de baja del destinatario en el grupo del número o WABA remitente. Si el contacto se dio de baja de ese grupo, el envío se omite. En un recorrido, el contacto avanza al siguiente paso: se omite el mensaje, no el recorrido.
  • Número predeterminado de SMS. Si envías desde el número predeterminado del espacio de trabajo, las bajas se aplican al grupo de ese número.

Alcance de STOP / START entrantes

Cuando un destinatario responde con una palabra clave, esta se aplica al número concreto (SMS) o WABA (WhatsApp) en el que recibió el mensaje:

  • STOP (y cualquier otra palabra clave de baja salvo STOPALL) se aplica al grupo por número o por WABA: da de baja al contacto únicamente de los mensajes de ese número o WABA.
  • STOPALL se aplica a todo el canal: da de baja al contacto de todos los números o WABA de ese canal.
  • START vuelve a suscribir al contacto al grupo de ese mismo número o WABA.
Una URL de webhook entrante está ligada a un solo espacio de trabajo

Los espacios de trabajo son marcas distintas, así que un STOP da de baja al contacto de esa marca, no de toda la cuenta. Ese alcance viene de la URL de webhook entrante, que lleva un único espacio de trabajo.

Joryio no puede deducir la marca a partir del propio mensaje. Los remitentes se identifican por nombre (por ejemplo Acme), mientras que una respuesta llega a un número de teléfono: no hay nada que permita relacionarlos.

Por eso, cada respuesta y cada baja que llega a una URL entrante se registra en el espacio de trabajo de esa URL. Si una cuenta de proveedor da servicio a más de una marca, configura una URL entrante distinta por número en el portal del proveedor, o dale a cada marca su propia cuenta de proveedor. De lo contrario, un STOP destinado a la Marca B se registra en la Marca A, y la Marca B sigue enviando.

Palabras clave

El manejo de palabras clave entrantes de SMS se compone de tres capas: una base en inglés siempre activa, valores predeterminados localizados (activados por defecto) y tus propias adiciones personalizadas. Todas se combinan al buscar coincidencias.

Base en inglés (siempre activa, no se puede eliminar)

Son necesarias para cumplir la FCC y la CTIA, y no se pueden desactivar:

TipoPalabras clave
BajaSTOP, STOPALL, UNSUBSCRIBE, CANCEL, END, QUIT, REVOKE, OPTOUT
AltaSTART, YES, UNSTOP, SUBSCRIBE, OPTIN
AyudaHELP, INFO
Base de la FCC, abril de 2025

REVOKE y OPTOUT forman parte de la base de palabras de baja (la norma de la FCC de abril de 2025 sobre revocación de consentimiento por SMS). Siempre se respetan; no tienes que añadirlas ni activarlas.

Valores predeterminados localizados (activados por defecto, acumulativos)

Las palabras habituales de baja, alta y ayuda en otros idiomas se reconocen de forma predeterminada, para que cada contacto pueda darse de baja en su idioma. Por ejemplo:

IdiomaBajaAltaAyuda
Hebreoהסר, הסרה, עצור, ביטול, הפסקהתחל, הצטרף, כןעזרה, מידע
EspañolPARE, BASTA, CANCELAR, ALTOSI, ALTAAYUDA
FrancésARRET, ARRÊT, DESABONNEROUIAIDE
AlemánSTOPP, ABBESTELLENJAHILFE
PortuguésPARAR, SAIR--

Adiciones personalizadas (por organización)

En Configuración → SMS / Suscripciones puedes añadir tus propias palabras clave de baja, alta y ayuda (incluidas palabras de baja para todo el canal). La base y los valores localizados se muestran como solo lectura junto a la lista personalizada editable, por lo que solo gestionas tus adiciones. Las palabras clave de SMS se configuran a nivel de organización.

WhatsApp

TipoPalabras clave
BajaSTOP, STOPALL, UNSUBSCRIBE, CANCEL, END, QUIT, OPTOUT, OPT-OUT
AltaSTART, UNSTOP, SUBSCRIBE, YES, OPTIN, OPT-IN

Solo STOPALL se aplica a todo el canal; las demás palabras de baja anteriores se aplican al grupo por número o WABA. Lo mismo ocurre con las palabras personalizadas de baja para todo el canal que configures para SMS: se comportan como STOPALL, mientras que las palabras de baja normales se limitan al número donde llegó el mensaje.

Cómo se toleran las variaciones menores en las coincidencias

La coincidencia de mensajes entrantes no distingue mayúsculas y tolera la puntuación y los espacios, conforme a las directrices de la CTIA de respetar las variaciones menores. Antes de buscar coincidencias, se normalizan tanto la primera palabra como el cuerpo completo del mensaje: se eliminan la puntuación y los espacios de los extremos, y las letras ASCII se pasan a mayúsculas. Por tanto, Stop, STOP!, " stop " y STOP. cuentan como STOP.

La normalización es segura para caracteres no ASCII: el hebreo, el árabe y otros sistemas de escritura no tienen mayúsculas ni minúsculas, así que se conservan sin cambios (הסר sigue siendo הסר); solo se recortan la puntuación y los espacios externos.

Volver a suscribirse tras una baja

La forma de volver depende de cómo se dio de baja el contacto:

  • Envió STOP (u otra palabra de baja) → debe enviar START. Cuando un contacto se da de baja respondiendo por SMS, el operador (por ejemplo, Twilio) coloca un bloqueo a nivel de operador en ese número. No hay ninguna API para eliminarlo: la única forma de volver es que el contacto envíe START (u otra palabra de alta) desde ese mismo teléfono. Joryio nunca vuelve a suscribir a la fuerza a quien envió STOP; la suscripción siempre la inicia el contacto.
  • Se dio de baja en una página alojada o centro de preferencias → puede volver a darse de alta en el sitio. Un contacto que se dio de baja mediante un enlace o centro de preferencias (no enviando un mensaje) puede suscribirse de la misma forma, por ejemplo con {{ resubscribe_url }} o reactivando SMS en el centro de preferencias.

Precedencia y mecanismo seguro

  • La baja global del canal prevalece sobre los grupos. Una baja global de canal - por ejemplo, todo el canal SMS o WhatsApp del contacto con el estado unsubscribed - es un bloqueo definitivo que prevalece sobre cualquier suscripción de grupo. Si el canal está dado de baja globalmente, ningún grupo por número puede volver a habilitar envíos a ese contacto.
  • Alternativa segura para todo el canal. Si no se puede resolver el grupo por número o WABA, el sistema adopta una alternativa segura y aplica la baja a todo el canal, en lugar de arriesgarse a seguir enviando mensajes a quien intentó darse de baja.

Seguridad de mensajes entrantes

  • Los webhooks entrantes se verifican mediante firma; los mensajes STOP o START falsificados se rechazan.
  • Los proveedores que no admiten la verificación de firma entrante se rechazan para palabras clave entrantes.
Los nombres de remitente alfanuméricos no pueden recibir STOP

Un grupo por número solo funciona con un remitente que pueda recibir respuestas. Un número de teléfono es bidireccional: los destinatarios pueden responder y STOP funciona. Un nombre de remitente alfanumérico es unidireccional: los destinatarios no pueden responder, por lo que las palabras clave STOP o de baja no funcionarán. La disponibilidad de remitentes alfanuméricos depende de la configuración de tu proveedor de SMS.

Alcance del enlace de baja en nodos de recorrido de SMS y Viber

Un paso de mensaje de SMS o Viber en un recorrido incluye un enlace de baja cuyo alcance puedes elegir en cada nodo. El remitente de «De» define la lista de suscripción, de modo que, por defecto, el enlace de baja (y un STOP entrante) elimina al contacto únicamente de la lista de ese remitente; seguirá pudiendo recibir mensajes de tus otros remitentes.

En los nodos de mensaje de SMS y Viber, el control «El enlace de baja elimina de:» ofrece:

OpciónComportamiento
Lista de este remitente (predeterminada)El enlace de baja da de baja al contacto únicamente de la lista del número o remitente que envía, igual que el alcance de un STOP enviado por mensaje.
Todos los SMS / Todo Viber (global)El enlace de baja da de baja al contacto de todo el canal, equivalente a STOPALL.

La opción predeterminada (lista del remitente) es la más precisa y predecible, y mantiene el enlace de baja coherente con el alcance de STOP entrante. Amplíala al ámbito global solo cuando un nodo represente realmente una baja de todo el canal. Refleja el alcance de List-Unsubscribe del email (global frente a list): el mismo modelo global frente a lista, aplicado por nodo a SMS y Viber.


Las bajas de email siguen a la dirección (contactos duplicados)

La misma dirección de email puede pertenecer legítimamente a más de un contacto en un espacio de trabajo; por ejemplo, si una persona se importó dos veces o se creó desde fuentes distintas. Las bajas de email y los rebotes duros se registran para la dirección de email (dentro del espacio de trabajo), no solo para el registro de contacto que recibió el mensaje. Esto coincide con el tratamiento del consentimiento de email en plataformas de engagement maduras y garantiza que se respete la baja de la persona, aunque un registro duplicado aún parezca suscrito.

Qué implica

  • La baja cubre todos los duplicados. Cuando alguien se da de baja - desde tu enlace de baja, el encabezado List-Unsubscribe de un clic, una página de preferencias o una queja de spam - se suprimen todos los contactos de ese espacio de trabajo con el mismo email, no solo el que recibió el envío.
  • Global frente a lista. Una baja global suprime la dirección en todo el canal (todas las campañas y recorridos). Una baja de lista o tema suprime la dirección únicamente para esa lista; la persona seguirá pudiendo recibir comunicaciones de otras listas.
  • Los rebotes duros también siguen a la dirección. Un rebote duro suprime la dirección en todo el espacio de trabajo, para que un contacto duplicado no siga enviando a un buzón inexistente. Una supresión de entregabilidad no se elimina con una suscripción posterior (solo un cambio real de email o una eliminación manual la restaura), lo que protege tu reputación de remitente.
  • Limitado al espacio de trabajo. La supresión se vincula al espacio de trabajo que envió a la dirección. Una baja en un espacio no silencia esa dirección en otro espacio de trabajo de la misma cuenta.
  • Excepción transaccional. Un envío configurado para llegar a todos los contactos (la preferencia transaccional/all) sigue omitiendo las bajas de consentimiento, pero nunca se envía a una dirección con rebote duro: no tiene sentido enviar a un buzón inexistente.

Tanto las campañas como los recorridos lo comprueban en el momento del envío, por lo que un contacto duplicado creado después de la baja también queda suprimido.


Encabezado List-Unsubscribe (RFC 8058)

Descripción general

RFC 8058 define una forma estándar para que los clientes de email ofrezcan una baja con un clic. Joryio añade estos encabezados automáticamente a los emails salientes.

Encabezados añadidos

List-Unsubscribe: <https://api-eu1.joryio.com/u/{token}>
List-Unsubscribe-Post: List-Unsubscribe=One-Click

Configuración

Actívalo en la configuración del espacio de trabajo:

subscriptionSettings: {
listUnsubscribe: {
enabled: true,
includeMailto: true, // Incluye un enlace mailto: (recomendado para Gmail)
scope: 'global' // 'global' o 'list'
}
}

Opciones de alcance

AlcanceComportamiento
globalLa baja con un clic elimina al usuario de todas las comunicaciones por email
listSolo da de baja de la lista específica desde la que se envió el email

Control a nivel de campaña

En una campaña de email, el paso Redactar del editor incluye un único selector de Baja que controla tanto si aparece el enlace o encabezado de baja con un clic como de qué elimina a las personas:

  • Baja global (predeterminada): se incluye el encabezado y la baja con un clic elimina al destinatario de todos los emails (una baja para todo el espacio de trabajo). Es la opción habitual para email de marketing.
  • Darse de baja de: <tema> (solo se muestra cuando el espacio de trabajo tiene listas de suscripción): se incluye el encabezado y la baja elimina al destinatario únicamente de ese tema, sin afectar a los demás. Al elegir un tema, el envío también omite a quienes ya se dieron de baja de él.
  • Sin baja: solo transaccional: elimina por completo el encabezado List-Unsubscribe de este envío (recibos, restablecimientos de contraseña, códigos de un solo uso), que están exentos de los requisitos de baja.

Segmentación frente al alcance de baja. Este control solo afecta al enlace o encabezado de baja. No filtra destinatarios. Para enviar solo a los suscriptores de un tema, añade un filtro «miembro de la lista» en el paso Audiencia. Ese filtro ya excluye a quien se dio de baja de la lista y se evalúa al enviar.

SMS y WhatsApp no tienen un encabezado RFC 8058, por lo que su paso Redactar muestra en su lugar un selector de tema de baja (global o un tema específico) con el mismo significado de alcance. No existe una opción de «sin baja» porque siempre se respeta una baja mediante STOP.

Al enviar, la selección de la campaña se combina con la política del espacio de trabajo. La estructura guardada es:

campaign.channelConfig = {
// Omite listUnsubscribe por completo para heredar la política del espacio de trabajo (= Global)
listUnsubscribe: {
enabled: false, // «Sin baja»: suprime el encabezado en esta campaña
},
};
// El alcance de tema se guarda por separado como subscriptionCategoryId de la campaña.

Cumplimiento: elige «Sin baja» solo para mensajes genuinamente transaccionales o relacionales. Los emails masivos y de marketing requieren un encabezado List-Unsubscribe (normas para remitentes masivos de Gmail y Yahoo).


Página de preferencias (diseño personalizado)

De forma predeterminada, los destinatarios que hacen clic en un enlace de baja o «gestionar preferencias» ven el centro de preferencias integrado de Joryio. Puedes sustituirlo por tu propia página de marca.

Ubicación: Configuración → Suscripciones → Página de preferencias.

La página es por espacio de trabajo y cuenta con tres modos:

  • Predeterminado integrado: el centro de preferencias estándar de Joryio (controles de canal y lista). Siempre funciona y no requiere configuración.
  • HTML personalizado: un editor HTML/Liquid completo con vista previa en vivo (renderizada con datos de ejemplo). Se explica más abajo.
  • Redirigir a URL: omite nuestra página y envía al destinatario a la tuya. En el enlace de baja, primero registramos la baja y después redirigimos a tu URL con ?email=…&status=unsubscribed; el enlace de gestionar preferencias redirige con un ?token=… que tu página puede utilizar para leer y escribir preferencias mediante la API pública. El cumplimiento se mantiene en ambos casos.

Cómo funciona

  • Creas el cuerpo de la página en HTML. Puede usar Liquid: personalización ({{ firstName }}, {{ email }}), bloques de contenido ({{ blocks.<slug> }}) y las etiquetas de URL de suscripción que aparecen abajo.
  • Coloca la etiqueta {{ preferences_form }} donde deban aparecer los controles funcionales de suscripción (interruptores de canal y lista, Guardar, Darse de baja de todo). Joryio inserta el formulario funcional en ese lugar.
  • Si omites la etiqueta, los controles se añaden automáticamente al final, de modo que el destinatario siempre puede darse de baja (el editor te avisa si falta la etiqueta).
  • La página se renderiza del lado del servidor para cada destinatario (para que las URL de baja y la personalización sean reales) y luego se sanea antes de llegar al navegador: se conservan el diseño, las imágenes, los estilos en línea y un bloque <style> con alcance; se eliminan scripts, controladores de eventos, iframes, elementos <form> y CSS que puede ejecutar scripts.

Etiquetas disponibles

EtiquetaDescripción
{{ preferences_form }}Los controles funcionales de suscripción (colócala una vez)
{{ unsubscribe_url }}Baja global
{{ preferences_url }}Enlace de vuelta a esta página
{{ resubscribe_url }}Enlace para volver a suscribirse
{{ firstName }} / {{ email }}Personalización del destinatario
{{ blocks.<slug> }}Un bloque de contenido reutilizable

Notas

  • Se acepta el marcado de página completa (<html>/<head>/<body>). Como la página es una pantalla independiente, se renderizan el contenido y los estilos de su cuerpo.
  • La misma página sirve tanto para la baja con un clic (/u/:token) como para los enlaces de gestionar preferencias (/preferences/:token).
  • La página predeterminada integrada también ofrece un «motivo de baja» opcional (registrado en el log de auditoría y el evento message.unsubscribed) y una acción de «volver a suscribirse a todo». Un {{ resubscribe_url }} (que añade ?action=resubscribe) vuelve a suscribir al abrirse.

Gestión de rebotes

Tipos de rebote

TipoDescripciónAcción
Rebote suaveFallo temporal de entrega (buzón lleno, servidor caído)Se cuenta dentro de una ventana; se convierte en rebote duro si hay demasiados
Rebote duroFallo permanente de entrega (dirección inválida, dominio inexistente)Marca el email como inválido (suprimido de los envíos)

Un rebote duro marca la dirección como inválida y deja de enviarle, pero no da de baja al contacto: nunca solicitó dejar de recibir comunicaciones, así que su consentimiento se conserva (al borrar el rebote, por ejemplo al cambiar el email, se le puede volver a enviar). La supresión sigue a la dirección de email en todos los contactos duplicados del espacio de trabajo (consulta Las bajas de email siguen a la dirección), por lo que tampoco se envía a un segundo contacto con el mismo buzón inexistente. Un rebote suave se tolera hasta softBounceMaxRetries veces dentro de softBounceRetryHours; después se trata como rebote duro.

Configuración

subscriptionSettings: {
bounceHandling: {
softBounceRetryHours: 24, // Ventana en la que se cuentan los rebotes suaves
softBounceMaxRetries: 5, // Rebotes suaves en la ventana antes de convertirlos en rebote duro
resubscribeOnEmailChange: true // Borra la marca de inválido al cambiar el email
}
}

Integración de webhooks

Joryio procesa automáticamente los webhooks de rebote de:

  • Amazon SES: notificaciones de SNS
  • SendGrid: webhooks de eventos
  • Mailgun: webhooks

Los webhooks de rebote y eventos del proveedor se configuran con tu proveedor de email durante la incorporación; después, los eventos llegan automáticamente.

Eliminación manual de rebotes

Los administradores pueden borrar el estado de rebote desde la interfaz o la API:

await subscriptionsApi.clearBounceStatus(userId, 'Email confirmed valid by user');

Variables de plantilla Liquid

Variables disponibles

Úsalas en tus plantillas de email:

VariableDescripción
{{ unsubscribe_url }}URL de baja global
{{ unsubscribe_url_list }}URL de baja específica de la lista
{{ category_unsubscribe_url }}Baja de la lista de suscripción del email (alias de la URL de lista; usa la global como alternativa)
{{ preferences_url }}URL del centro de preferencias
{{ resubscribe_url }}URL para volver a suscribirse (para campañas de reactivación)

Ejemplo de uso

<p>¿No quieres recibir estos emails?</p>
<p>
<a href="{{ unsubscribe_url_list }}">Darse de baja de esta lista</a>
o
<a href="{{ preferences_url }}">Gestionar tus preferencias</a>
</p>

Filtros de segmento

Tipos de filtro

Tres tipos de filtro para la segmentación:

Filtro de suscripción de canal

Segmenta usuarios según el estado de suscripción al canal:

{
type: 'channel_subscription',
operator: 'subscribed_to_channel', // o 'not_subscribed_to_channel'
channel: 'email',
subscriptionStatus: 'opted_in' // opcional: estado específico
}

Filtro de pertenencia a lista

Segmenta usuarios según su pertenencia a una lista:

{
type: 'list_membership',
operator: 'member_of_list', // o 'not_member_of_list'
listId: 'list-uuid',
channel: 'email' // opcional: canal específico
}

Filtro de estado de rebote

Segmenta usuarios según la validez de su email:

{
type: 'bounce_status',
operator: 'email_valid' // 'email_valid', 'email_bounced',
// 'email_hard_bounced', 'email_soft_bounced'
}

Casos de uso

  1. Enviar solo a usuarios con aceptación explícita:

    { type: 'channel_subscription', operator: 'subscribed_to_channel',
    channel: 'email', subscriptionStatus: 'opted_in' }
  2. Excluir emails con rebote:

    { type: 'bounce_status', operator: 'email_valid' }
  3. Segmentar suscriptores al newsletter:

    { type: 'list_membership', operator: 'member_of_list',
    listId: 'newsletter-list-id' }

Referencia de la API

La referencia REST completa para gestionar el consentimiento de canal, las listas de suscripción y la pertenencia a listas - con todos los endpoints, ejemplos de solicitud/respuesta y alcances necesarios - está en la página API de suscripciones.

Dos detalles útiles antes de usarla:

  • Puedes suscribir al crear el contacto. POST /users acepta una matriz opcional subscriptions que se aplica en la misma llamada; consulta la API de usuarios. Las suscripciones al crear nunca reactivan una baja existente; para volver a obtener la aceptación de un contacto dado de baja sigue siendo necesario el endpoint específico de suscripción indicado abajo.
  • El :userId de estas rutas es el id interno del contacto en Joryio (devuelto por la API de usuarios), no tu ID externo de usuario.

Una aceptación habitual: suscribir un contacto a una lista en el canal de email:

curl -X POST https://api-eu1.joryio.com/subscriptions/contacts/665f1c2ab3d4e5f6a7b8c9d0/lists/3f6c1a2e-9d4b-4f0a-8c7e-1b2d3e4f5a6b \
-H "Authorization: Bearer jry_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{ "channel": "email", "source": "api", "consentText": "Weekly newsletter signup" }'

Endpoints públicos (sin autenticación)

// Baja con un clic (RFC 8058)
POST /u/:token

// Obtener preferencias
GET /preferences/:token

// Guardar preferencias
POST /preferences/:token
Body: { channels: [...], lists: [...] }

SDK Endpoints (SDK Key Auth)

Estos endpoints los utiliza el SDK web y se autentican mediante el encabezado X-SDK-Key.

// Actualizar suscripción de canal
POST /v1/subscriptions/channel
Headers: { 'X-SDK-Key': 'jry_sdk_web_...' }
Body: {
channel: 'email' | 'sms' | 'whatsapp' | 'push' | 'viber',
status: 'optedIn' | 'subscribed' | 'unsubscribed',
userId?: string, // Úsalo si el usuario está identificado
anonymousId?: string // Úsalo si el usuario es anónimo
}

// Actualizar la pertenencia al grupo de suscripción (lista)
POST /v1/subscriptions/group
Headers: { 'X-SDK-Key': 'jry_sdk_web_...' }
Body: {
groupId: string, // ID de la lista
channel: 'email' | 'sms' | 'whatsapp' | 'push' | 'viber',
action: 'subscribe' | 'unsubscribe',
userId?: string,
anonymousId?: string
}

Configuración

Configuración del espacio de trabajo

Configura las suscripciones en Configuración > Suscripciones:

interface SubscriptionSettings {
listUnsubscribe?: {
enabled: boolean;
includeMailto: boolean;
scope: 'global' | 'list';
};
bounceHandling?: {
softBounceRetryHours: number; // Ventana para contar rebotes suaves
softBounceMaxRetries?: number; // Rebotes suaves en la ventana antes de convertirlos en duros
resubscribeOnEmailChange: boolean; // Borra la marca de inválido al cambiar el email
};
doubleOptIn?: {
defaultEnabled: boolean;
confirmationEmailTemplateId?: string;
};
bccEmail?: string; // Para archivado de cumplimiento
}

Cumplimiento

Respaldo de pie de baja

Para que ningún email de marketing se envíe sin una forma de darse de baja:

  • Bloque de pie de cumplimiento: un bloque de contenido compliance_footer (creado automáticamente con un valor predeterminado razonable la primera vez que activas el añadido automático) contiene tu dirección y los enlaces de baja/preferencias. Puedes incluirlo en cualquier lugar con {{ blocks.compliance_footer }} o editarlo en Bloques de contenido.
  • Interruptor de añadido automático: Configuración → Suscripciones → «Añadir automáticamente un pie de baja cuando falte». Al activarlo, el proceso de envío añade el pie de cumplimiento a cualquier email de marketing (uno que incluya un encabezado List-Unsubscribe) cuyo cuerpo no tenga un enlace de baja o preferencias. No bloquea el envío.
  • Advertencia en el editor: el editor de emails de campaña muestra una advertencia cuando el cuerpo no tiene un enlace de baja, para que lo detectes antes de enviar (un respaldo visible, no un bloqueo estricto). Ofrece dos accesos directos: Añadir un enlace (abre el editor para insertarlo) y activar el pie con añadido automático (abre esta página de Suscripciones). Se puede descartar durante la sesión. Cuando el añadido automático ya está activo, se muestra una nota informativa más discreta porque el pie se añadirá por ti.

Las listas de suscripción también se pueden seleccionar en nodos de recorrido de SMS, WhatsApp y Viber (no solo de email), de modo que la comprobación de envío por nodo omite los contactos que se dieron de baja de esa lista en cualquier canal.

RGPD (Europa)

  • Usa doble opt-in para usuarios de la UE.
  • Guarda el texto de consentimiento y la marca de tiempo.
  • Ofrece acceso sencillo al centro de preferencias.
  • Atiende las solicitudes de baja inmediatamente.
  • Mantén un registro de auditoría de todos los cambios de suscripción.

CAN-SPAM (Estados Unidos)

  • Incluye una dirección postal física en los emails.
  • Atiende las solicitudes de baja en un plazo de 10 días laborables (Joryio lo hace de inmediato).
  • Identifica claramente los emails comerciales.
  • No uses asuntos engañosos.

TCPA (Estados Unidos - SMS)

  • Obtén consentimiento expreso por escrito antes de enviar SMS.
  • Respeta las palabras clave STOP inmediatamente.
  • Incluye instrucciones de baja en los mensajes.
  • Joryio gestiona automáticamente STOP, STOPALL, UNSUBSCRIBE, CANCEL, END, QUIT, REVOKE y OPTOUT (la base de inglés siempre activa), además de los valores predeterminados localizados (por ejemplo, el hebreo הסר) y las palabras personalizadas que añadas.

Gestión de palabras clave de SMS y WhatsApp

Joryio procesa automáticamente las palabras clave entrantes y tolera diferencias de mayúsculas, puntuación y espacios (Stop, STOP! y " stop " cuentan igual). STOPALL es la única baja integrada para todo el canal (además de las palabras personalizadas de baja global que configures); las demás palabras de baja se aplican al grupo de suscripción por número o WABA en el que llegó el mensaje, y START vuelve a suscribir al grupo de ese mismo número o WABA. Para consultar el conjunto completo -la base en inglés (incluidos REVOKE y OPTOUT), los valores localizados y tus adiciones personalizadas- y la regla para volver a suscribirse, consulta Palabras clave arriba.

SMS

Palabra claveAcción
STOP, UNSUBSCRIBE, CANCEL, END, QUIT, REVOKE, OPTOUTDa de baja del grupo del número receptor
STOPALLDa de baja de todo el canal (todos los números)
START, YES, UNSTOP, SUBSCRIBE, OPTINVuelve a suscribir al grupo del número receptor
HELP, INFOEnvía un mensaje de ayuda
Valores predeterminados localizados (por ejemplo, הסר, PARE, ARRET) y adiciones personalizadasIgual que su categoría anterior

WhatsApp

Palabra claveAcción
STOP, UNSUBSCRIBE, CANCEL, END, QUIT, OPTOUT, OPT-OUTDa de baja del grupo del WABA receptor
STOPALLDa de baja de todo el canal (todos los WABA)
START, UNSTOP, SUBSCRIBE, YES, OPTIN, OPT-INVuelve a suscribir al grupo del WABA receptor

Los webhooks entrantes se verifican mediante firma: los STOP y START falsificados se rechazan, y los proveedores que no admiten verificación de firma entrante se rechazan para palabras clave entrantes. Si no se puede resolver el grupo por número o WABA, la baja se aplica de forma segura a todo el canal.

El webhook entrante de SMS se configura en tus números durante la incorporación; las palabras clave se procesan automáticamente.


Prácticas recomendadas

  1. Usa siempre encabezados List-Unsubscribe: mejoran la entregabilidad y Gmail muestra el botón de baja.

  2. Implementa doble opt-in para marketing: mejora la calidad de la lista y el cumplimiento.

  3. Supervisa las tasas de rebote: las tasas altas perjudican la reputación de remitente.

  4. Segmenta por estado de suscripción: envía solo a usuarios con aceptación explícita.

  5. Haz que darse de baja sea sencillo: un clic, sin iniciar sesión.

  6. Conserva registros de auditoría: son necesarios para el cumplimiento y útiles ante disputas.

  7. Prueba el centro de preferencias: asegúrate de que los usuarios puedan gestionar sus preferencias fácilmente.

  8. Usa la baja específica de lista: permite que los usuarios sigan suscritos a parte del contenido.


Solución de problemas

Problemas habituales

No se envían emails

  • Comprueba si el usuario tiene el estado unsubscribed en el canal de email.
  • Comprueba si el email está marcado como inválido por rebotes.
  • Verifica el estado de rebote en el perfil de usuario.

La baja no funciona

  • Comprueba la caducidad del token (90 días de forma predeterminada).
  • Verifica que la firma del token sea correcta.
  • Consulta el registro de auditoría para detectar errores.

No se procesa un rebote

  • Verifica que la URL del webhook esté bien configurada en el proveedor de email.
  • Comprueba la autenticación del webhook.
  • Revisa los registros de errores.

Depuración

Comprueba el estado de suscripción:

# Llamada a la API
curl -X GET "https://api-eu1.joryio.com/subscriptions/contacts/{userId}" \
-H "Authorization: Bearer {token}"

Consulta el registro de auditoría:

curl -X GET "https://api-eu1.joryio.com/subscriptions/contacts/{userId}/history" \
-H "Authorization: Bearer {token}"