> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Migrate from MessageBird (Bird) to Orbit: SMS, Flows, and WhatsApp

> Migrate your MessageBird integration to Orbit step by step, mapping access keys, Flow Builder, WhatsApp channels, MNM lookups, Verify, and delivery-report webhooks to their Orbit equivalents.

# Migration from MessageBird to Orbit

This guide walks you through migrating your SMS, Flow Builder, WhatsApp, lookup, and Verify integration from MessageBird (Bird) to Orbit. The manual migration proceeds incrementally — traffic stays on MessageBird until you redirect your own integration.

## Concept Mapping

| MessageBird concept                   | Orbit equivalent                                           | Notes                                                                        |
| ------------------------------------- | ---------------------------------------------------------- | ---------------------------------------------------------------------------- |
| Access key (`AccessKey` header)       | API key (`dv_live_sk_xxxx`) sent as `X-API-Key`            | Single key, simpler auth                                                     |
| Flow Builder                          | Orbit Flows (visual builder + `POST /api/v1/flows`)        | The builder canvas is the same surface the API writes                        |
| WhatsApp channels                     | Orbit WhatsApp (your own WABA)                             | See the [WABA setup guide](/guides/whatsapp/waba-setup)                      |
| MNM lookups                           | Number Intelligence lookup (`POST /api/v1/numbers/lookup`) | Same mobile-number-network verdict, one call                                 |
| Verify                                | Orbit Verify                                               | OTP/2FA send and check                                                       |
| REST API / omni-channel conversations | Orbit channels matrix                                      | SMS, RCS, WhatsApp, voice, email from one surface; see [channels](/channels) |

***

## Prefer not to do it by hand?

The **Settings → Migrations** import wizard ports your MessageBird account's
*configuration* — channels, contacts, and Flow Builder flows — onto Orbit with
a read-only access key. MessageBird is one of the five supported migration
sources. It never touches your live traffic, so your existing MessageBird
integration keeps running until you cut over.

1. In the dashboard, open the wizard for `/import/messagebird` (also reachable from onboarding).
2. **Connect** — paste a MessageBird access key as the read-only credential. The key is handed forward to the job as an encrypted envelope; it is never persisted in the browser.
3. **Choose scope** — checkbox the entity kinds you want: `channels`, `contacts`, `flows`.
4. **Dry-run preview** — the wizard calls the preview endpoint and shows counts per entity, an ETA, and known conflicts before anything is written.
5. **Commit** — start the job and stream progress; check Status: `pending`, `running`, `succeeded`, `failed`, or `cancelled` from [Settings → Migrations](/guides/platform-migration-jobs).

The CLI wizard covers Twilio today; for MessageBird, use the dashboard wizard above or drive the same API endpoints over REST (`POST /imports/:source/run`). See [run a platform migration end-to-end](/guides/platform-migration-jobs) for the full job lifecycle (cancel, roll back, evidence).

The rest of this guide covers the manual, code-level migration for teams who want control over each step.

***

## Step 1: Create Your Orbit Account

1. Sign up at [orbit.devotel.io/signup](https://orbit.devotel.io/signup)
2. Generate an API key at **Settings → API Keys**
3. Note your key prefix: `dv_live_sk_xxxx`

Authenticate every request with the `X-API-Key` header — a single key replaces MessageBird's `AccessKey` pairing.

***

## Step 2: Port Your Numbers

Port your MessageBird numbers to Orbit. Number porting takes 7–14 business days.

**Via API:**

A port request must carry a Letter of Authorization (LoA). Orbit only accepts an LoA URL it issued itself, so upload the signed PDF to Orbit first and submit the returned URL — a link to your own bucket is rejected with `LOA_URL_INVALID_ORIGIN`.

```bash theme={null}
# 1. Upload the signed LoA PDF. The response `data.url` is a short-lived signed
#    URL (1-hour default). Pass `ttl_ms` to keep it valid long enough for the
#    carrier to fetch it during submission — here, 24 hours.
curl -X POST "https://api.orbit.devotel.io/api/v1/files/upload?ttl_ms=86400000" \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -F "file=@./loa.pdf;type=application/pdf"
# => { "data": { "id": "file_...", "url": "https://storage.googleapis.com/...", ... } }

# 2. Submit the port request, passing the uploaded `data.url` as loaFileUrl.
curl -X POST https://api.orbit.devotel.io/api/v1/numbers/porting \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "numbers": ["+14155551234", "+14155551235"],
    "currentCarrier": "MessageBird",
    "country": "US",
    "authorizedSigner": "Your Name",
    "accountNumber": "123456789",
    "accountPin": "1234",
    "loaFileUrl": "https://storage.googleapis.com/.../loa.pdf?X-Goog-Signature=..."
  }'
```

**Alternative:** Buy new numbers from Orbit and update your systems gradually.

For a walkthrough of the whole port-in flow, see [port numbers](/guides/port-numbers).

***

## Step 3: Update Message Sending

### MessageBird (Before)

```javascript theme={null}
const messagebird = require('messagebird')('your_access_key');

await messagebird.messages.create({
  originator: '+18005551234',
  recipients: ['+14155552671'],
  body: 'Hello from MessageBird!',
});
```

### Orbit (After)

```javascript theme={null}
import { Devotel } from '@devotel-orbit/node'

const orbit = new Devotel({ apiKey: 'dv_live_sk_xxxx' })

await orbit.messages.send({
  channel: 'sms',
  to: '+14155552671',
  body: 'Hello from Orbit!',
  webhook_url: 'https://yourapp.com/webhooks/sms-status',
})
```

### Key Differences

| Feature           | MessageBird                                                       | Orbit                                                                                                                     |
| ----------------- | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| Auth              | `AccessKey <key>` header                                          | Single API key in `X-API-Key`                                                                                             |
| Send endpoint     | `messagebird.messages.create({ originator, recipients[], body })` | `orbit.messages.send({ channel, to, body })`                                                                              |
| Channel selection | Implicit                                                          | Explicit `channel` field                                                                                                  |
| Sender            | Required `originator`                                             | Optional `from`, resolved via sender chain when named; see [sender resolution](/troubleshooting/sender-resolution-errors) |
| Status webhook    | Sticky per-message callback URL                                   | Optional `webhook_url` or global webhook on `message.*` events                                                            |

MessageBird's `recipients` array maps to Orbit's scalar `to` — one recipient per send. For a bulk batch, walk your recipient list and fan out per request, or move the workload to a campaign; see [campaigns](/campaigns).

***

## Step 4: Update Delivery-Report Webhooks

### Webhook Header Changes

| MessageBird        | Orbit                                    |
| ------------------ | ---------------------------------------- |
| Unsigned JSON body | `X-Orbit-Signature` (HMAC-SHA256) header |

Verify against `X-Orbit-Signature` — it is the canonical header Orbit sends on every webhook delivery. `X-Devotel-Signature` is still emitted for backward compatibility only; treat it as legacy and do not build new verifiers against it.

### Payload Format Changes

**MessageBird (delivery report JSON):**

```json theme={null}
{
  "id": "mb_msg_03692",
  "recipient": "+14155552671",
  "originator": "+18005551234",
  "status": "delivered"
}
```

**Orbit (signed JSON envelope):**

```json theme={null}
{
  "id": "evt_msg_001",
  "type": "message.delivered",
  "created_at": "2026-09-19T12:00:00Z",
  "data": {
    "message_id": "msg_abc123",
    "channel": "sms",
    "from": "+18005551234",
    "to": "+14155552671",
    "status": "delivered"
  }
}
```

### Update Signature Verification

```javascript theme={null}
// MessageBird (before) — validated nothing, consumed unsigned JSON
const delivery = JSON.parse(req.body);

// Orbit (after) — verify HMAC-SHA256 over the raw body first, then consume
import { verifyWebhookSignature } from '@devotel-orbit/node'
const valid = verifyWebhookSignature(rawBody, signature, 'whsec_your_secret')
```

***

## Step 5: Migrate Flow Builder to Orbit Flows

Orbit's Flows is a visual canvas backed by the same `{ nodes, edges }` graph the Flows API writes — the same shape your MessageBird Flow Builder exports carry. Export each flow from MessageBird, re-model it as an Orbit `definition`, and create it with `POST /api/v1/flows`.

### Trigger mapping

| MessageBird Flow Builder trigger | Orbit `trigger_type`                                                             |
| -------------------------------- | -------------------------------------------------------------------------------- |
| Incoming message                 | `event` — `definition.trigger.event` names the inbound event                     |
| Scheduled                        | `schedule` — with top-level `cron_expr` (and optional `cron_tz`)                 |
| Webhook POST                     | `webhook` — outsiders POST free-form JSON; each key lands as a flat run variable |
| Manual run / API kick-off        | `manual` — start with `POST /flows/:id/execute` or `POST /flows/:id/start`       |

Two rules to avoid surprises:

1. **Gate event triggers.** An inbound-message event also fires on API imports and CSV uploads, not just your live traffic. Put a Condition first, or a bulk import tags along behind your flow.
2. **Variables are flat.** Write `{{phone}}`, not `{{contact.phone}}`; a missing key renders as an empty string. Validate with Flow's Test mode before publishing.

### Recipe example — welcome SMS rebuilt

**Shape:** inbound message → welcome SMS → 24-hour delay → follow-up email.

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/flows \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Welcome (migrated from Flow Builder)",
    "trigger_type": "event",
    "definition": {
      "trigger": { "type": "event", "event": "contact.created" },
      "nodes": [
        {
          "id": "gate_source",
          "type": "condition",
          "data": { "field": "source", "operator": "equals", "value": "signup_form" }
        },
        {
          "id": "welcome_sms",
          "type": "sendSms",
          "data": {
            "to": "{{phone}}",
            "body": "Welcome to Acme, {{first_name}}! Reply HELP for help, STOP to opt out."
          }
        },
        { "id": "wait_24h", "type": "delay", "data": { "amount": 24, "unit": "hours" } },
        {
          "id": "followup_email",
          "type": "sendEmail",
          "data": {
            "to": "{{email}}",
            "subject": "Getting started with Acme",
            "body": "Hi {{first_name}}, here are three things worth doing first."
          }
        }
      ],
      "edges": [
        { "source": "gate_source", "sourceHandle": "yes", "target": "welcome_sms" },
        { "source": "welcome_sms", "target": "wait_24h" },
        { "source": "wait_24h", "target": "followup_email" }
      ]
    }
  }'
```

The graph is validated on save: no unreachable nodes, and no loops without an exit. Publish the draft so inbound messages trigger it. Use the visual [Flows builder canvas](/flows/overview) as the same surface; see [flows recipes](/guides/flows-recipes) for more definition shapes (reminders, triage, surveys, order updates) ready to drop in.

***

## Step 6: Migrate WhatsApp

MessageBird's WhatsApp "channels" fold their connectivity into the Bird tenant via a shared managed setup. On Orbit you connect your own WhatsApp Business Account (WABA) — the migration runs through Meta Business Manager verification, embedded sign-up, display-name and template approval, and quality/risk setup. The full walkthrough is the [WhatsApp WABA setup guide](/guides/whatsapp/waba-setup).

During the MessageBird → Orbit cutover, keep both channels live until templates and messaging limits carry over; routing happens at your integration layer.

***

## Step 7: Migrate Verify (OTP)

### MessageBird (Before)

```javascript theme={null}
// Start OTP delivery
const request = await messagebird.verify.create({
  recipient: '+14155552671',
  originator: 'Acme',
  template: 'Your code is %token',
});

// Check the code the user typed
const check = await messagebird.verify.verify(request.id, '123456');
```

### Orbit (After)

```javascript theme={null}
const sent = await orbit.verify.send('+14155552671', 'sms');

const result = await orbit.verify.check({
  verification_id: sent.data.verification_id,
  code: '123456',
});
```

See [verify in 30 minutes](/guides/verify-in-30-min) for the full OTP onboarding path.

***

## Migration Checklist

* [ ] Create Orbit account and generate API keys
* [ ] Run the Migrations wizard for `channels`, `contacts`, `flows` (or skip if manual)
* [ ] Port numbers or purchase new ones
* [ ] Install the Orbit SDK (`npm install @devotel-orbit/node`)
* [ ] Update message-sending code (originator/recipients/body → channel/to/body)
* [ ] Update webhook endpoint handlers
* [ ] Update webhook signature verification (unsigned JSON → HMAC-SHA256)
* [ ] Re-model Flow Builder exports as Orbit flow definitions
* [ ] Stand up the new WABA for WhatsApp (if applicable)
* [ ] Migrate Verify/OTP (if applicable)
* [ ] Update monitoring and alerting
* [ ] Run parallel testing (send via both MessageBird and Orbit)
* [ ] Decommission the MessageBird integration
* [ ] Release the old account's credentials from your secret store

***

## Parallel Running Strategy

Run MessageBird and Orbit in parallel during migration:

1. **Phase 1 (Week 1–2):** Send 10% of traffic through Orbit, 90% through MessageBird
2. **Phase 2 (Week 3–4):** Split 50/50 and compare delivery rates
3. **Phase 3 (Week 5):** Route 100% through Orbit, keep MessageBird as fallback
4. **Phase 4 (Week 6+):** Decommission MessageBird

<Tip>
  Need help? Our solutions team offers free migration support for customers moving from MessageBird. Contact [migrate@devotel.io](mailto:migrate@devotel.io).
</Tip>
