# Migrate SMS from Bird Connectivity Platform

This page maps the Bird Connectivity Platform API at `rest.messagebird.com`, the one you may still know as the MessageBird API, to Bird. Follow the [main migration guide](/docs/guides/sms/migrate) in order and use these mappings for steps 3, 4, and 5.

Both platforms are Bird's, and the API is the part that changes. Three differences affect every call. Requests go to your regional host, `https://us1.platform.bird.com` or `https://eu1.platform.bird.com`, rather than one global host. Authentication is a bearer API key (`Authorization: Bearer bk_us1_…`) rather than `Authorization: AccessKey`. And the send is asynchronous: [`POST /v1/sms/messages`](/docs/api/reference/create-sms-message) returns `202 Accepted` with the message queued, where the Connectivity Platform returned the message object with a per-recipient status already attached.

## Map the send call

| What it does     | Connectivity Platform   | Bird                                         |
| ---------------- | ----------------------- | -------------------------------------------- |
| Recipient        | `recipients` (up to 50) | `to`, one per request                        |
| Sender           | `originator`            | `from`                                       |
| Body             | `body`                  | `text`                                       |
| Intent           | (none)                  | `category`, required on free text            |
| Encoding         | `datacoding`            | detected automatically                       |
| Transliteration  | (none)                  | `options.smart_encoding` (default `false`)   |
| Client reference | `reference`             | `metadata`, or `tags` when you filter on it  |
| Status reports   | `reportUrl`             | a workspace webhook subscribed to `sms.*`    |
| Safe retries     | (none)                  | `Idempotency-Key` header                     |
| Scheduling       | `scheduledDatetime`     | no equivalent: `scheduled_at` is rejected    |
| Validity         | `validity`              | no equivalent: `validity_period` is rejected |
| Route selection  | `gateway`               | Bird selects the route                       |
| Message class    | `mclass`                | no equivalent                                |
| Binary and flash | `type`, `typeDetails`   | text only                                    |

Both rejected fields are [reserved](/docs/guides/sms/sending-sms#reserved-fields) and answer `422 SMSUnsupportedFeature`.

Porting notes:

- **The recipients array becomes one call per recipient.** A Connectivity Platform call with 50 recipients becomes 50 sends, or one [batch](/docs/guides/sms/sending-sms#batch-sending) of independent messages. The batch is not a fan-out of one body: each entry carries its own recipient, sender, and text.
- **`datacoding` has no equivalent, and that is deliberate.** Bird detects the encoding from the body and reports the segment count on the message. If you set `datacoding: auto` to keep messages inside GSM-7, the closest behavior is `options.smart_encoding`, which replaces characters outside the GSM-7 alphabet with their nearest equivalent before sending.
- **`reference` splits into two fields.** Put an internal identifier in `metadata`, which is echoed on every webhook event, and use `tags` for the low-cardinality labels you want to filter and slice analytics by.
- **Flash messages, binary payloads, and UDH concatenation do not port.** If you rely on `mclass` or `typeDetails` today, raise it with support before you plan the cutover rather than after.
- **Also on the Connectivity Platform Verify API?** The port is a separate job with its own guide: see [Migrate Verify from another provider](/docs/guides/verify/migrate).

## Carry over opt-outs

The Connectivity Platform left stop-keyword handling to you, whether you built it in Flows or in your own application against inbound messages. Bird does that job itself: it recognizes stop, start, and help keywords on your numbers in supported countries, records the suppression, and enforces it on every send. That code retires with the migration.

What does not retire is the list. Export whatever you keep today, as pairs of subscriber number and the originator they stopped, and import it through the [suppression loop](/docs/guides/sms/migrate#4-carry-over-your-opt-out-list) before your first production send. If you only ever kept a global list of subscribers who opted out, import each subscriber once per originator you still send from.

## Translate status reports

| Outcome                    | Connectivity Platform | Bird              |
| -------------------------- | --------------------- | ----------------- |
| Accepted by the API        | (synchronous)         | `sms.accepted`    |
| Handed to the carrier      | `sent`, `buffered`    | `sms.sent`        |
| Carrier confirmed delivery | `delivered`           | `sms.delivered`   |
| Delivery failed            | `delivery_failed`     | `sms.failed`      |
| Validity window elapsed    | `expired`             | `sms.expired`     |
| Refused before sending     | (request error)       | `sms.rejected`    |
| Waiting to be sent         | `scheduled`           | no equivalent yet |

The delivery mechanism changes more than the vocabulary:

- **Signed JSON posts replace `reportUrl` GET callbacks.** Status reports arrived as `GET` requests with the outcome in the query string (`status`, `statusReason`, `statusErrorCode`, `mccmnc`, `price[amount]`). Bird `POST`s a JSON event to endpoints your workspace registers, signed per [Standard Webhooks](https://www.standardwebhooks.com). The handler is a rewrite rather than a URL change.
- **Correlation no longer depends on `reference`.** A status report was only useful if you had set a reference; a Bird event always carries `sms_id`, both numbers, and your echoed `metadata` and `tags`.
- **Retry semantics differ.** The Connectivity Platform retried a failed report up to 10 times. Bird's deliveries are at-least-once and unordered, so deduplicate on the `webhook-id` header and sort by the payload `timestamp`.
- **Price is not on the event.** The Connectivity Platform report carried `price[amount]` and `price[currency]`. Read cost from the message itself with [`GET /v1/sms/messages/{id}`](/docs/api/reference/get-sms-message) or in aggregate from the [Stats API](/docs/guides/sms/stats-api).

Inbound messages work the same way: subscribe to `sms.received` once for the workspace instead of pointing each number at a URL.

## Cut over

[Destinations](/docs/guides/sms/migrate#1-enable-your-destination-countries), [senders](/docs/guides/sms/migrate#2-set-up-a-sender), and the [traffic ramp](/docs/guides/sms/migrate#6-test-against-simulated-destinations) are provider-independent and covered in the main guide. The item to raise early is your originators: alphanumeric sender IDs are re-created and, where the country requires it, re-registered here, and numbers you hold on the Connectivity Platform move by a port that support arranges rather than a setting you flip.

## Next steps

- [Sending SMS](/docs/guides/sms/sending-sms): the payload you are porting to, in full
- [Opt-outs and keywords](/docs/guides/sms/opt-outs-and-keywords): what Bird answers for you, and how to manage suppressions
- [SMS events](/docs/guides/sms/events): the event vocabulary your status handler moves to
- [Webhooks & events](/docs/guides/webhooks): endpoint setup and Standard Webhooks verification