Templates

Templates let you author a message once and run it across email, SMS, WhatsApp, and voice. Variable substitution is the same shape on every channel; what differs is the rendering target — React on email, plain text on SMS, Meta-approved bodies on WhatsApp, TTS on voice.

Email templates

React Email is the recommended authoring path. MJML and raw HTML are both accepted as alternatives — pass mjml: or html: instead of react:. The SDK renders React Email components to MIME-safe HTML + a plain-text fallback before the request leaves your process.

// emails/welcome.tsx
import { Html, Body, Container, Heading, Text, Button } from "@react-email/components";

export function WelcomeEmail({ name }: { name: string }) {
  return (
    <Html>
      <Body>
        <Container>
          <Heading>Hi {name}, your invite is ready.</Heading>
          <Text>You can start sending in under five minutes.</Text>
          <Button href="https://bird.dev/docs/quickstart">Read the quickstart</Button>
        </Container>
      </Body>
    </Html>
  );
}
import { WelcomeEmail } from "./emails/welcome"

const { data, error } = await bird.email.send({
  to:      ["delivered@bird.dev"],
  from:    "Bird <hello@bird.dev>",
  subject: "Your Bird invite is ready",
  react:   <WelcomeEmail name="Ada" />,
})

SMS templates

SMS templates are stored bodies with {{variable}} substitution. The SDK counts segments and warns when a render would push you over a segment boundary; Unicode runs are detected automatically and tracked as 70-char segments instead of 160.

Define the template once, send it by name:

const { data, error } = await bird.sms.send({
  to: "+15005550006",
  from: "+18885550101",
  template: "otp_default",
  variables: { code: "482917" },
});

The template body lives in your account — Hi {{name}}, your code is {{code}}. Don't share it. — and is referenced by name on every send. The response includes segments and encoding so you can log per-recipient cost.

WhatsApp templates

WhatsApp templates require Meta approval before they can be sent outside a 24-hour session window. The flow is: submit, Meta reviews, approved (or rejected), webhook fires, you can send.

Gotchas to know up front:

  • Header media type is fixed at approval time. A template approved with an image header cannot later send with a video header — submit a separate template.
  • Buttons have hard limits. Three reply buttons or two CTAs max; mixing types in one template is rejected.
  • Variable index ordering is positional. Meta sees {{1}}, {{2}}, {{3}} and your SDK call must pass variables in the same order they appear in the body.
  • Locale variants are separate templates upstream. Bird groups them under one name on send; Meta tracks each en_US, es_MX, pt_BR as its own approved row.
const { data: tmpl, error: createError } = await bird.whatsapp.templates.create({
  name: "order_confirmed",
  language: "en_US",
  category: "UTILITY",
  body: "Hi {{1}}, your order {{2}} is on its way. ETA: {{3}}",
  variables: ["name", "order_id", "eta"],
});

// `tmpl.state` is "pending" — wait for the `template.approved` webhook

const { data: msg, error: sendError } = await bird.whatsapp.send({
  to: "+15005550009",
  from: "+18885550101",
  template: "order_confirmed",
  variables: { name: "Ada", order_id: "A-2491", eta: "today, 4pm" },
});

Voice templates

Voice templates are TTS bodies with the same {{variable}} syntax as SMS. Use them in a say step inside a flow.

const { data, error } = await bird.voice.calls.create({
  to: "+15005550010",
  from: "+18885550101",
  flow: [
    {
      say: {
        template: "verification_voice",
        variables: { code: "482917" },
        voice: "en-US-amy",
      },
    },
  ],
});

The stored template body — Your verification code is {{code}}. Again, {{code}}. — is rendered to audio by Bird's streaming TTS at call time. First audio byte under 250ms.

The bird.templates.* SDK surface

Channel-agnostic stored templates live under bird.templates. WhatsApp's Meta-approved templates live under bird.whatsapp.templates and are covered above.

MethodEndpointWhat it does
bird.templates.createPOST /v1/templatesDefine a new template with channel-specific bodies.
bird.templates.getGET /v1/templates/{name}Fetch the latest version (or a pinned version) of a template.
bird.templates.listGET /v1/templatesList templates, filterable by channel and tag.
bird.templates.updatePATCH /v1/templates/{name}Create a new version. Prior versions remain renderable.

get supports If-None-Match against the version etag. update is additive — past versions remain pinnable from sends with template: { name, version: 3 }.

The template-sync CLI

@bird/templates is an OSS library — and CLI — for authoring templates in code and syncing them to your Bird account. Source at github.com/bird-io/templates. One command in CI keeps your stored templates in lockstep with your repo:

bird templates sync ./templates

The CLI also runs a local preview server for iterating on React Email templates without round-tripping to the API.

Commencez avec un seul canal.
Ajoutez les autres quand vous êtes prêt.

Une clé API de test est disponible immédiatement. L'accès production se débloque dès que vous ajoutez un moyen de paiement et vérifiez un expéditeur.

CommencerLire la docou

Vous utilisez Claude Code, Cursor ou Codex ? Connectez-le à notre serveur MCP — 141 outils, un par endpoint API, avec des clés d'agent à portée limitée.