Engage customers worldwide with WhatsApp API
Connect marketing, service, and ops teams with customers on the world's most popular messaging app.
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"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.
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.
- 01
Official Meta Business Solution Provider (BSP)
Direct relationship with Meta since the API existed. No resold transit, no third-party hop.
- 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.
- 03
Templates in every language
One slug, many languages. Name one on the send, or let the template's default resolve.
- 04
Buttons and carousels
Link, quick-reply, phone-number and copy-code buttons, and 2-10 card carousels.
- 05
Media and rich content
Images, video, audio, stickers, documents, and location, on one send field each.
- 06
Tags and metadata on every send
Tags become filter and analytics dimensions; metadata rides back on every webhook.
- 07
Inbound message webhooks
HMAC-signed events for inbound messages, delivery receipts, and read receipts.
- 08
3B+ users on one endpoint
Three billion-plus monthly WhatsApp users addressable from a single bird.whatsapp.send call.
Explore the WhatsApp platform
Each capability in depth. One API, one set of keys.
Templates.
Categories, per-language approval, and placeholders filled at send time.
Sending.
One content kind per request, idempotency keys, tags, and metadata.
Two-way.
Inbound messages, the 24-hour customer service window, and replies.
Numbers.
Bird-managed senders, bringing your own number, and business accounts.
Pricing.
One all-in rate per message, by destination country and category.
FAQs.
Every WhatsApp question in one place, from first send to analytics.
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.
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.
{
"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.
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
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.