Formato de plantillas
Para desarrolladores
Una plantilla del editor visual se guarda como un documento JSON: filas, columnas y módulos, cada uno con sus propiedades. Es el documento que la API de plantillas devuelve y recibe completo en config, y el que un asistente de IA lee y edita, conectado por MCP o con la plantilla copiada desde el editor.
La especificación de esta página es la misma que entrega la API, así que puede leerla aquí o pedirla desde su integración:
GET /v1/mail-templates/formatdevuelve la especificación, los tipos de campo, los valores por defecto, un documento mínimo para empezar y la lista de tipos de módulo.GET /v1/mail-templates/format/modules/{kind}devuelve el formato de un tipo de módulo, con un módulo completo de ejemplo.
Para ver un documento completo real, GET /v1/template-catalog/{category}/{name} devuelve una plantilla de la biblioteca sin copiarla.
El recorrido completo por la API está en Plantillas por API, y los ejemplos de cada tipo de módulo en Ejemplos del formato de plantillas. La especificación técnica está en español y nombra los campos, los valores y los códigos tal como los escribe y los devuelve la plataforma.
El formato del documento de plantilla
El documento JSON que guarda una plantilla del editor de correo de la plataforma y a partir del
cual se genera el HTML del correo. Junto con este texto, el formato se entrega como datos:
field_types (los campos de cada nodo y el tipo que toma cada uno) y defaults (el valor con que
empieza cada campo de una fila, una columna o un módulo nuevos).
Esta parte aplica a la API de plantillas.
La API de plantillas lee y escribe el documento completo, en config, y entrega junto con este
texto un skeleton: un documento completo mínimo para empezar.
Partir de un documento que existe
Un campo que el documento omite no es un error: el correo se genera con el valor de respaldo del motor. Ese valor no siempre coincide con el valor por defecto del editor, así que la plantilla se guarda, pero se ve peor (se pierde un margen, cambia un color). Modifique un documento que ya existe en vez de escribir uno desde cero, y envíelo de vuelta completo, como se leyó, con sus cambios.
- Lea el documento: el
configde una plantilla, una plantilla de la biblioteca o elskeleton. - Cambie lo que necesite: un campo, una fila, una columna, un módulo. Deje el resto como estaba.
- Valide el resultado antes de guardarlo.
Los campos y los valores por defecto de un tipo de módulo también se entregan por separado, con un módulo de ejemplo de ese tipo.
Estructura
{ "page": {
"title": "…", optional, the <title> of the HTML
"body": { … }, page properties: font, colors, width
"rows": [ top to bottom
{ "uuid", "type", "container", "content",
"columns": [ left to right
{ "uuid", "grid-columns", "style",
"modules": [ top to bottom
{ "uuid", "type", "descriptor": { … } } ] } ] } ] } }
Las anotaciones del bloque están en inglés: el título es opcional y es el título del HTML, el cuerpo lleva las propiedades de la página (fuente, colores, ancho), las filas y los módulos van de arriba abajo, y las columnas de izquierda a derecha.
page.body: sutypeesfidelizador-page-properties.content.style.font-familyycontent.style.colorson la fuente y el color del texto de la página,content.computedStyle.linkColorel color de sus enlaces ymessageWidthel ancho de cada fila (lo normal es600px).container.style.background-colores el color alrededor del mensaje.- Una fila es una franja del correo.
container.stylepinta el ancho completo (un color o una imagen de fondo),content.styleel ancho del mensaje.content.computedStyle.rowColStackOnMobileapila sus columnas en un teléfono. - Una columna ocupa
grid-columnsde las 12 unidades de la fila. Las unidades de las columnas de una fila suman 12:[12],[6, 6],[4, 8],[3, 3, 3, 3]. - Un módulo es un bloque de contenido. Su
typeesfidelizador-modules-<kind>.
Ids
Cada fila, columna y módulo lleva un uuid, único en el documento. Una edición se dirige a un
nodo por él, así que dos nodos con el mismo id pueden hacer que una edición caiga en el que no
corresponde. El validador rechaza un id ausente o repetido.
- Conserve el
uuidde cada nodo que ya existe. - Un nodo nuevo recibe un id nuevo. Al copiar un nodo, dele un id nuevo a la copia y a cada nodo dentro de ella.
- Un documento de HTML crudo no tiene filas y por lo tanto no tiene ids.
Módulos
Cada módulo guarda su contenido en una clave del descriptor y su espaciado en descriptor.style
(padding-*). descriptor.computedStyle.hideContentOnMobile / hideContentOnDesktop lo ocultan
en uno de los dos lados. descriptor.mobileStyle reemplaza padding-*, font-size y
text-align en un teléfono. Los valores por defecto de cada campo están en defaults, bajo
modules.<kind>.descriptor.
| Tipo | Clave del contenido | Qué se fija |
|---|---|---|
heading | heading | text (HTML en línea), title (h1…h6), style.color, style.font-size |
paragraph | paragraph | html (<p> con formato en línea y enlaces), style.color |
list | list | html (<ul> u <ol>), tag, style.color |
image | image | src, alt, href (enlace opcional), width |
button | button | label, href, style.background-color, style.color |
divider | divider | style.border-top (1px solid #EDEDED) |
spacer | spacer | style.height |
menu | menuItemsList | items[]: text y link.href |
social | iconsList | icons[]: name e image.{src, href, alt} |
icons | iconsList | icons[]: image, alt, href, text |
video | video | src (la página del video), thumbSrc (su imagen) |
html | html | html: marcado que pasa tal cual |
table | table | content.headers[].cells[].html, content.rows[].cells[].html |
countdown | countdown | until (instante ISO 8601), labels, colores |
text es un alias heredado de paragraph y empty no muestra nada; ninguno de los dos se ofrece
para contenido nuevo. Los arreglos (items, icons, cells) son valores, no se combinan: fijar
uno reemplaza el arreglo completo del valor por defecto.
Tipos de campo
field_types enumera los campos de cada nodo y el tipo que toma cada uno. Un campo que no está ahí
es desconocido: el motor nunca lo lee. Los tipos que conviene conocer antes de escribir un valor:
| Tipo | Acepta | Ejemplo |
|---|---|---|
| objeto / arreglo | exactamente eso; un texto donde va un objeto es un error, nunca un atajo | "descriptor": {"paragraph": {…}}, no "descriptor": "Hola" |
color | hexadecimal (#RGB, #RRGGBB, con alfa), rgb()/rgba(), hsl(), transparent, un nombre de color CSS | #1F2933 |
length | un número con su unidad (px, %, em, rem, pt), 0, auto; hasta cuatro para padding y border-radius. line-height puede ir sin unidad | 10px, 0 8px, 150% |
size | un número, o un número de píxeles o un porcentaje como texto | 600, "600px" |
url | https://, http://, mailto:, tel:, un enlace especial fide:, una ruta relativa a la raíz | https://example.com/oferta |
image | https:// o una ruta relativa a la raíz de la galería | /mail-templates/logo.png |
html | HTML, nunca Markdown: <strong>, <a href>, <br> | <p>Hola <strong>Ana</strong></p> |
date | ISO 8601 | 2030-12-31T23:59:00-03:00 |
grid-columns | un número entero de 1 a 12 | 6 |
Los números son finitos y tienen como máximo 15 dígitos. Un valor CSS es solo el valor: sin ;,
sin una segunda declaración y sin comillas alrededor.
Reglas de calidad
El validador informa estas reglas. Cada hallazgo nombra el puntero JSON del valor y cómo arreglarlo, y su nivel viene en inglés:
- error — el motor fallaría, el correo llevaría algo inseguro o la estructura es ambigua. El documento se rechaza.
- warning — el correo se genera, pero algún destinatario lo recibe roto.
- suggestion — probablemente un error de tipeo que el motor ignora sin avisar.
| Nivel | Código | Regla |
|---|---|---|
| error | invalid_document, invalid_structure | La forma descrita arriba: page, rows, columns y modules están donde corresponde |
| error | invalid_type | Un campo tiene el tipo que declara field_types: un objeto, un arreglo, un texto, un número o un booleano |
| error | invalid_number | Un número es finito y lo bastante corto para que el motor lo lea |
| error | missing_id, duplicate_id | Cada fila, columna y módulo tiene su propio uuid |
| error | empty_row | Una fila tiene al menos una columna |
| error | invalid_grid | Cada columna tiene grid-columns, un número entero de 1 a 12 |
| error | unknown_module_type | Cada módulo es de un tipo que el motor sabe mostrar |
| error | unsafe_link | Ningún enlace ni imagen ejecuta código: javascript:, vbscript: o un enlace data: |
| error | unsafe_html | Sin <script>, <iframe>, <object>, <embed>, <form>, <meta>, <base>, <style>, <link>, manejadores de eventos (onclick=) ni scripts en un estilo en línea |
| error | unbalanced_html | Cada <td> que el HTML abre, lo cierra |
| error | unsafe_css_value | Un valor CSS no lleva ", ;, <, >, {, } ni barra invertida: un valor por clave |
| error | unrenderable | El motor muestra el documento. Se informa cuando falla y ninguna regla de arriba explica por qué |
| warning | invalid_color, invalid_length | Un color o una longitud es uno que un cliente de correo entiende |
| warning | invalid_link, placeholder_link | Un enlace es una URL completa: no www.…, no un # de relleno, no un href vacío |
| warning | invalid_image_src | Una imagen es una URL https:// o una ruta de la galería; http:, data: y las rutas relativas no se muestran |
| warning | invalid_enum | Un campo con un conjunto cerrado de valores toma uno de ellos (title es h1…h6, en minúsculas) |
| warning | empty_content | Un bloque tiene su contenido: un botón su texto, un texto sus palabras, un menú sus ítems |
| warning | malformed_html, markdown_in_html | Las etiquetas se cierran en el orden en que se abren, y el texto es HTML y no Markdown |
| warning | grid_not_twelve | Los grid-columns de una fila suman 12 |
| warning | invalid_date, countdown_expired | Una cuenta atrás cuenta hasta un momento válido en el futuro |
| warning | unusual_width | messageWidth está entre 320 y 900 píxeles |
| warning | empty_document | La página tiene filas |
| warning | image_without_src, image_without_alt | Una imagen muestra algo, y dice qué muestra |
| warning | button_without_link | Un botón enlaza a alguna parte |
| warning | text_without_color | Un texto declara su propio color. El HTML declara esquemas de color claro y oscuro, así que un texto sin color se pinta claro en modo oscuro y puede desaparecer |
| warning | click_here_without_link | Un texto que pide un clic lleva el enlace |
| warning | unresolved_special_link, missing_special_link | Los enlaces especiales obligatorios son fide:unsubscribe, fide:complaint y fide:browser-view, y un correo lleva los tres, normalmente en el pie |
| warning | gmail_clip | El HTML se queda por debajo de 102 400 bytes, donde Gmail recorta el mensaje y oculta el resto |
| suggestion | unknown_key | Un campo que ningún lector conoce, en cualquier nivel, propiedades CSS incluidas. El motor lo ignora sin avisar; suele ser un error de tipeo |
| suggestion | short_type | Un documento completo escribe el tipo del módulo con su prefijo fidelizador-modules- |
Las imágenes vienen de la galería de la plataforma o de la biblioteca de la cuenta, por ruta
relativa a la raíz (/mail-templates/…) o por la URL completa que trae un documento guardado. Los
enlaces especiales van en href.
La vista esencial
Esta parte aplica al conector MCP y a la plantilla que el editor copia para un chat de IA. La API de plantillas no acepta la vista esencial.
El conector MCP trabaja sobre una forma más corta del mismo documento: su vista esencial. Tiene la misma forma, solo con lo que difiere de los valores por defecto, así que la mayoría de sus bytes dicen algo.
- La plataforma convierte una vista de vuelta en el documento completo: repone cada valor por
defecto y le da un
uuida cada fila, columna y módulo que no lo tenga, así que un nodo que se agrega a una vista no necesita id. Si la vista viene de un documento sin cambios, el documento que resulta genera el mismo HTML byte a byte. - En una vista, el
typede un módulo puede ser el<kind>solo, sin el prefijofidelizador-modules-. - En una vista,
nullen un campo que tiene valor por defecto significa "ausente": el documento guardado no lo traía, y el motor aplica su propio valor de respaldo. No toque esosnullsalvo que quiera fijar el campo. - Los ejemplos del formato son vistas esenciales: un esqueleto y uno por tipo de módulo.
Documento mínimo para empezar (skeleton)
{
"page": {
"title": "Esqueleto",
"body": {
"type": "fidelizador-page-properties",
"container": {
"style": {
"background-color": "#F4F5F7"
}
},
"content": {
"style": {
"font-family": "Arial, 'Helvetica Neue', Helvetica, sans-serif",
"color": "#1F2933"
},
"computedStyle": {
"linkColor": "#0C6CC7",
"messageBackgroundColor": "#FFFFFF",
"messageWidth": "600px"
}
}
},
"rows": [
{
"container": {
"style": {
"background-color": "transparent",
"background-image": "none",
"background-position": "top left",
"background-repeat": "no-repeat"
}
},
"content": {
"computedStyle": {
"hideContentOnDesktop": false,
"hideContentOnMobile": false,
"rowColStackOnMobile": true,
"rowReverseColStackOnMobile": false,
"verticalAlign": "top"
},
"style": {
"background-color": "transparent",
"background-image": "none",
"background-position": "top left",
"background-repeat": "no-repeat",
"color": "#000000",
"width": "600px"
}
},
"empty": false,
"locked": false,
"synced": false,
"uuid": "d8821e6a-d48b-5af9-8dea-6ae8d5d55b68",
"type": "one-column-empty",
"columns": [
{
"style": {
"background-color": "transparent",
"border-top": "0px solid transparent",
"border-right": "0px solid transparent",
"border-bottom": "0px solid transparent",
"border-left": "0px solid transparent",
"padding-top": "5px",
"padding-right": "0px",
"padding-bottom": "5px",
"padding-left": "0px"
},
"uuid": "d198ba03-ec45-5365-9ee1-08330e1ea761",
"grid-columns": 12,
"modules": [
{
"uuid": "ae5863fd-e860-51a7-983d-9e048f600188",
"type": "fidelizador-modules-heading",
"descriptor": {
"heading": {
"title": "h1",
"style": {
"color": "#1F2933",
"font-family": "inherit",
"font-size": "24px",
"font-weight": "700",
"line-height": "120%",
"letter-spacing": "0px",
"text-align": "left",
"direction": "",
"link-color": "#1C93CA"
},
"text": "Título del correo"
},
"style": {
"padding-top": "10px",
"padding-right": "10px",
"padding-bottom": "10px",
"padding-left": "10px"
},
"computedStyle": {
"hideContentOnMobile": false,
"hideContentOnDesktop": false
}
}
},
{
"uuid": "79fd3fb4-c967-5b85-8d93-70a7c78b15b2",
"type": "fidelizador-modules-paragraph",
"descriptor": {
"paragraph": {
"style": {
"color": "#1F2933",
"font-family": "inherit",
"font-size": "16px",
"font-weight": "400",
"line-height": "150%",
"letter-spacing": "0px",
"text-align": "left",
"direction": ""
},
"computedStyle": {
"linkColor": "#1C93CA"
},
"html": "<p>Un párrafo que presenta el contenido.</p>"
},
"style": {
"padding-top": "10px",
"padding-right": "10px",
"padding-bottom": "10px",
"padding-left": "10px"
},
"computedStyle": {
"hideContentOnMobile": false,
"hideContentOnDesktop": false
}
}
},
{
"uuid": "dc0af782-89e0-5501-9e7f-544d968a6e88",
"type": "fidelizador-modules-button",
"descriptor": {
"button": {
"href": "https://example.com",
"target": "_blank",
"style": {
"background-color": "#0c6cc7",
"border-radius": "5px",
"color": "#ffffff",
"font-family": "inherit",
"font-size": "16px",
"font-weight": "700",
"line-height": "120%",
"letter-spacing": "0px",
"width": "100%",
"padding-top": "13px",
"padding-right": "30px",
"padding-bottom": "13px",
"padding-left": "30px",
"border-top": "0px solid #000000",
"border-right": "0px solid #000000",
"border-bottom": "0px solid #000000",
"border-left": "0px solid #000000"
},
"label": "Ver más"
},
"style": {
"text-align": "center",
"padding-top": "10px",
"padding-right": "10px",
"padding-bottom": "10px",
"padding-left": "10px"
},
"computedStyle": {
"width": 120,
"height": 42,
"hideContentOnMobile": false,
"hideContentOnDesktop": false
}
}
}
]
}
]
},
{
"container": {
"style": {
"background-color": "transparent",
"background-image": "none",
"background-position": "top left",
"background-repeat": "no-repeat"
}
},
"content": {
"computedStyle": {
"hideContentOnDesktop": false,
"hideContentOnMobile": false,
"rowColStackOnMobile": true,
"rowReverseColStackOnMobile": false,
"verticalAlign": "top"
},
"style": {
"background-color": "transparent",
"background-image": "none",
"background-position": "top left",
"background-repeat": "no-repeat",
"color": "#000000",
"width": "600px"
}
},
"empty": false,
"locked": false,
"synced": false,
"uuid": "d509b948-a515-51b6-b77c-bb062b6fa63e",
"type": "one-column-empty",
"columns": [
{
"style": {
"background-color": "transparent",
"border-top": "0px solid transparent",
"border-right": "0px solid transparent",
"border-bottom": "0px solid transparent",
"border-left": "0px solid transparent",
"padding-top": "5px",
"padding-right": "0px",
"padding-bottom": "5px",
"padding-left": "0px"
},
"uuid": "82963943-8e72-5123-b5bb-1b90cfb095bb",
"grid-columns": 12,
"modules": [
{
"uuid": "9c4e0d79-0a35-56ee-8783-b39ce54d9a76",
"type": "fidelizador-modules-paragraph",
"descriptor": {
"paragraph": {
"style": {
"color": "#6B7280",
"font-family": "inherit",
"font-size": "12px",
"font-weight": "400",
"line-height": "150%",
"letter-spacing": "0px",
"text-align": "center",
"direction": ""
},
"computedStyle": {
"linkColor": "#1C93CA"
},
"html": "<p>Recibes este correo porque te suscribiste. <a href=\"fide:browser-view\">Ver en el navegador</a> · <a href=\"fide:unsubscribe\">Darte de baja</a> · <a href=\"fide:complaint\">Reportar abuso</a></p>"
},
"style": {
"padding-top": "10px",
"padding-right": "10px",
"padding-bottom": "10px",
"padding-left": "10px"
},
"computedStyle": {
"hideContentOnMobile": false,
"hideContentOnDesktop": false
}
}
}
]
}
]
}
]
}
}
Cómo trabajar con un chat de IA
Hay dos formas de que un asistente de IA edite una plantilla:
Con el conector MCP
El asistente lee la plantilla y este formato, la edita y la valida por su cuenta. Ver .
Copiando y pegando
En el editor de plantillas, Copiar para LLMs abre una ventana de dos pasos. El primero copia el pedido del usuario, una indicación para el asistente, la especificación completa del formato en el idioma del panel y la plantilla en vista esencial, con los cambios sin guardar. El segundo recibe el JSON que devolvió el asistente: el editor lo valida antes de cargarlo, y si encuentra errores los muestra y ofrece copiarlos para el asistente. El paso a paso está en .
Cargar una plantilla pegada no la guarda. Revise el resultado en el editor y guarde cuando quiera; un deshacer vuelve a la plantilla anterior.