Engage customers worldwide with WhatsApp API

Connect marketing, service, and ops teams with customers on the world's most popular messaging app.

send-notification.ts
202 · 480ms
import { BirdClient } from "@messagebird/sdk";

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

const msg = await bird.whatsapp.send({
  to: "+15551234567",
  template: {
    slug: "bird_delivery_update",
    components: [{ type: "body", parameters: [
      { type: "text", name: "ref",  text: "#4821" },
      { type: "text", name: "date", text: "Wednesday" },
    ] }],
  },
});

console.log(msg.id, msg.status);
// → "wam_01krdgeqcxet5s7t44vh8rt9mg", "accepted"
Reminder: you have an appointment on 3 Sep at 14:30. We look forward to seeing you.9:42 AM
Reschedule
Your order #4821 is out for delivery, arriving Wednesday. Thanks for shopping with us.9:43 AM
Your subscription renews on 3 Sep for €12.00. No action is needed.9:44 AM
View plan

5 minutes from npm install to first send

Send a WhatsApp from the language you already use.

SDKs in every major runtime. The first send goes out on a Bird-managed template like bird_delivery_update, already approved by Meta and picking its own sender, so you watch a real message land before you author one of your own.

1
2
3
4
5
6
7
8
const msg = await bird.whatsapp.send({
  to: "+15551234567",
  template: {
    slug: "bird_otp",
    components: [{ type: "body", parameters: [{ type: "text", text: "123456" }] }],
  },
});
console.log(msg.id, msg.status);

Eight things we handle between you and Meta.

WhatsApp is gated: an approved template, an opted-in recipient, a verified business. Those gates do not move. Your provider decides whether they show up in your code or get buried in a dashboard.

  1. 01

    Official Meta Business Solution Provider (BSP)

    Direct relationship with Meta since the API existed. No resold transit, no third-party hop.

  2. 02

    Template management

    Read the catalog and Meta's verdict per language from the CLI or MCP tools. Authoring and submission are in the dashboard.

  3. 03

    Templates in every language

    One slug, many languages. Name one on the send, or let the template's default resolve.

  4. 04

    Buttons and carousels

    Link, quick-reply, phone-number and copy-code buttons, and 2-10 card carousels.

  5. 05

    Media and rich content

    Images, video, audio, stickers, documents, and location, on one send field each.

  6. 06

    Tags and metadata on every send

    Tags become filter and analytics dimensions; metadata rides back on every webhook.

  7. 07

    Inbound message webhooks

    HMAC-signed events for inbound messages, delivery receipts, and read receipts.

  8. 08

    3B+ users on one endpoint

    Three billion-plus monthly WhatsApp users addressable from a single bird.whatsapp.send call.

Why we build WhatsApp

We were one of the first WhatsApp BSPs. We're still one of the few that ship code with you.

WhatsApp is gated. You need an approved template; you need an open customer service window to send anything but one; you need a Meta business verification. That part doesn't change, and won't. What changes is whether your BSP makes those gates easier or harder to walk through: by exposing them in your code, on webhooks you can subscribe to, in errors that say exactly what's wrong. We chose the first.

send-notification.ts
202 · 480ms
import { BirdClient } from "@messagebird/sdk";

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

const msg = await bird.whatsapp.send({
  to: "+15551234567",
  template: {
    slug: "bird_delivery_update",
    components: [{ type: "body", parameters: [
      { type: "text", name: "ref",  text: "#4821" },
      { type: "text", name: "date", text: "Wednesday" },
    ] }],
  },
});

console.log(msg.id, msg.status);
// → "wam_01krdgeqcxet5s7t44vh8rt9mg", "accepted"

Every state change is a webhook.

HMAC-signed payloads, replay-protected, idempotent. The same envelope on every Bird channel: learn one, you've learned them all.

POST /webhooks/bird
signed
{
  "type": "whatsapp.read",
  "timestamp": "2026-05-19T15:42:08.114Z",
  "data": {
    "whatsapp_id":  "wam_01krdgeqcxet5s7t44vh8rt9mg",
    "workspace_id": "ws_01krdgeqcxet5s7t44vh8rt9mg",
    "direction":    "outbound",
    "from":         { "phone_number": "+15557654321" },
    "to":           { "phone_number": "+15551234567" },
    "tags":         [{ "name": "campaign", "value": "order-updates" }],
    "metadata":     { "order_id": "BRD-49217" }
  }
}

Retry schedule: 5s, 5m, 30m, 2h, 5h, then 10h twice. After the last one the delivery is permanently failed, and replay recovers it from the dashboard or API.

  • whatsapp.acceptedAccepted by the API and queued for send to Meta.
  • whatsapp.sentHanded off to Meta's Cloud API.
  • whatsapp.deliveredMeta reports the message landed on the recipient's device.
  • whatsapp.readRecipient opened the message (if read receipts are on).
  • whatsapp.rejectedRefused before send, and not charged: reason code in the payload.
  • whatsapp.failedPermanent failure: reason code in the payload.
  • whatsapp.receivedInbound message from a WhatsApp user.

Reaching the same customer on SMS is the same call, one field over.

Same client, same auth, same error envelope, same webhook shape. What changes is the payload: WhatsApp carries a template Meta approved, SMS carries text. Tags and metadata ride along on both, so one set of dashboards covers the pair.

WhatsApp

whatsapp
await bird.whatsapp.send({
  to: "+15551234567",
  template: {
    slug: "bird_delivery_update",
    language: "en",
    components: [{ type: "body", parameters: [
      { type: "text", name: "ref",  text: "BRD-49217" },
      { type: "text", name: "date", text: "10 Jul 2026" },
    ] }],
  },
});

A Bird-managed template: approved by Meta, stocked in 70+ languages, and it picks its own sender. Placeholder values ride in as components.

SMS

sms
await bird.sms.send({
  from:     "Bird",
  to:       "+15551234567",
  text:     `Your order BRD-49217 has shipped.`,
  category: "transactional",
});

The same verb on the other channel: free-form text plus a category, no template approval in the way.

One rate per message, with Meta's fee included.

Priced by usage. Each rate covers Meta's fee and ours in a single number, and moves with the destination country and message category. No seat fee, and nothing locked behind an annual commit.

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