Email templates

In preview

Templates with logic, rendered at send time.

Store an email template in Bird and reference it by id. Conditionals, loops, and defaults render per recipient when the message is sent, so the personalization logic stays in the template instead of scattered through your code. Author it as HTML with declared variables, or render your React Email components.

welcome.tsx
200 · 1.2s
import { BirdClient } from "@messagebird/sdk";
import { render } from "@react-email/render";
import { WelcomeEmail } from "./emails/welcome";

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

const { data, error } = await bird.email.send({
  from:    "Bird <hello@bird.com>",
  to:      ["ada@example.com"],
  subject: "Your invite is ready",
  html:    await render(<WelcomeEmail name="Ada" />),
}).safe();

if (error) throw error;
console.log(data.id);
// → "em_2bX91Yk8h..."

Store a template. Personalize at send.

Send only the values that change per recipient.

Templates are part of the Bird Email API. Store the layout and its logic once; on each send you reference the template and pass a flat set of per-recipient values. Bird renders the final message on its side, so the same template drives one email or a million.

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

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

// Store a template once. Conditionals and loops render per recipient at send.
const tpl = await bird.templates.create({
  name:    "order-confirmation",
  subject: "Order {{ order_number }} confirmed",
  html:    "<h1>Thanks, {{ first_name or 'there' }}.</h1>" +
           "{{ if is_member }}<p>You earned {{ points }} points.</p>{{ end }}",
  variables: [
    { key: "first_name",   type: "string", fallback: "there" },
    { key: "order_number", type: "string" },
    { key: "is_member",    type: "boolean" },
    { key: "points",       type: "number" },
  ],
});

// Send it. Bird fills in the blanks for each recipient.
await bird.email.send({
  from:      "orders@acme.com",
  to:        ["ada@example.com"],
  template:  tpl.id,
  variables: { first_name: "Ada", order_number: "A-1043", is_member: true, points: 120 },
});

More than find-and-replace.

Real templating logic, resolved on Bird's side, per recipient, at send.

  1. 01

    Conditionals.

    Show a block only when a value applies, like a members-only note or a free-shipping banner, without sending two different templates.

  2. 02

    Loops.

    Repeat a row for each item in an array, so one order-confirmation template lists every line item a customer actually bought.

  3. 03

    Defaults.

    Fall back to a safe value when a field is missing, so a blank first name never ships as an empty greeting.

  4. 04

    Nested data.

    Reach into structured objects with dotted paths, not just flat top-level keys, so your existing data shapes map straight in.

  5. 05

    Rich variable types.

    Declared variables can be strings, numbers, booleans, arrays, and objects, which is what makes the loops and conditionals above possible.

Author it the way you already work.

Send plain HTML with a list of declared variables through the API, and you have a working template, no new editor to learn. Prefer components? Render your React Email templates to HTML and store the result. The one thing to know: leave Bird's tokens as literal text in the JSX so they survive the render and get filled at send, instead of baking in a value when React renders.

receipt.ts
React Email
import { render } from "@react-email/render";
import { Receipt } from "./emails/receipt";

// Author in React. Leave Bird's tokens as literal text so they
// survive the render and get personalized per recipient at send.
const html = await render(<Receipt firstName="{{ first_name }}" />);

await bird.templates.create({
  name:    "receipt",
  subject: "Your receipt",
  html,
  variables: [{ key: "first_name", type: "string", fallback: "there" }],
});

Versioned, like the rest of your code.

Editing happens on a draft and never touches what's live. Publishing freezes an immutable, numbered version and pushes it; if a change goes wrong, roll back to an earlier version and re-publish. Concurrent edits are caught rather than silently overwritten, so two people working on the same template don't clobber each other. Every version carries a stable identity, which is what lets per-template reporting attribute opens and clicks correctly.

Keep every brand consistent.

A brand kit holds your colors, type, spacing, logo, and voice, and applies them to a template so the output looks and reads like you without restyling each one by hand. Kits live at the workspace level and a workspace can keep several, because a parent brand, a sub-brand, and your plain transactional mail rarely want the same look. Using a kit is optional: a template with none falls back to clean defaults.

Preview exactly what will ship.

Fill a template with sample values and see the rendered message before anyone receives it. The preview runs through the same rendering path as the real send, so what you approve is what goes out, not a close-enough approximation that drifts once tokens and logic resolve for real recipients.

Where templates are headed.

The stored-template model is the foundation for what comes next: describe a template in a prompt and generate a draft to refine, build one visually without writing HTML, and let coding agents compose on-brand templates over MCP, plus a library of starter templates to begin from. These build on the same versioned, brand-aware model, not a separate track, so the API you integrate today is the one they extend.

Go deeper in the docs.

See how templates fit the sending guide, including rendering React Email, and wire up email events and webhooks so opens and clicks flow back per template.

Templates FAQ

Do I have to use a visual builder?+
No. You can create and manage templates entirely through the API: send your HTML with a list of declared variables, and reference the template by id when you send. The builder is one way in, not the only one.
How is a stored template different from passing HTML on every send?+
A stored template is referenced by id and versioned, and its personalization renders on Bird's side at send. So you send only the per-recipient values, not the whole body each time, and the layout and logic live in one place instead of being rebuilt in your code on every call.
What can the personalization do beyond find-and-replace?+
Conditionals, loops over arrays, nested fields, and per-field defaults, all resolved per recipient at send. A membership block can show only for members; an order can list its line items. The logic is stored in the template, so a flat key-value payload per recipient is enough to drive it.
Does React Email work?+
Yes. Render your React Email components to HTML and store the result as the template body. For per-recipient values, leave Bird's tokens as literal text in the JSX so they survive the render and get filled at send, rather than baking in a value when React renders.
Can I roll back a template I broke?+
Publishing freezes an immutable, numbered version. Editing happens on a draft and never touches what's live until you publish, and you can roll back to any earlier published version and re-publish it. Each version keeps a stable identity, which is what lets per-template reporting attribute sends correctly.
Is there AI generation or a drag-and-drop builder?+
They're what we're building toward, not what ships first. The API (HTML with declared variables) and React Email are the authoring paths available today; a visual builder, prompt-to-template generation, and an agent compose surface come on top of the same stored-template model.
How do I actually send a stored template?+
The send call is unchanged: reference the template by id and pass the per-recipient values, and the existing email send API handles the rest. There's no separate template-send endpoint to learn.

Templates are part of a platform, not a point tool.

Store, version, and personalize templates on the same Email API that handles sending, deliverability, suppression, and analytics. One set of keys.

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