Referencia de Liquid
Joryio personaliza mensajes con Liquid: escribes marcadores como {{ user.firstName }} y filtros como {{ price | currency }}, que se resuelven por destinatario al enviar. Esta página es la referencia completa de todos los espacios de nombres de variables y filtros personalizados de Joryio, con ejemplos.
¿Es tu primera vez con Liquid en Joryio? Empieza con Plantillas Liquid para conocer lo básico y vuelve aquí cuando necesites más detalle.
Todos los filtros de abajo están registrados en un único motor Liquid compartido que usa cada ruta de renderizado. Por eso, la misma plantilla se renderiza de forma idéntica en email, SMS, WhatsApp, Push, in-app y webhook, tanto en campañas y recorridos como en las vistas previas. Lo que ves en la vista previa es lo que envía cada canal.
Conviene conocer dos comportamientos tolerantes predeterminados:
- Una variable sin valor se renderiza como texto vacío, nunca como error.
- Un filtro desconocido devuelve el valor sin modificarlo en vez de fallar.
Variables
Los espacios de nombres disponibles, de un vistazo. Sigue los enlaces para ver todos los detalles de cada uno.
| Espacio de nombres | Contenido | Detalles |
|---|---|---|
user.firstName, user.lastName, user.email, user.phone, user.id, user.externalId, user.whatsappName | Atributos predeterminados del contacto | Variables de mensaje |
user.custom.<attribute> | Cualquier atributo personalizado del contacto - por ejemplo, user.custom.plan | Variables de mensaje |
trigger.properties.<field> | Cómo entró al recorrido: el evento de entrada, fijo durante toda la ejecución | Variables de mensaje |
event.properties.<field>, event.name | Lo último que hizo: el evento más reciente que hizo avanzar el recorrido | Variables de mensaje |
reply.text, reply.type, reply.profile.name | Alias intuitivos para la última respuesta entrante de WhatsApp o SMS del contacto | Variables de mensaje |
blocks.<slug> | Un bloque de contenido reutilizable, renderizado en línea | Bloques de contenido |
unsubscribe_url, preferences_url, resubscribe_url | Enlaces de suscripción por destinatario - email, SMS y sesión de WhatsApp | Plantillas Liquid |
cart.items, cart.value, cart.subtotal, cart.currency, cart.checkoutUrl | El carrito de la tienda sincronizado del contacto - artículos con name, quantity, price, total, imageUrl, url, sku value es el total que envió la tienda (puede incluir el envío); subtotal es el subtotal de los artículos, antes de envío e impuestos. | Productos |
last_order.items, last_order.total, last_order.currency, last_order.placedAt, last_order.orderUrl | Su pedido más reciente que no fue cancelado ni reembolsado - los mismos campos de artículo que el carrito | Productos |
top_products | Los productos que más piden - hasta 5 con name, sku, imageUrl, url, timesOrdered, lastOrderedAt, el más frecuente primero | Productos |
Filtros products y entity | Registros de catálogo de productos o entidad personalizada de una selección guardada | Fuentes de catálogo, abajo |
trigger.*, event.* y reply.* solo se resuelven dentro de un recorrido; el resto funciona en todas partes.
cart, last_order y top_products están vacíos para un contacto sin pedidos en la tienda - envuélvelos en {% if %}.
Filtros
Encadena filtros con | y pasa argumentos después de :. Por ejemplo:
{{ user.firstName | capitalize | default: "there" }}
{{ order.total | currency: "EUR" }}
Texto
| Filtro | Función | Ejemplo → Resultado |
|---|---|---|
capitalize | Convierte en mayúscula la primera letra y en minúscula el resto | {{ "mAYA" | capitalize }} → Maya |
uppercase | Convierte toda la cadena en mayúsculas | {{ "sale" | uppercase }} → SALE |
lowercase | Convierte toda la cadena en minúsculas | {{ "SALE" | lowercase }} → sale |
truncate | Corta una cadena a una longitud - 50 de forma predeterminada - y añade un sufijo - ... por defecto. El sufijo se añade después del corte, además de la longitud | {{ "The quick brown fox jumps" | truncate: 9 }} → The quick... |
strip_html | Elimina etiquetas HTML | {{ "<b>Sale</b> today" | strip_html }} → Sale today |
url_encode | Codifica una cadena para URL - para crear enlaces | {{ "red shoes" | url_encode }} → red%20shoes |
pluralize | Dado un número, devuelve la palabra singular o plural. El plural es singular + s de forma predeterminada; pasa un tercer argumento para plurales irregulares | 3 {{ 3 | pluralize: "item" }} → 3 items |
default | Valor de reserva cuando el valor es null, no está definido o es una cadena vacía | {{ user.firstName | default: "there" }} → there - cuando está vacío |
landing_page_url | Un enlace a una página de destino publicada por su slug. Lleva el token del destinatario, así que la página sabe quién llegó; un argumento opcional añade una cadena de consulta. El enlace se construye siempre; un slug no publicado (o inexistente) responde 404 en la redirección. Vacío solo cuando el slug está mal formado o el renderizado no tiene espacio de trabajo | {{ "spring-sale" | landing_page_url: "utm_campaign=spring" }} → https://api-eu1.joryio.com/api/p/go/<workspace>/spring-sale?jp=…&utm_campaign=spring |
Números y moneda
| Filtro | Función | Ejemplo → Resultado |
|---|---|---|
currency | Formatea un número como importe. El código de moneda es USD de forma predeterminada; pasa cualquier código ISO. Usa formato en-US - símbolo primero y miles con coma | {{ 1249.5 | currency }} → $1,249.50 · {{ order.total | currency: "EUR" }} → €49.90 |
Fechas
| Filtro | Función | Ejemplo → Resultado |
|---|---|---|
date_format | Formatea una fecha. Estilos: short - predeterminado, long y full. Un estilo desconocido usa short. Los nombres de meses y días están en inglés - en-US. Formatea en la zona horaria del espacio de trabajo, igual en la vista previa que en el envío; un valor solo de fecha, como un cumpleaños (1990-05-01), nunca se desplaza. También acepta un patrón strftime ("%d %b %H:%M"), y un segundo argumento opcional indica otra zona IANA - una zona desconocida vuelve a la zona del espacio de trabajo | {{ order.createdAt | date_format }} → Jul 12, 2026 · {{ order.createdAt | date_format: "long" }} → July 12, 2026 · "full" → Sunday, July 12, 2026 · {{ order.createdAt | date_format: "%d %b %H:%M", "America/New_York" }} → 12 Jul 05:30 |
add_days | Añade N días a una fecha - un número negativo los resta. Devuelve una marca de tiempo ISO; encadena date_format para hacerla legible | {{ order.createdAt | add_days: 7 | date_format }} → Jul 19, 2026 |
time_ago | Tiempo relativo fácil de entender - granularidad de año, mes, semana, día, hora, minuto y segundo | {{ user.custom.lastOrderAt | time_ago }} → 3 days ago |
Las fechas y horas del contenido de los mensajes se muestran en la zona horaria del espacio de trabajo, la misma que usa el resto de la aplicación. Se aplica a date_format y al filtro estándar date ({{ order.createdAt | date: "%H:%M" }}); pasa una zona como último argumento para usar otra.
Matrices
| Filtro | Función | Ejemplo → Resultado |
|---|---|---|
join | Une los elementos de una matriz en una cadena. El separador predeterminado es , | {{ names | join }} → Ana, Ben, Gal · {{ names | join: " / " }} → Ana / Ben / Gal |
map | Extrae una propiedad de cada elemento y devuelve una matriz nueva | {{ items | map: "name" | join }} → Mug, Tee |
first | El primer elemento | {{ items | first }} |
last | El último elemento | {{ items | last }} |
size | Longitud de una matriz o cadena - 0 para cualquier otro valor | {{ cart.items | size }} → 3 |
count | Número de elementos de una matriz - 0 si no es una matriz | {{ cart.items | count }} → 3 |
Agregados y filtrado
Estos funcionan con matrices de registros - artículos del carrito, pedidos o selecciones de catálogo. Cada argumento field admite rutas de puntos en objetos anidados - por ejemplo, "price.amount".
| Filtro | Función | Ejemplo → Resultado |
|---|---|---|
sum | Suma un campo numérico de toda la matriz - los valores ausentes cuentan como 0 | {{ orders | sum: "totalAmount" }} → 540 |
avg | Calcula la media de un campo numérico - 0 para una matriz vacía | {{ orders | avg: "totalAmount" }} → 180 |
max | Valor numérico mayor de un campo - ignora valores no numéricos; 0 si no hay ninguno | {{ products | max: "price" }} → 129.9 |
min | Valor numérico menor de un campo | {{ products | min: "price" }} → 19.9 |
where | Filtra la matriz. Tres formas: where: "featured" conserva elementos verdaderos; where: "category", "shoes" conserva elementos iguales; where: "price", "gt", 100 compara con un operador: eq, neq, gt, gte, lt, lte, contains, in. También funcionan las formas simbólicas ==, !=, >, >=, <, <= | {{ items | where: "category", "shoes" | count }} → 2 |
sort | Ordena por un campo, en orden asc - predeterminado - o desc. Sin campo, ordena los valores mismos | {{ products | sort: "price", "desc" | first }} → el producto más caro |
Ejemplo desarrollado: los tres pedidos más recientes del destinatario:
{% assign recent = orders | sort: 'createdAt', 'desc' %}
{% for order in recent limit: 3 %}
- {{ order.createdAt | date_format }}: {{ order.totalAmount | currency }}
{% endfor %}
Gasto total: {{ orders | sum: 'totalAmount' | currency }}
Fuentes de catálogo
Dos filtros incorporan colecciones de registros a un mensaje, cada uno respaldado por una
selección guardada del espacio de trabajo. Ambos devuelven una matriz: úsalos en una
etiqueta assign y después recórrela.
products: catálogo de productos de la tienda
Extrae del catálogo de productos sincronizado - Shopify / WooCommerce / Magento - mediante una selección de productos. Hay un único catálogo, por lo que el argumento es el nombre de la selección:
{% assign products = 'featured' | products %}
{% for item in products %}
- {{ item.name }}: {{ item.price | currency }}
{% endfor %}
entity: fuente de entidad personalizada
Extrae registros de una entidad personalizada. El primer argumento es el nombre de entidad y el segundo - opcional - es el nombre de selección:
{% assign episodes = 'tv_series' | entity: 'latest' %}
{% for item in episodes %}
- {{ item.name }}
{% endfor %}
Puedes pasar variables a una selección de entidad parametrizada; por ejemplo, proporcionarle el evento de entrada del recorrido:
{% assign items = 'products' | entity: 'product_by_id', trigger %}
Notas sobre su comportamiento:
- Las selecciones que son iguales para todos los destinatarios - sin filtros de atributos de usuario ni contexto - se almacenan en caché - unos 5 minutos en la vista previa del editor, alrededor de un minuto en un envío -; así, los envíos masivos no vuelven a consultar por destinatario. Las selecciones de entidad personalizadas se evalúan de nuevo para cada destinatario.
- Si no se encuentra la selección de entidad o producto - o falla la consulta, el filtro devuelve una matriz vacía. El bucle
forsimplemente no renderiza nada; nunca genera un error. - Los tres filtros (
products,entityy el aliascatalog) se ejecutan en todas las rutas de renderizado con los mismos nombres: la vista previa del editor, los envíos de campañas, los envíos de recorridos y todos los workers de canal. Lo que muestra la vista previa es lo que recibe el destinatario. - Las opciones Catálogo de productos y Entidad personalizada del selector de personalización crean el fragmento
assignpor ti. Consulta Variables de mensaje.
catalogEl antiguo filtro catalog es un alias en desuso de entity y sigue funcionando, por lo que las plantillas existentes no se rompen. En adelante, usa entity para entidades personalizadas y products para el catálogo de la tienda.
Recomendaciones
recommendations.<slot> es una lista de productos recomendados para el destinatario, a partir de un modelo de recomendación. El slot es el nombre del modelo como clave (For you pasa a ser for_you); recommendations.default es el modelo predeterminado del espacio de trabajo. Cada artículo tiene name, price (con formato), compareAtPrice, discountPercent, url, image, description e id. Funciona en todos los canales - email (campañas y recorridos), SMS, WhatsApp, Viber, push, in-app y landing pages - y también en las vistas previas del editor y los envíos de prueba:
{% for item in recommendations.for_you limit: 3 %}
{{ item.name }} - {{ item.price }} {{ item.url }}
{% endfor %}
Consulta Slots de Liquid para el nombre de los slots y los campos de cada artículo, y la lista de bloqueo para los productos que nunca se recomiendan.
En qué se diferencia Joryio de Liquid estándar
Algunos filtros se comportan intencionalmente de forma distinta a sus equivalentes de Shopify/LiquidJS:
capitalizetambién convierte en minúsculas el resto de la cadena:"mAYA"pasa a serMaya, noMAYA.defaulttrata una cadena vacía como ausente, por lo que{{ user.firstName | default: "there" }}usa el valor de reserva incluso si el atributo existe, pero está vacío.truncateañade el sufijo después de la longitud de corte; Liquid estándar cuenta los puntos suspensivos dentro de la longitud.whereysortson superconjuntos de los integrados: aceptan las formas de llamada estándar y añaden operadores de comparación, dirección de ordenación y campos de ruta de puntos.
Liquid estándar también funciona
Todo lo anterior se añade a LiquidJS estándar. Las etiquetas integradas - {% if %} / {% elsif %} / {% else %}, {% for %}, {% assign %}, {% case %} y otras - y los filtros integrados - upcase, downcase, date, replace, split, plus, times y muchos más - funcionan en cualquier plantilla de Joryio. Cuando un filtro de Joryio comparte nombre con uno integrado - capitalize, default, where, sort, join, map, first, last, size - se ejecuta la versión de Joryio descrita en esta página. Consulta la lista completa de filtros integrados en liquidjs.com/filters/overview.html.
Contenido relacionado
- Plantillas Liquid: introducción y selector Personalizar.
- Variables de mensaje:
trigger.*,event.*yreply.*en recorridos. - Bloques de contenido: fragmentos reutilizables mediante
blocks.slug. - Recomendaciones de productos: modelos, tipos y los slots
recommendations.*.