Email templates

Personal by design. Ready at send time.

Design an email worth opening. Publish a version, then personalize every send. Build in the dashboard or with the API, CLI and MCP; your team works with the same stored templates.

Cursor

Template studio

Published version
first_name: "Alex"

Example designs, personalized with each recipient’s values. Nothing is sent.

Trusted every day by teams that build world-class software

Read more customer stories

Store a template. Personalize at send.

The markup already lives in Bird.

Templates are part of the Bird Email API. Store the layout and its logic once; each send names the template by slug or id and passes the values its tokens need. The final message is rendered on our side, so the same template backs one receipt, a batch of a hundred, or a broadcast to a whole audience.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { BirdClient } from "@messagebird/sdk";

const bird = new BirdClient({ apiKey: process.env.BIRD_API_KEY! });

// No subject and no html: the template's published version supplies both.
const { data, error } = await bird.email
  .send({
    from:     "orders@acme.com",
    to:       ["delivered@messagebird.dev"],
    category: "transactional",
    template: {
      slug:       "order-confirmation",
      parameters: {
        first_name: "Ada",
        order_number: "A-1043",
      },
    },
  })
  .safe();

More than find-and-replace.

Liquid, resolved on Bird's side when the message is generated.

  1. 01

    Conditionals.

    A Liquid if block shows only when its value applies, like a members-only note or a free-shipping banner, so one template covers both cases.

  2. 02

    Loops.

    A for loop repeats a row for each item in an array, so one order-confirmation template lists every line item a customer actually bought. One limit worth knowing up front: a broadcast carries a single value per contact property and has nothing to iterate, so a template that loops sends through the messages API rather than a broadcast.

  3. 03

    Filters.

    Pipe a value through a Liquid filter before it renders. The default filter catches a missing first name before it ships as an empty greeting.

  4. 04

    Named parameters and contact fields.

    Use flat names for your own parameters, such as order_number. Reserved bird.contact fields read recipient properties. Ordinary dotted parameter paths are rejected when publishing.

  5. 05

    Nothing to declare.

    The token list is read off your markup, combined across every language, so there's no separate variable schema to keep in step with the body. Use the parameter shapes documented for your sending workflow; broadcasts are limited to registered contact properties.

Author it the way you already work.

Three ways in, all reaching the same template. The dashboard builder is the visual one. The bird CLI drives the whole lifecycle from a script or a deploy step, and an agent reaches the same operations over MCP. Already have React Email components? Render them to HTML and store the result, leaving Bird's tokens as literal text in the JSX so they survive the render instead of baking in a value when React runs.

publish-receipt.sh
bird CLI
# Render React Email to HTML, then publish it as a template version.
node scripts/render-receipt.mjs > receipt.json

bird email templates create receipt --category transactional --source html
bird email templates versions languages set "$TEMPLATE" "$DRAFT" en \
  --body-file receipt.json --yes

# --validate-only reports every problem across every language, freezing nothing.
bird email templates versions submit "$TEMPLATE" "$DRAFT" --validate-only --yes
bird email templates versions submit "$TEMPLATE" "$DRAFT" --yes

A creative workflow you can trust

Beautiful in the inbox. Dependable behind the scenes.

Versioned, like the rest of your code.

Editing happens on a draft and never touches what's live, because a send always resolves the current published version and drafts are never sent. Publishing freezes an immutable, numbered version and makes it live; if a change goes wrong, roll back to an earlier one. Saves carry the revision you last read, so if a teammate changed that language meanwhile the save is refused as a conflict rather than overwriting their work. Delivery and engagement stats break down per template, so you can see which one is actually landing.

One template, up to 25 languages.

A template carries content in up to 25 languages, each with its own subject and body, keyed by a BCP-47 tag like en or pt-BR. A send names the language it wants, or leaves it off and gets the template's default. When a send asks for a language the template doesn't carry, on_missing_language decides what happens: fallback serves the closest match, so a request for pt-BR is answered by a stocked pt, and fail rejects the send outright, for content where the wrong language is worse than no send at all.

Preview exactly what will ship.

Fill a template with sample values and get back the subject and the HTML and text bodies a send would deliver. Preview renders the draft, which is how you check a change before it goes live, or a published version when you want to see what is going out right now. Nothing is sent. It also runs the same personalization checks as publishing, so a construct that would be rejected shows up here first.

Start from a reusable draft.

Start with the dashboard editor or copy a built-in template into an editable draft. Check subject, preview text, language and contact fallbacks before publishing. Keep the published version stable while you prepare the next change.

Go deeper in the docs.

The templates guide covers drafts, published versions, per-language content and the Liquid rules. The sending guide has the send-side contract, and email events and webhooks gets opens and clicks flowing back.

Put it into practice.

Continue with the documentation, guides and examples for this topic. Resources are in English.

Try the practice and get an implementation brief

Questions about templates

Do I have to use a visual builder?
No. Use the dashboard editor or author HTML through the CLI and MCP workflows. React Email output can be stored as HTML with Bird placeholders preserved. The API, CLI and MCP operate on the same drafts and published versions you see in the dashboard.
How does personalization work?
Stored templates support Liquid and named parameters. Ordinary parameter names are flat, such as order_number. Reserved bird.contact fields read recipient properties. Broadcasts use registered contact properties and cannot use template loops; the messages API supports more general template sends.
Can I roll back a template?
Yes. Editing changes a draft. Publishing creates an immutable version used by later sends. Rolling back selects an earlier published version and resets the draft to that content; sends already accepted keep their content.
Can one template serve several languages?
A template can hold language-specific subject, preview text and body content. Configure a default language and decide whether a missing language falls back or fails. Preview with representative contact values before publishing.

Talk to our email team

Create email your team can reuse.

Discuss your template workflow, personalization and integration. We’ll help you plan how your team builds and sends email with Bird.

Create your account, then create a reusable template and preview its content.

Your details

All contact fields are required.

So our team can reach you about your demo.

Products of interest

Optional

We’ll contact you to arrange your demo.
Privacy policy

Start with one channel.
Add the others when you're ready.

A test API key is yours immediately. Production unlocks when you add a payment method and verify a sender.

Using Claude Code, Cursor, or Codex? Copy a setup prompt and your agent installs the Bird CLI and skills for you. Pick yours:

Cursor