Migrate from MessageBird (classic) to Bird.

If you're on the legacy messagebird SDK against rest.messagebird.com, this is your path forward. The same account, the same number pool, the same suppression list — new namespace, new base URL, stricter contract. Dual-key window holds through Dec 31 2026.

Eleven things that moved.

The shape of the API moved with the rename. Auth header, base URL, key prefix, and response style all changed. The contract is stricter — idempotency keys and the User-Agent header are now required.

AreaClassicv1
Base URLhttps://rest.messagebird.com/https://api.bird.com/v1/
Auth headerAccessKey live_xxxxxxxxAuthorization: Bearer bird_live_xxxxxxxx
Key prefix (live)live_xxxxxxxxbird_live_xxxxxxxx
Key prefix (test)test_xxxxxxxxbird_test_xxxxxxxx
SDK packagemessagebird (npm)@bird/sdk (npm)
Top-level namespacemessagebird.*bird.*
Response shapecallback-style (err, response)async/await · { data, error } destructure
User-Agentoptionalrequired · 403 without it
Idempotency-Keyoptionalrequired on every POST · 24h dedupe
Paginationoffset / limitcursor: ?limit=50&starting_after=...
Error envelopemixed shapes per endpoint{ type, message, doc_url } · single shape

The send path, both shapes.

Callback-style with a custom auth header on the left. Async with the standard Bearer token on the right. The fields line up; the contract is what changed.

classic-send.ts
before
import { initClient } from "messagebird";

const mb = initClient(process.env.MESSAGEBIRD_KEY!, "ENABLE_FEATURES");

// Base URL: https://rest.messagebird.com/
// Auth header: "AccessKey live_xxxxxxxx"
mb.messages.create(
  {
    originator: "Bird",
    recipients: ["+14155550172"],
    body:       "Your code is 4242.",
  },
  (err, response) => {
    if (err) throw err;
    console.log(response.id);
  },
);
v1-send.ts
after
import { BirdClient } from "@bird/sdk";

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

// Base URL: https://api.bird.com/v1/
// Auth header: "Authorization: Bearer bird_live_xxxxxxxx"
const { data, error } = await bird.sms.send({
  from: "Bird",
  to:   "+14155550172",
  text: "Your code is 4242.",
});

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

Six routes with v1 replacements.

These are the routes that changed shape, not just URL. The rest of the surface is a straight namespace swap.

Classic endpointv1 endpointNote
POST /messagesPOST /v1/smsSame fields. recipients[] → to (string or array).
POST /conversationsPOST /v1/whatsapp/messages, /v1/smsChannel-specific endpoints. Conversation state on you.
POST /verifyPOST /v1/verificationsfallback channel is a single attribute.
POST /lookupGET /v1/lookupGET, not POST. Single response with all fields.
POST /voice/callsPOST /v1/voice/callsSame shape. TwiML-compatible XML accepted.
WhatsApp session-window state in dashboard onlysession_window field on every send responseSDK surfaces remaining seconds; flag templated vs free-form sends.

Both keys work against the same account through Dec 31 2026.

You don't have to migrate every service in the same week. Pick one service, cut it over to bird_live_*, validate it for a week. Move the next one. The underlying account, sender pool, and suppression list are shared.

dual-key.ts
parallel
// Both keys work against the same account through Dec 31 2026.
// Use the dual-key window to migrate services one at a time —
// no big-bang switchover.

// Service A: still on the legacy SDK
import { initClient } from "messagebird";
const mb = initClient(process.env.MESSAGEBIRD_KEY!);

// Service B: cut over to v1
import { BirdClient } from "@bird/sdk";
const bird = new BirdClient({ apiKey: process.env.BIRD_API_KEY! });

// Both calls hit the same underlying account, same sender pool,
// same suppression list, same billing line.
//
// On Dec 31 2026 the legacy auth scheme is retired. Rotate the
// last service to the v1 key before then.

Five dates to plan around.

Anchor your migration plan against the Dec 31 2026 cutoff. If you wait until December, you'll be doing a code change in the middle of the holiday freeze.

  1. TodayClassic keys (live_*, test_*) keep working against rest.messagebird.com and the v1 surface in compatibility mode.
  2. Jun 1 2026v1 SDK and api.bird.com/v1 become the documented default. Classic surface stays online unchanged.
  3. Sep 30 2026New endpoints (Verifications, Lookup v2, RCS, Push) ship to v1 only. Existing classic endpoints still served.
  4. Dec 1 2026Deprecation warnings surface in the dashboard for accounts still issuing requests against rest.messagebird.com.
  5. Dec 31 2026Classic auth scheme retired. Requests to rest.messagebird.com return 410 Gone with a doc_url pointing at this page.

WhatsApp session window. Now exposed in the SDK, not just the dashboard.

WhatsApp's 24-hour customer service window controls when you can send a free-form message vs a templated one. On classic the state lived only in the dashboard. In v1 every WhatsApp send response includes a session_window object with the remaining seconds and the message type that's currently allowed — you can branch on it before composing the next message.

Every POST. Twenty-four-hour dedupe.

Classic accepted Idempotency-Key as a polite-but-optional header. v1 requires one. The SDK auto-generates a v4 UUID if you don't supply one; for retry safety, prefer a deterministic key tied to your business logic (user id + operation + attempt id).

idempotent-send.ts
required
import { BirdClient } from "@bird/sdk";
import { randomUUID } from "node:crypto";

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

// Required on every POST in v1. The SDK auto-generates a v4 UUID
// if you don't supply one, and surfaces it back on the response.
const { data, error } = await bird.sms.send(
  {
    from: "Bird",
    to:   "+14155550172",
    text: "Your code is 4242.",
  },
  {
    idempotencyKey: `send-otp-${userId}-${attemptId}`,
  },
);

// Re-running the same call with the same key inside the 24h window
// returns the original response — no duplicate send, no double charge.
if (error?.type === "idempotent_replay") {
  console.log("Already sent:", error.original_id);
}

If you used Conversations heavily on classic, plan for state.

Classic POST /conversations was a stateful chat surface on top of SMS, WhatsApp, and webchat. v1 routes the underlying channels but doesn't maintain conversation state for you. Most teams keep their existing data store and call bird.sms.send / bird.whatsapp.send directly; the channel-level webhooks (with conversation_id in your own tags) handle threading.

Bring your classic account forward.

Existing customers: log in with your classic credentials and a bird_live_* key shows up in the dashboard immediately. New to Bird? Start with a test key.

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.

Get startedRead docsor

Using Claude Code, Cursor, or Codex? Point it at our MCP server — tools for every channel we expose, with scoped agent keys.

Cursor