Plantillas de email
Una plantilla es un asunto y un cuerpo de email que guardas una vez y envías muchas veces. Escribes las partes que cambian como marcadores {{ variable }}, publicas la plantilla y luego la envías por slug en lugar de pegar el mismo HTML en cada llamada API. Una plantilla pertenece a tu espacio de trabajo.
Crea y gestiona plantillas en Email > Templates, a través de /v1/email/templates, con la bird CLI, o a través del servidor MCP. Los métodos tipados están disponibles en los SDKs de TypeScript, Python, PHP y Go bajo email.templates. Los esquemas completos de solicitud y respuesta están en la referencia de API. Envía una plantilla publicada a través del endpoint de envío habitual.
Qué contiene una plantilla
Cada plantilla tiene dos nombres, y cumplen funciones distintas:
- slug es el nombre con el que envías la plantilla, por ejemplo welcome-email. Lo eliges al crear la plantilla y no se puede cambiar después. Un slug puede contener letras minúsculas, números, guiones y guiones bajos, tiene que empezar y terminar con una letra o un número, y puede tener hasta 63 caracteres. Dos prefijos están prohibidos: bird_, que está reservado para nuestras plantillas integradas, y emt_, que es el formato usado para los ID de plantilla. El dashboard llama a este campo Alias.
- name es una etiqueta de texto libre para mostrar. Por defecto toma el valor del slug, y puedes cambiarla en cualquier momento. Nada se resuelve a través del nombre, así que renombrar una plantilla para mostrarla nunca rompe un envío.
Además de esos, una plantilla tiene un ID emt_ permanente, que es fijo durante toda su vida. También tiene una category, que puede ser marketing o transactional, y un source de autoría: html, markup terminado que tú proporcionas, opcionalmente personalizado con Liquid. La categoría y el origen se fijan al crear la plantilla.
Proporcionamos un catálogo de plantillas integradas, y sus slugs empiezan todos con bird_. Una plantilla integrada no pertenece a ningún espacio de trabajo, no se puede editar y siempre se puede enviar tal cual. Copia una a tu espacio de trabajo para hacerla tuya, y se convierte en una plantilla normal que puedes editar. La copia llega como un borrador no publicado que hereda la categoría, el origen y la configuración de idioma del original, así que publícala antes de enviarla.
Borradores y versiones publicadas
Cada plantilla tiene exactamente un borrador, que es la copia de trabajo que editas. También tiene cualquier cantidad de versiones publicadas, cada una numerada (1, 2, 3, y así sucesivamente) y que nunca se modifica una vez que existe. Editar cambia el borrador en el lugar. Publicar toma una instantánea del borrador actual, la convierte en la siguiente versión numerada y hace que esa versión sea la que usan los envíos. El borrador en sí sigue siendo editable, así que puedes seguir trabajando en el siguiente.
La regla que importa para el envío es esta: un envío siempre usa la versión publicada de la plantilla, y un borrador nunca se envía por sí solo. Puedes seguir editando el borrador mientras una versión estable sigue saliendo, y publicar cuando el cambio esté listo. Publicar una nueva versión cambia lo que renderizan los envíos posteriores. Un envío que ya fue aceptado no se ve afectado por una publicación que ocurra después.

Las versiones admiten dos acciones más. Descartar los cambios del borrador para restablecer el borrador a lo que esté publicado en ese momento. O revertir para que una versión publicada anterior vuelva a ser la que usan los envíos. Solo puedes revertir a una versión que fue publicada, nunca al borrador en sí. Revertir reemplaza el borrador con el contenido de esa versión, así que cualquier cambio no guardado en el borrador se pierde, y la edición posterior parte de la versión a la que revertiste. Una reversión no crea una nueva versión.
Para elegir una imagen existente, necesitas acceso de lectura a la biblioteca multimedia del espacio de trabajo. Para subir, pegar o arrastrar una imagen nueva, necesitas acceso de escritura. Si Insertar imagen está deshabilitado o no puedes buscar o subir imágenes, pide a un administrador del espacio de trabajo el permiso correspondiente para la biblioteca multimedia. El permiso para editar plantillas por sí solo no da acceso a la biblioteca multimedia.
En el panel, elige Visual > Insertar imagen para buscar en tu biblioteca multimedia o subir una imagen PNG, JPEG, GIF o WebP de hasta 5 MB. Las imágenes WebP estáticas se convierten a PNG o JPEG. Selecciona la imagen para definir su Descripción de la imagen, ancho de visualización, alineación y enlace. Marca Imagen decorativa solo si no aporta información; una imagen con enlace necesita una descripción que explique su destino. Cada idioma conserva sus propias descripciones de imágenes y su diseño.
Usa Reemplazar imagen para cambiar la imagen seleccionada sin perder su descripción, enlace, ancho ni alineación. También puedes pegar o arrastrar un archivo de imagen a la vez al editor visual. Espera a que termine la subida, o cancélala, antes de guardar o enviar una prueba. Revisa la vista previa y abre Más acciones > Correo de prueba para enviarte el contenido actual. Código sigue disponible para editar HTML.
Eliminar una imagen de la biblioteca multimedia no la elimina de los correos ya enviados. Al reemplazar una imagen se usa una URL nueva, por lo que los mensajes anteriores siguen mostrando la original.
El guardado está protegido por un número de revisión. Envía el revision que leíste por última vez para el idioma que estás guardando. Si alguien más cambió ese idioma mientras tanto, el guardado se rechaza como conflicto en lugar de sobrescribir su trabajo. Omite revision para guardar incondicionalmente. Publicar y revertir usan el revision del propio borrador de la misma forma.
Contenido en más de un idioma
Una plantilla almacena contenido en hasta 25 idiomas, cada uno con su propio asunto y cuerpo, etiquetado con un código BCP-47 como en o pt-BR. Un idioma es el predeterminado de la plantilla. Publicar una plantilla publica todos los idiomas que contiene al mismo tiempo. No puedes publicar un idioma por separado, así que todos tienen que estar terminados primero. Cada idioma necesita un asunto y un cuerpo, y el idioma predeterminado de la plantilla tiene que ser uno de los idiomas que hayas completado. Si falta algo de eso, no se publica nada, y el error te dice qué falta en cada idioma para que puedas corregirlo todo de una vez. No tienes que terminar todos los idiomas de entrada: publica los que estén listos y agrega el resto después.
Un idioma necesita un cuerpo HTML. Puedes omitir su text: al publicar se crea automáticamente una alternativa de texto plano a partir del HTML, así que obtienes ambas partes sin escribir la segunda tú mismo.
Cada idioma también puede incluir texto de vista previa, a veces llamado preheader: la línea que una bandeja de entrada muestra después del asunto en su lista de mensajes. Es opcional, de hasta 255 caracteres, y acepta los mismos marcadores {{ variable }} que el asunto. Si lo omites, la bandeja de entrada recurre a la primera línea del cuerpo, que rara vez es la línea que elegirías. La publicación rechaza texto de vista previa en un idioma cuyo cuerpo no tiene parte HTML, ya que un cliente de correo solo lee la línea de vista previa del markup HTML oculto, y rechaza {{ bird.unsubscribe_url }} dentro de él por la misma razón que el asunto no puede contenerlo: ninguno de los dos es un lugar donde pueda ir un enlace.
Dos configuraciones cubren un envío que no nombra un idioma para el que la plantilla tenga contenido, y protegen contra errores distintos:
| Configuración | Qué controla |
|---|---|
| on_missing_language | Qué sucede cuando un envío solicita un idioma que la plantilla no tiene. fallback, el valor predeterminado, sirve la coincidencia más cercana. Primero prueba una forma más amplia del mismo idioma, de modo que un pt almacenado puede servir una solicitud de pt-BR. Luego recurre al idioma predeterminado de la plantilla. fail rechaza el envío en su lugar, para contenido donde enviar el idioma equivocado es peor que no enviar nada. |
| language_source_required | Si un envío tiene que indicar un idioma. Está desactivado por defecto, así que un envío que no indica ninguno recibe el idioma predeterminado. Actívalo y ese envío se rechaza en su lugar. Un broadcast indica un idioma para toda su audiencia, así que una plantilla con este ajuste activado necesita que ese idioma se elija antes de que el broadcast pueda enviarse. |
Puedes configurar ambas de forma independiente. Por sí solo, fail solo aplica cuando un envío nombra un idioma que no tenemos, así que un envío que no nombre ninguno sigue pasando. Activa ambas configuraciones juntas cuando quieras que cada envío nombre un idioma a propósito.
Personalización con variables
Escribe marcadores {{ variable }} en el asunto, el texto de vista previa y el cuerpo. Los detectamos automáticamente, combinados en todos los idiomas, así que nunca tienes que declararlos por separado. El prefijo del marcador distingue los dos tipos. Una ruta que empieza con bird. lee de nuestros datos, ya sea un registro de contacto o el enlace de cancelación de suscripción. Todo lo demás es un parámetro al que le das un valor cuando envías.
El nombre de un parámetro es una sola palabra, como {{ animal }}. Un nombre con puntos busca una estructura que un parámetro no tiene, así que publicar uno se rechaza: escribe el valor como su propio parámetro, o lee datos de contacto con bird.contact.<attribute> en su lugar.
En un envío individual o un lote, el valor de un parámetro viene del objeto template.parameters del envío, identificado por su nombre. Un conjunto de valores cubre a todos los destinatarios de ese envío. bird es el único nombre que no puedes usar ahí: una clave template.parameters llamada bird se rechaza con un 422.
Un broadcast no tiene objeto parameters, así que su contenido solo puede usar marcadores bird.. bird.contact.<attribute> se llena a partir de las propiedades de contacto de cada destinatario, que es lo que personaliza el contenido por destinatario. Cada propiedad de contacto está disponible por su propia clave, y también los tres campos integrados: first_name, last_name y email.
Ejemplo de código
Hi {{ bird.contact.first_name }},Cada parámetro en la plantilla necesita un valor cuando envías. De lo contrario, la API devuelve un 422 que nombra el parámetro faltante. Proporciona valores para los parámetros en todos los idiomas, porque el idioma seleccionado puede depender de la configuración de respaldo. Una propiedad de contacto faltante se renderiza como un valor vacío, así que agrega un respaldo para contenido visible al cliente: {{ bird.contact.first_name | default: "there" }}.
Un broadcast es más estricto con los nombres que acepta, porque las propiedades de contacto son lo único con lo que puede llenar placeholders. Sus placeholders bird.contact.* solo pueden nombrar un campo integrado o una propiedad de contacto que el espacio de trabajo haya registrado. Cualquier otro placeholder, incluido un parámetro, es uno que el broadcast no tiene forma de llenar. El envío se rechaza, y el error nombra el placeholder.
Lo que archivar una propiedad cambia para una plantilla es solo el contenido nuevo: la propiedad desaparece del selector en el editor, y publicar una versión cuyo contenido la lea se rechaza, nombrando la propiedad. Las versiones publicadas antes del archivado no se ven afectadas.
Los placeholders usan Liquid, así que los filtros y el flujo de control funcionan junto con la sustitución simple. Un condicional {% if %} y un bucle {% for %} sobre un valor de tipo array son válidos. Un puñado de construcciones se rechazan al publicar, y el error nombra exactamente qué cambiar:
- Inclusiones parciales, usando {% include %} o {% render %}.
- Las etiquetas increment, decrement y ifchanged.
- Los filtros money, format_date, format_time, json, inspect y type.
- Comparar contra empty o blank. Usa .size == 0 en su lugar.
- Bloques anidados mucho más profundamente de lo que el marcado real de un correo necesita.
La plantilla de un broadcast no puede usar un bucle {% for %} en absoluto, porque un broadcast llena un solo valor por propiedad de contacto y no tiene nada sobre lo que iterar. Si tu contenido necesita un bucle, envíalo a través del API de mensajes.
Toda plantilla usa Liquid, incluida una que solo contiene placeholders {{ variable }}. Antes de publicar, validamos el asunto, el texto de vista previa, el HTML y el contenido en texto plano como Liquid. También añadimos el filtro escape a cada salida HTML que no termine ya con escape o escape_once, para que un valor que contenga & o < no pueda alterar el marcado circundante. La salida reservada de cancelación de suscripción se deja intacta para que el envío pueda reemplazarla. El asunto y el cuerpo en texto plano se dejan tal como están escritos. Dado que la publicación añade estos filtros, el HTML que leas de una versión publicada puede no ser idéntico byte a byte a lo que enviaste.
Coloca una URL completa directamente en un href, por ejemplo <a href="{{ sign_in_url }}">Sign in</a>. No añadas url_encode al valor entero. Codifica en porcentaje https://, /, ? y &, lo que impide que el resultado funcione como enlace absoluto. Nosotros añadimos el escape HTML preservando la estructura de la URL. Cuando un parámetro proporciona un componente de la URL, codifica ese componente explícitamente: <a href="https://example.com/search?q={{ query | url_encode }}">Search</a>.
Previsualizar antes de publicar
Renderiza una plantilla con valores de ejemplo y obtén de vuelta el asunto y los cuerpos HTML y de texto plano que un envío entregaría. La previsualización usa nuestro renderizador local de Liquid y renderiza el borrador por defecto, que es como verificas un cambio antes de que entre en producción. También puede renderizar una versión publicada. Funciona con tus propias plantillas y con las integradas, y no se envía nada.
También puedes pasarle el contenido tú mismo en lugar de que lea el borrador. Pasa un asunto y cuerpos y se renderizan, tratados exactamente como se trataría un borrador, que es lo que permite a un editor mostrar un cambio a medida que se escribe sin guardar nada primero.
La personalización se completa por ti, así que lo que recibes de vuelta se lee como texto terminado en lugar de placeholders {{ }}. Indica un contact y cada bird.contact.<attribute> se resuelve contra las propiedades propias de ese contacto, que es como verificas tu redacción contra un registro real antes de que alguien lo reciba. Los valores salen de la misma proyección que un broadcast usa para llenar sus placeholders, así que la previsualización responde con lo que un envío respondería.
Omite contact y se sustituyen valores de relleno en su lugar: Bird y Test para el nombre y el apellido, bird.test@example.com para el correo electrónico, y el respaldo registrado de cada otra propiedad. Una propiedad referenciada sin respaldo se renderiza como su clave entre corchetes, como [loyalty_tier], lo que te indica tanto que el valor es un placeholder como qué propiedad aún necesita un respaldo.
Un contacto se lee tal como está en este momento. Eso hace que la previsualización sea la herramienta correcta para revisar contenido que estás a punto de enviar, y la incorrecta para preguntar qué contenía un envío anterior. Para leer lo que un envío realmente entregó, abre ese mensaje en el registro de correo, que lo renderiza a partir de los valores que ese envío llevaba.
Añade language para renderizar un idioma específico, o omítelo para usar el idioma por defecto de la plantilla. La respuesta te indica qué idioma renderizó, lo cual importa cuando el que pediste no está disponible y el on_missing_language de la plantilla sirvió una coincidencia cercana.
Si el borrador tiene personalización que se rechazaría al publicar, la previsualización devuelve ese mismo error, así que también sirve para encontrar problemas a tiempo.
En el constructor de plantillas del dashboard, Preview with contact data en la parte inferior del panel izquierdo muestra el correo renderizado junto a lo que estés editando, tanto en el editor visual como en el de código. El selector debajo elige con los datos de quién se llenan los placeholders, y Sample data son los valores de relleno mencionados arriba.
Enviar con una plantilla
Establece el campo template del envío como un objeto que nombre la plantilla, ya sea por id (emt_...) o por slug, usando exactamente uno de los dos. Coloca los valores de sus variables en template.parameters. Añade language para elegir un idioma específico, o omítelo para enviar el idioma por defecto de la plantilla, a menos que la plantilla requiera que cada envío nombre uno. Omite subject, html y text por completo, porque la plantilla ya los proporciona.
Ejemplo de código
{
"from": "hello@yourdomain.com",
"to": ["delivered@messagebird.dev"],
"category": "transactional",
"template": {
"slug": "welcome-email",
"parameters": { "first_name": "Jane" }
}
}Un comportamiento que vale la pena planificar: la categoría de la plantilla es un valor por defecto, y el propio category del envío la sobrescribe. Omite category, y el envío hereda la categoría de la plantilla, así que una plantilla operativa se envía como transaccional sin que la repitas en cada llamada. Establece category, y tu valor gana. El resto del contrato del lado del envío está en enviar con una plantilla.
Creación fuera del dashboard
Todo el ciclo de vida está disponible fuera del dashboard. El paso de publicación se llama submit allí, y es la operación que convierte el borrador en la siguiente versión publicada:
Ejemplo de código
bird email templates create welcome-email --category marketing --source html
bird email templates versions languages set <emt_...> <emv_...> en --subject "Hi {{ bird.contact.first_name }}" --html "<p>Hello</p>"
bird email templates versions submit <emt_...> <emv_...> --validate-only # report problems, freeze nothing
bird email templates versions submit <emt_...> <emv_...> # freeze, go livecreate devuelve la plantilla junto con su draft_version_id, que todo comando de versión e idioma requiere. --validate-only ejecuta las mismas comprobaciones de completitud que un submit real, sin congelar nada, así que es la forma económica de encontrar todos los problemas en todos los idiomas en una sola pasada. Leer una plantilla de vuelta te da sus metadatos y su estado por idioma, pero no su contenido. El contenido está en los idiomas de una versión, un idioma a la vez.
Los SDKs ofrecen el mismo ciclo de vida como métodos tipados bajo email.templates, con las operaciones de versión e idioma anidadas debajo como email.templates.versions y email.templates.versions.languages. Un agente accede a las mismas operaciones a través de las herramientas email_templates_* MCP.
Próximos pasos
- Enviar correo: el payload completo del envío y cómo los envíos con plantilla encajan en él
- Categorías: elegir marketing vs transactional en cada envío
- bird email templates: gestionar plantillas desde la terminal
- Referencia de API: esquemas completos de solicitud y respuesta para las dieciocho operaciones de plantillas
- SDKs: los métodos tipados email.templates en TypeScript, Python, PHP y Go
- Servidor MCP: permitir que un agente cree y publique plantillas
- Cómo crear una plantilla de email: un video que construye una en el dashboard y luego hace que un agente construya otra
Recursos relacionados
Continúa con la documentación, guías y ejemplos sobre este tema. Los recursos están en inglés.