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 placeholders de {{ 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 Insertar imagen 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 panel, elige Visual > Insertar imagen para buscar en tu biblioteca de medios 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 configurar su Descripción de imagen, ancho de visualización, alineación y enlace. Marca una imagen como Imagen decorativa solo cuando no aporta información; una imagen con enlace necesita una descripción que explique su destino. Cada idioma mantiene sus propias descripciones de imagen y diseño.
Usa Reemplazar imagen 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 Más acciones > Correo de prueba para enviarte el contenido actual a ti mismo. Código 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
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.
Un broadcast no tiene objeto parameters, así que su contenido solo puede usar marcadores bird.. bird.contact.<attribute> se llena con 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.
Hi {{ bird.contact.first_name }},Cada parámetro de la plantilla necesita un valor cuando envías. De lo contrario, 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 ausente se renderiza como un valor vacío, así que agrega un valor por defecto para el contenido visible al cliente: {{ bird.contact.first_name | default: "there" }}.
Un broadcast es más estricto en cuanto a los nombres que acepta, porque las propiedades de contacto son lo único con lo que puede llenar los marcadores. Sus marcadores bird.contact.* solo pueden nombrar un campo integrado o una propiedad de contacto que el espacio de trabajo haya registrado. Cualquier otro marcador, incluido un parámetro, es uno que el broadcast no tiene forma de llenar. El envío se rechaza y el error nombra el marcador.
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.
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 de API de mensajes.
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. Eso hace que la vista previa sea la herramienta correcta para comprobar contenido que estás a punto de enviar, y la incorrecta para consultar qué contenía un envío anterior. Para ver lo que un envío realmente entregó, abre ese mensaje en el registro de correo electrónico, 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 panel, 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 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 tiene ninguna; y untextvacío se rellena con una alternativa en texto plano derivada del HTML. Leer esa versión devuelve el contenido reescrito, así que no coincidirá con 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 }}, que es un parámetro: el envío proporciona su valor en template.parameters, como en "parameters": {"first_name": "Jane"}. Eso es adecuado para un envío a una dirección de correo electrónico. Un placeholder {{ bird.contact.first_name }} lee el registro de contacto del destinatario, que un broadcast rellena a partir de las propiedades de contacto de cada destinatario. Un envío a una dirección de correo electrónico no tiene registro de contacto, así que rechaza una plantilla que lea bird.contact.* con un 422 (EmailTemplateContactUnavailable).
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 panel y luego hace que un agente construya otra
Recursos relacionados
Continúa con la documentación, guías y ejemplos de este tema.