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 de API. Una plantilla pertenece a tu espacio de trabajo.
Crea y gestiona plantillas en Email > Templates, a través de /v1/email/templates, con el bird CLI, o a través del servidor de 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:
sluges el nombre con el que envías la plantilla, por ejemplowelcome-email. Lo eliges cuando creas 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, yemt_, que es el formato usado para los IDs de plantilla. El dashboard llama a este campo Alias.namees 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 la visualización nunca rompe un envío.
Junto con esos, una plantilla tiene un ID permanente emt_, 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 quedan fijos cuando creas la plantilla.
Proporcionamos un catálogo de plantillas integradas, y sus slugs siempre empiezan 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 en tu espacio de trabajo para hacerla tuya, y se convierte en una plantilla normal que puedes editar. La copia llega como un borrador sin publicar que hereda la categoría, el origen y la configuración de idioma de la 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 no se modifica una vez que existe. Editar cambia el borrador en su 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 sigue siendo editable, así que puedes continuar 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 los envíos posteriores renderizan. 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 actualmente. 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 de medios del espacio de trabajo. Para subir, pegar o soltar una imagen nueva, necesitas acceso de escritura. Si Insert image está desactivado o no puedes explorar ni subir, pide a un administrador del espacio de trabajo el permiso correspondiente de la biblioteca de medios. El permiso de edición de plantillas por sí solo no otorga acceso a la biblioteca de medios.
En el dashboard, elige Visual > Insert image para buscar en tu biblioteca de medios o subir una imagen PNG, JPEG, GIF o WebP estática dentro de los límites de subida de la biblioteca de medios. Las imágenes WebP estáticas se convierten a PNG o JPEG. Selecciona la imagen para configurar su Image description, ancho de visualización, alineación y enlace. Marca una imagen como Decorative image solo cuando no aporte información; una imagen con enlace necesita una descripción que explique su destino. Cada idioma mantiene sus propias descripciones de imagen y su propio diseño.
Usa Replace image para cambiar una imagen seleccionada conservando su descripción, enlace, ancho y alineación. También puedes pegar o soltar un archivo de imagen en el editor visual. Espera a que termine la subida, o cancélala, antes de guardar o enviar una prueba. Revisa la vista previa y luego abre More actions > Test email para enviarte el contenido actual. Code sigue disponible para editar HTML.
Eliminar una imagen de la biblioteca de medios no la elimina de los correos ya enviados. Reemplazar una imagen usa una URL nueva, así 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 en el ínterin, el guardado se rechaza como conflicto en lugar de sobrescribir su trabajo. Omite revision para guardar incondicionalmente. La publicación y la reversión usan el revision propio del borrador de la misma forma.
Contenido en más de un idioma
Una plantilla contiene 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 solo idioma, así que todos deben 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 indica 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 añade el resto después.
Un idioma necesita un cuerpo HTML. Puedes omitir su text: la publicación entonces crea una alternativa en texto plano a partir del HTML automáticamente, así que obtienes ambas partes sin escribir la segunda tú mismo.
Cada idioma también puede llevar 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 admite 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 que elegirías. La publicación rechaza el 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 marcado HTML oculto, y rechaza {{ bird.unsubscribe_url }} dentro de él por la misma razón por la que el asunto no puede llevarlo: ninguno de los dos es un lugar donde pueda ir un enlace.
Dos ajustes cubren un envío que no indica un idioma para el que la plantilla tenga contenido, y protegen contra errores diferentes:
| Ajuste | 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 incorrecto es peor que no enviar nada. |
language_source_required | Si un envío tiene que indicar un idioma o no. Está desactivado de forma predeterminada, 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 esté elegido antes de que el broadcast pueda enviarse. |
Puedes configurar estos dos ajustes de forma independiente. Por sí solo, fail solo se aplica cuando un envío indica un idioma que no tenemos, así que un envío que no indica ninguno sigue pasando. Activa ambos ajustes juntos cuando quieras que cada envío indique un idioma a propósito.
Personalización con variables
Los envíos de plantilla individuales, por lotes y programados pueden usar valores de contacto. Cada dirección To debe coincidir con un contacto cuando la plantilla lee bird.contact.*.
Escribe marcadores {{ variable }} en el asunto, el texto de vista previa y el cuerpo. Los detectamos automáticamente, combinados entre 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 intenta acceder a 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 mismo 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.
Usa bird.contact.<attribute> para leer las propiedades de contacto del destinatario, incluidos los campos integrados first_name, last_name y email. Los envíos individuales y por lotes asocian cada dirección To a un contacto del mismo espacio de trabajo. Los destinatarios Cc y Bcc reciben una copia del email personalizado de cada destinatario To; no necesitan contactos coincidentes. Un broadcast lee los contactos de su audiencia y también acepta valores del llamante en sus parámetros de plantilla.
Hi {{ bird.contact.first_name }},Proporciona los parámetros obligatorios del llamante que usa el idioma resuelto. Si falta un parámetro obligatorio, se devuelve 422. Un valor nulo en un parámetro obligatorio también devuelve 422; una cadena vacía intencionada sigue siendo válida. La lista variables de la versión abarca sus idiomas, así que un parámetro que solo usa otro idioma no necesita valor en este envío.
Al publicar, se registran los atributos de contacto que usa cada idioma. Al enviar, solo se obtienen los atributos que usan el idioma seleccionado y los campos del envío. Un envío sin referencias a contactos omite la búsqueda de contactos.
Una plantilla que lee bird.contact.* requiere un contacto coincidente en el espacio de trabajo para cada destinatario To. Si un destinatario To no tiene contacto, la solicitud devuelve 422 con E04099 antes de aceptar cualquier mensaje, también en envíos por lotes o programados. Crea o actualiza primero el contacto, o usa parámetros del llamante. Un filtro default o un condicional de Liquid no elimina este requisito. Las plantillas que usan parámetros del llamante o bird.unsubscribe_url sin atributos de contacto no necesitan un contacto coincidente.
Por ejemplo, envía Hello {{ bird.contact.first_name }} a Alice y Bob, con Charlie en Cc:
| Entrega | Saludo | To visible |
|---|---|---|
| Alice | Hello Alice | Alice |
| Charlie, en copia del email de Alice | Hello Alice | Alice |
| Bob | Hello Bob | Bob |
| Charlie, en copia del email de Bob | Hello Bob | Bob |
Charlie recibe dos copias, así que esta solicitud genera cuatro entregas. Bcc funciona igual, con la dirección Bcc oculta. Estos emails personalizados por separado se aplican cuando la plantilla usa atributos de contacto; los envíos normales mantienen su comportamiento de direccionamiento actual. La misma regla se aplica a cada mensaje del lote API.
Un contacto existente puede tener el nombre o el apellido vacío. Los nombres ausentes se muestran vacíos y tú eliges el saludo en la plantilla. Para saludar por el nombre, usa Hello {{ bird.contact.first_name | default: "there" }}!. Para el nombre completo, combina ambas partes, elimina los espacios de los extremos y elige un valor predeterminado:
{% assign full_name = bird.contact.first_name | append: " " | append: bird.contact.last_name | strip %}
Hello {{ full_name | default: "there" }}!Esto muestra Hello Jane Doe!, Hello Jane! o Hello Doe! cuando están presentes los nombres correspondientes, y Hello there! cuando faltan ambos. No necesitas nombres de sustitución para todo el espacio de trabajo.
Las propiedades de contacto que creas conservan sus valores de respaldo configurados. Un valor null almacenado cuenta como ausente; una cadena explícitamente vacía, false o cero sigue siendo un valor. Una propiedad ausente sin respaldo se muestra vacía. Las propiedades desconocidas y los valores almacenados no válidos impiden la aceptación. Si falla la consulta, puedes reintentar; no se sustituyen los valores por respaldos.
Un marcador bird.contact.* debe nombrar un atributo integrado o una propiedad personalizada registrada. Los nombres de parámetros del llamante se mantienen separados de esas rutas de la plataforma. Los envíos de plantilla individuales y por lotes, incluidos los programados, guardan los valores del destinatario en el momento de la aceptación y los reutilizan en los reintentos de entrega y en las lecturas del mensaje. Los broadcasts mantienen su propio momento de preparación. Los envíos individuales y por lotes rechazan las entradas sensibles antes de la aceptación.
Archivar una propiedad la elimina del selector de propiedades en el editor. Las nuevas versiones de plantilla que la lean no pueden publicarse, y el error nombra la propiedad. Las plantillas ya publicadas siguen enviándose. Desarchiva la propiedad para volver a usarla en nuevas versiones.
Los marcadores 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,decrementyifchanged. - Los filtros
money,format_date,format_time,json,inspectytype. - Comparar contra
emptyoblank. Usa.size == 0en su lugar. - Bloques anidados mucho más profundamente de lo que el marcado real de un correo necesita.
Los parámetros de plantilla de un broadcast aceptan valores escalares; los arrays y los objetos no se admiten. Sin parámetros del llamante, tanto {% for %} como {% tablerow %} admiten rangos literales como (1..3) y listas creadas dentro de Liquid con split. Un broadcast que usa parámetros del llamante admite {% for %} sobre una lista creada con split, pero rechaza los rangos y {% tablerow %}. En ambos casos, se rechaza una etiqueta de bucle que lea directamente un parámetro del llamante o una propiedad del contacto, incluidos los rangos cuyo extremo lea uno de esos valores.
Todas las plantillas usan Liquid, incluida una que solo contiene marcadores {{ 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 que lo rodea. La salida reservada de cancelación de suscripción se mantiene sin cambios para que el envío pueda reemplazarla. El asunto y el cuerpo en texto plano se dejan tal cual. Como la publicación añade estos filtros, el HTML que lees de una versión publicada puede no ser idéntico byte a byte a lo que enviaste.
Pon 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 de forma explícita: <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 en texto plano que un envío entregaría. La vista previa usa nuestro renderizador Liquid local y renderiza el borrador por defecto, que es como compruebas un cambio antes de que entre en vigor. También puede renderizar una versión publicada. Funciona tanto con tus propias plantillas como 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 los cuerpos, y se renderizan exactamente como se haría con un borrador, que es lo que permite a un editor mostrar un cambio mientras se escribe sin guardar nada primero.
La personalización se completa automáticamente, así que lo que recibes se lee como texto final en lugar de marcadores {{ }}. Indica un contact y cada bird.contact.<attribute> se resuelve contra las propiedades de ese contacto, que es como compruebas tu redacción contra un registro real antes de que nadie lo reciba. Los valores provienen de la misma proyección que usa un broadcast para rellenar sus marcadores, así que la vista previa responde con lo que respondería un envío.
Omite contact y se sustituyen valores de ejemplo: Bird y Test para el nombre y el apellido, bird.test@example.com para el correo electrónico, y el valor de respaldo registrado de cada otra propiedad. Una propiedad referenciada sin valor de respaldo se renderiza como su clave entre corchetes, por ejemplo [loyalty_tier], lo que te indica a la vez que el valor es un marcador y qué propiedad aún necesita un respaldo.
Un contacto se lee tal como está en este momento. Por eso la vista previa es la herramienta adecuada para comprobar contenido que estás a punto de enviar, pero no para consultar qué contenía un envío anterior. Para ver lo que un envío entregó realmente, abre ese mensaje en el registro de email, que lo renderiza a partir de los valores que llevaba ese envío.
Añade language para renderizar un idioma específico, o déjalo fuera para usar el idioma por defecto de la plantilla. La respuesta te indica qué idioma renderizó, lo que importa cuando el que solicitaste no está disponible y el on_missing_language de la plantilla sirvió una coincidencia cercana.
Si el borrador tiene personalización que sería rechazada al publicar, la vista previa devuelve ese mismo error, así que también sirve para detectar problemas con antelación.
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 que hay debajo elige con los datos de quién se rellenan los marcadores, y Sample data son los valores de ejemplo descritos arriba.
Enviar con una plantilla
Establece el campo template del envío como un objeto que nombra la plantilla, ya sea por id (emt_...) o por slug, usando exactamente uno de los dos. Pon los valores de sus variables en template.parameters. Añade language para elegir un idioma concreto, o déjalo fuera para enviar el idioma predeterminado de la plantilla, a menos que la plantilla exija que cada envío nombre uno. Deja subject, html y text fuera por completo, porque la plantilla ya los proporciona.
{
"from": "hello@yourdomain.com",
"to": ["delivered@messagebird.dev"],
"category": "transactional",
"template": {
"slug": "welcome-email",
"parameters": { "first_name": "Jane" }
}
}Un comportamiento que conviene prever: la categoría de la plantilla es un valor por defecto, y el category del propio envío la sobrescribe. Si no incluyes category, el envío hereda la categoría de la plantilla, así que una plantilla operacional se envía como transaccional sin que tengas que repetirlo en cada llamada. Si incluyes category, 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: a través de la API, los SDKs en email.templates, el bird CLI y las herramientas email_templates_* MCP. En los SDKs, las operaciones de versión e idioma están en email.templates.versions y email.templates.versions.languages. El paso de publicación se llama submit allí, y convierte el borrador en la siguiente versión publicada.
Puedes guardar las plantillas en tu propio repositorio y sincronizarlas desde un paso de build. Un borrador y una versión publicada devuelven HTML diferente, así que elige la lectura que corresponda a lo que tu herramienta necesita:
- Un borrador se devuelve exactamente como lo guardaste. El HTML que guardas en un idioma del borrador regresa byte a byte desde la lectura de ese idioma (
GET /v1/email/templates/{id}/versions/{draft_version_id}/languages/{language}) y desde la lectura de versión, incluidos el doctype, los bloques<style>, comentarios, espacios en blanco, saltos de línea y texto no ASCII. Un guardado no devuelve contenido, así que lee el idioma de vuelta cuando necesites confirmar qué se almacenó. - Una versión publicada se devuelve lista para enviar. La publicación reescribe el HTML de tres maneras: cada salida
{{ }}que no termina ya conescapeoescape_oncerecibe el filtroescape, excepto{{ bird.unsubscribe_url }}, que se deja tal cual; el texto de vista previa se inserta en un bloque oculto justo después de la etiqueta de apertura<body>, o al inicio si el HTML no la tiene; y untextvacío se rellena con una alternativa en texto plano derivada del HTML. Leer esa versión devuelve el contenido reescrito, así que difiere de tu archivo fuente.
Para exportar el código fuente que creaste, lee el borrador. Leer una plantilla o listar plantillas devuelve metadatos y el estado por idioma, pero no el HTML.
Los ejemplos siguientes usan {{ first_name }}, un parámetro del llamante que se proporciona en template.parameters, como en "parameters": {"first_name": "Jane"}. Usa {{ bird.contact.first_name }} cuando la plantilla deba leer el contacto del espacio de trabajo del destinatario. Indica los destinatarios por email tanto en los envíos individuales como en los envíos por lotes. Los estilos guardados en la plantilla publicada siguen formando parte de esa versión. No existe un espacio de nombres bird.brandkit.* en tiempo de ejecución.
Crea la plantilla con su primer idioma. La respuesta incluye el id de la plantilla y el draft_version_id, que las operaciones de versión e idioma necesitan. El ejemplo también establece preview_text, el texto de vista previa que una bandeja de entrada muestra después del asunto en su lista de mensajes. La publicación lo inserta en el HTML como un bloque oculto, que es una de las razones por las que una versión publicada devuelve HTML diferente al del borrador:
const template = await bird.email.templates.create({
slug: "welcome-email",
category: "transactional",
source: "html",
languages: {
en: {
subject: "Welcome, {{ first_name }}",
preview_text: "Your account is ready",
html: "<!DOCTYPE html>\n<html>\n<body>\n<p>Hi {{ first_name }}, thanks for signing up.</p>\n</body>\n</html>\n",
},
},
});
console.log(template.id, template.draft_version_id);template = client.email.templates.create(
slug="welcome-email",
category="transactional",
source="html",
languages={
"en": {
"subject": "Welcome, {{ first_name }}",
"preview_text": "Your account is ready",
"html": "<!DOCTYPE html>\n<html>\n<body>\n<p>Hi {{ first_name }}, thanks for signing up.</p>\n</body>\n</html>\n",
},
},
)
print(template.id, template.draft_version_id)template, err := client.Email.Templates.Create(context.Background(), bird.EmailTemplatesCreateParams{
Slug: "welcome-email",
Category: "transactional",
Source: "html",
Languages: map[string]bird.EmailTemplateLanguageContent{
"en": {
Subject: bird.String("Welcome, {{ first_name }}"),
PreviewText: bird.String("Your account is ready"),
Html: bird.String("<!DOCTYPE html>\n<html>\n<body>\n<p>Hi {{ first_name }}, thanks for signing up.</p>\n</body>\n</html>\n"),
},
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(template.Id, *template.DraftVersionId)$template = $bird->email->templates->create(
slug: 'welcome-email',
category: 'transactional',
source: 'html',
languages: [
'en' => [
'subject' => 'Welcome, {{ first_name }}',
'preview_text' => 'Your account is ready',
'html' => "<!DOCTYPE html>\n<html>\n<body>\n<p>Hi {{ first_name }}, thanks for signing up.</p>\n</body>\n</html>\n",
],
],
);
echo $template->getId(), ' ', $template->getDraftVersionId();bird email templates create --body-file - <<'EOF'
{
"slug": "welcome-email",
"category": "transactional",
"source": "html",
"languages": {
"en": {
"subject": "Welcome, {{ first_name }}",
"preview_text": "Your account is ready",
"html": "<!DOCTYPE html>\n<html>\n<body>\n<p>Hi {{ first_name }}, thanks for signing up.</p>\n</body>\n</html>\n"
}
}
}
EOFcurl -X POST https://us1.platform.bird.com/v1/email/templates \
-H "Authorization: Bearer bk_us1_..." \
-H "Content-Type: application/json" \
-d '{
"slug": "welcome-email",
"category": "transactional",
"source": "html",
"languages": {
"en": {
"subject": "Welcome, {{ first_name }}",
"preview_text": "Your account is ready",
"html": "<!DOCTYPE html>\n<html>\n<body>\n<p>Hi {{ first_name }}, thanks for signing up.</p>\n</body>\n</html>\n"
}
}
}'Para cambiar un idioma después, guárdalo de nuevo con bird email templates versions languages set o un PUT en la misma ruta de idioma. El cuerpo es plano: subject, que es obligatorio, más preview_text, html y text. Un campo que omitas se borra. Añade el revision que leíste por última vez para rechazar el guardado si alguien más cambió ese idioma en el ínterin.
El diseño del editor visual del dashboard es uno de los campos que se borran de esta manera, porque la API, los SDKs y CLI no pueden enviarlo. Guardar un idioma que construiste en el editor visual conserva su HTML pero elimina el diseño, así que el dashboard reabre ese idioma en el editor de código.
Los pasos siguientes usan TEMPLATE_ID y DRAFT_VERSION_ID para el id y el draft_version_id que devolvió la llamada de creación. Lee el idioma del borrador para obtener el HTML exactamente como está almacenado:
const language = await bird.email.templates.versions.languages.get(
"TEMPLATE_ID",
"DRAFT_VERSION_ID",
"en",
);
console.log(language.content?.html);language = client.email.templates.versions.languages.get(
"TEMPLATE_ID",
"DRAFT_VERSION_ID",
"en",
)
if language.content is not None:
print(language.content.html)language, err := client.Email.Templates.Versions.Languages.Get(context.Background(),
"TEMPLATE_ID", "DRAFT_VERSION_ID", "en")
if err != nil {
log.Fatal(err)
}
if language.Content != nil && language.Content.Html != nil {
fmt.Println(*language.Content.Html)
}$language = $bird->email->templates->versions->languages->get(
'TEMPLATE_ID',
'DRAFT_VERSION_ID',
'en',
);
echo $language->getContent()?->getHtml();bird email templates versions languages get "$TEMPLATE_ID" "$DRAFT_VERSION_ID" en \
| jq -j .content.html > welcome-email.htmlcurl "https://us1.platform.bird.com/v1/email/templates/$TEMPLATE_ID/versions/$DRAFT_VERSION_ID/languages/en" \
-H "Authorization: Bearer bk_us1_..." \
| jq -j .content.html > welcome-email.htmljq -j escribe la cadena sin añadir un salto de línea al final, así que el archivo contiene los mismos bytes que almacenó la API.
Envía el borrador con validate_only primero. Ejecuta las mismas comprobaciones que un envío real en todos los idiomas y reporta los problemas que encuentra en una sola respuesta, sin publicar nada. Después envíalo sin validate_only para publicar la siguiente versión, que es la que los envíos usarán:
const templateId = "TEMPLATE_ID";
const draftVersionId = "DRAFT_VERSION_ID";
const check = await bird.email.templates.versions.submit(templateId, draftVersionId, {
validate_only: true,
});
for (const problem of check.errors) {
console.log(problem.language, problem.field, problem.message);
}
if (check.valid) {
const result = await bird.email.templates.versions.submit(templateId, draftVersionId);
console.log(result.version?.version_number);
}template_id = "TEMPLATE_ID"
draft_version_id = "DRAFT_VERSION_ID"
check = client.email.templates.versions.submit(template_id, draft_version_id, validate_only=True)
for problem in check.errors:
print(problem.language, problem.field, problem.message)
if check.valid:
result = client.email.templates.versions.submit(template_id, draft_version_id)
if result.version is not None:
print(result.version.version_number)ctx := context.Background()
templateID := "TEMPLATE_ID"
draftVersionID := "DRAFT_VERSION_ID"
check, err := client.Email.Templates.Versions.Submit(ctx, templateID, draftVersionID,
bird.EmailTemplatesVersionsSubmitParams{ValidateOnly: bird.Bool(true)})
if err != nil {
log.Fatal(err)
}
if check.Errors != nil {
for _, problem := range *check.Errors {
fmt.Println(*problem.Message)
}
}
if check.Valid != nil && *check.Valid {
result, err := client.Email.Templates.Versions.Submit(ctx, templateID, draftVersionID,
bird.EmailTemplatesVersionsSubmitParams{})
if err != nil {
log.Fatal(err)
}
fmt.Println(*result.Version.VersionNumber)
}$templateId = 'TEMPLATE_ID';
$draftVersionId = 'DRAFT_VERSION_ID';
$check = $bird->email->templates->versions->submit(
$templateId,
$draftVersionId,
(new EmailTemplateSubmit())->setValidateOnly(true),
);
foreach ($check->getErrors() ?? [] as $problem) {
echo $problem->getLanguage(), ' ', $problem->getField(), ' ', $problem->getMessage(), "\n";
}
if ($check->getValid() === true) {
$result = $bird->email->templates->versions->submit($templateId, $draftVersionId, new EmailTemplateSubmit());
echo $result->getVersion()?->getVersionNumber();
}bird email templates versions submit "$TEMPLATE_ID" "$DRAFT_VERSION_ID" --validate-only
bird email templates versions submit "$TEMPLATE_ID" "$DRAFT_VERSION_ID"curl -X POST "https://us1.platform.bird.com/v1/email/templates/$TEMPLATE_ID/versions/$DRAFT_VERSION_ID/submit" \
-H "Authorization: Bearer bk_us1_..." \
-H "Content-Type: application/json" \
-d '{"validate_only": true}'
curl -X POST "https://us1.platform.bird.com/v1/email/templates/$TEMPLATE_ID/versions/$DRAFT_VERSION_ID/submit" \
-H "Authorization: Bearer bk_us1_..." \
-H "Content-Type: application/json" \
-d '{}'El cuerpo del envío acepta validate_only y expected_revision. Pasa el revision de la plantilla como expected_revision para rechazar la publicación si el borrador cambió desde la última vez que lo leíste.
Lista las plantillas de tu espacio de trabajo para encontrar el slug que nombra un envío:
for await (const tpl of bird.email.templates.list({ scope: "workspace" })) {
console.log(tpl.slug, tpl.name);
}for template in client.email.templates.list(scope="workspace"):
print(template.slug, template.name)for tpl, err := range client.Email.Templates.List(context.Background(), bird.EmailTemplatesListParams{Scope: "workspace"}) {
if err != nil {
log.Fatal(err)
}
fmt.Println(*tpl.Slug, tpl.Name)
}foreach ($bird->email->templates->list(['scope' => 'workspace']) as $template) {
echo $template->getSlug(), ' ', $template->getName(), "\n";
}bird email templates list --scope workspacecurl "https://us1.platform.bird.com/v1/email/templates?scope=workspace" \
-H "Authorization: Bearer bk_us1_..."Envía la plantilla publicada por slug, con un valor en template.parameters para cada parámetro que usa, aquí first_name:
const msg = await bird.email.send({
from: { email: "onboarding@messagebird.dev", name: "Bird" },
to: ["delivered@messagebird.dev"],
category: "transactional",
template: {
slug: "welcome-email",
parameters: { first_name: "Jane" },
},
});
console.log(msg.id, msg.status);msg = client.email.send(
from_={"email": "onboarding@messagebird.dev", "name": "Bird"},
to=["delivered@messagebird.dev"],
category="transactional",
template="welcome-email",
parameters={"first_name": "Jane"},
)
print(msg.id, msg.status)msg, err := client.Email.Send(context.Background(), bird.EmailSendParams{
From: "onboarding@messagebird.dev",
To: []string{"delivered@messagebird.dev"},
Category: "transactional",
Template: "welcome-email",
Parameters: map[string]any{"first_name": "Jane"},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(msg.Id, *msg.Status)$message = $bird->email->send(
from: 'Bird <onboarding@messagebird.dev>',
to: ['delivered@messagebird.dev'],
category: 'transactional',
template: (new EmailMessageSendRequestTemplate())
->setSlug('welcome-email')
->setParameters(['first_name' => 'Jane']),
);
echo $message->getId(), ' ', $message->getStatus();bird email send \
--category transactional \
--from hello@yourdomain.com \
--parameters '{"first_name":"Jane"}' \
--template welcome-email \
--to delivered@messagebird.devcurl -X POST https://us1.platform.bird.com/v1/email/messages \
-H "Authorization: Bearer bk_us1_..." \
-H "Content-Type: application/json" \
-d '{
"from": "hello@yourdomain.com",
"to": ["delivered@messagebird.dev"],
"category": "transactional",
"template": {
"slug": "welcome-email",
"parameters": { "first_name": "Jane" }
}
}'Próximos pasos
- Enviar email: el payload de envío completo y cómo encajan en él los envíos con plantilla
- Categorías: elegir
marketingotransactionalen 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 plantilla
- SDKs: los métodos tipados de
email.templatesen TypeScript, Python, PHP y Go - Servidor MCP: permitir que un agente cree y publique plantillas
- Cómo crear una plantilla de email: un vídeo que construye una plantilla en el dashboard y luego hace que un agente construya otra
Recursos relacionados
Continúa con la documentación, guías y ejemplos de este tema.