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
- Resumen
- Modelos de datos
- Suscripciones de canal
- Integración con SDK web
- Listas de suscripción
- Grupos de suscripción por número (SMS y WhatsApp)
- Encabezado List-Unsubscribe (RFC 8058)
- Página de preferencias (diseño personalizado)
- Manejo de rebotes
- Variables de plantilla Liquid
- Filtros de segmento
- Referencia de API
- Configuración
- 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
| Estado | Descripción |
|---|---|
optedIn | El usuario completó la confirmación de doble opt-in. |
subscribed | El usuario está suscrito (aceptación simple). |
unsubscribed | El 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:
| Estado | Descripción |
|---|---|
SubscriptionStatus.OPTED_IN | El usuario aceptó explícitamente (por ejemplo, doble opt-in confirmado). |
SubscriptionStatus.SUBSCRIBED | El usuario está suscrito pero no aceptó explícitamente. |
SubscriptionStatus.UNSUBSCRIBED | El 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/addToSubscriptionGroupno reciben ID de destino; se aplican a quien el SDK identifica actualmente (el propio ID de un visitante anónimo o el usuario enviado aidentify()). 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
anonymousIdse 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 aidentify(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 salvoSTOPALL) 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.STOPALLse aplica a todo el canal: da de baja al contacto de todos los números o WABA de ese canal.STARTvuelve a suscribir al contacto al grupo de ese mismo número o WABA.
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:
| Tipo | Palabras clave |
|---|---|
| Baja | STOP, STOPALL, UNSUBSCRIBE, CANCEL, END, QUIT, REVOKE, OPTOUT |
| Alta | START, YES, UNSTOP, SUBSCRIBE, OPTIN |
| Ayuda | HELP, INFO |
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:
| Idioma | Baja | Alta | Ayuda |
|---|---|---|---|
| Hebreo | הסר, הסרה, עצור, ביטול, הפסק | התחל, הצטרף, כן | עזרה, מידע |
| Español | PARE, BASTA, CANCELAR, ALTO | SI, ALTA | AYUDA |
| Francés | ARRET, ARRÊT, DESABONNER | OUI | AIDE |
| Alemán | STOPP, ABBESTELLEN | JA | HILFE |
| Portugués | PARAR, 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.
| Tipo | Palabras clave |
|---|---|
| Baja | STOP, STOPALL, UNSUBSCRIBE, CANCEL, END, QUIT, OPTOUT, OPT-OUT |
| Alta | START, 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
STOPoSTARTfalsificados se rechazan. - Los proveedores que no admiten la verificación de firma entrante se rechazan para palabras clave entrantes.
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ón | Comportamiento |
|---|---|
| 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
| Alcance | Comportamiento |
|---|---|
global | La baja con un clic elimina al usuario de todas las comunicaciones por email |
list | Solo 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
| Etiqueta | Descripció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
| Tipo | Descripción | Acción |
|---|---|---|
| Rebote suave | Fallo temporal de entrega (buzón lleno, servidor caído) | Se cuenta dentro de una ventana; se convierte en rebote duro si hay demasiados |
| Rebote duro | Fallo 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:
| Variable | Descripció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
-
Enviar solo a usuarios con aceptación explícita:
{ type: 'channel_subscription', operator: 'subscribed_to_channel',
channel: 'email', subscriptionStatus: 'opted_in' } -
Excluir emails con rebote:
{ type: 'bounce_status', operator: 'email_valid' } -
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 /usersacepta una matriz opcionalsubscriptionsque 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
:userIdde estas rutas es elidinterno 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 clave | Acción |
|---|---|
STOP, UNSUBSCRIBE, CANCEL, END, QUIT, REVOKE, OPTOUT | Da de baja del grupo del número receptor |
STOPALL | Da de baja de todo el canal (todos los números) |
START, YES, UNSTOP, SUBSCRIBE, OPTIN | Vuelve a suscribir al grupo del número receptor |
HELP, INFO | Envía un mensaje de ayuda |
Valores predeterminados localizados (por ejemplo, הסר, PARE, ARRET) y adiciones personalizadas | Igual que su categoría anterior |
| Palabra clave | Acción |
|---|---|
STOP, UNSUBSCRIBE, CANCEL, END, QUIT, OPTOUT, OPT-OUT | Da de baja del grupo del WABA receptor |
STOPALL | Da de baja de todo el canal (todos los WABA) |
START, UNSTOP, SUBSCRIBE, YES, OPTIN, OPT-IN | Vuelve 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
-
Usa siempre encabezados List-Unsubscribe: mejoran la entregabilidad y Gmail muestra el botón de baja.
-
Implementa doble opt-in para marketing: mejora la calidad de la lista y el cumplimiento.
-
Supervisa las tasas de rebote: las tasas altas perjudican la reputación de remitente.
-
Segmenta por estado de suscripción: envía solo a usuarios con aceptación explícita.
-
Haz que darse de baja sea sencillo: un clic, sin iniciar sesión.
-
Conserva registros de auditoría: son necesarios para el cumplimiento y útiles ante disputas.
-
Prueba el centro de preferencias: asegúrate de que los usuarios puedan gestionar sus preferencias fácilmente.
-
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
unsubscribeden 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}"