Migrate SMS from Twilio
This page maps Twilio's Programmable Messaging API, Messaging Services, and status callbacks to Bird. Follow the main migration guide in order and use these mappings for steps 3, 4, and 5.
Two differences shape the whole port. Twilio's POST /2010-04-01/Accounts/{AccountSid}/Messages.json takes form-encoded PascalCase parameters authenticated with your Account SID and Auth Token; POST /v1/sms/messages takes JSON authenticated with a bearer API key against your regional host. And a Twilio Messaging Service can bundle sender selection, opt-out handling and callback configuration. Map each behavior separately to the Bird owner; renaming its SID to a sender value does not preserve the whole service.
Hand this to your agent
Use this brief in your coding agent. It starts with discovery and produces a reviewable migration plan before any production change.
Code example
Help me migrate my SMS integration from Twilio to Bird.
1. Inspect this repository's sends, senders, callbacks, schedules, templates, opt-outs and tests. List the traffic and behavior that must survive the migration.
2. Read https://bird.com/docs/guides/sms/migrate/twilio.md and https://bird.com/docs/guides/sms/migrate.md. Use an existing authenticated Bird MCP or CLI connection. If neither is available, follow https://bird.com/docs/ai/set-up-your-agent.md. Discover the actual operations; do not invent commands or ask me to paste credentials into chat.
3. Prepare the code changes, sender/destination requirements, consent migration, webhook verification and rollout/rollback plan. Preserve the scope of each customer's preferences, including requests outside SMS replies. Separate API batches from audience broadcasts and preserve any behavior that has no direct endpoint equivalent.
4. Show me the exact affected resources, destinations, test volume and known costs before an action that sends messages, spends money, registers or changes a sender, or moves production traffic. Require explicit human authorization for each paid submission or production change. Name one-off 10DLC registration and resubmission fees before requesting approval. An existing explicit approval for that exact action is sufficient; broad migration approval is not. Simulated SMS destinations are billable and still require authorization.
5. If I am keeping Twilio numbers, prepare the human support port request and obtain authorization to send it. Read bird support-tickets create --help, then use the available CLI or MCP support operation with the reviewed number list and requirements. Return the ticket ID and follow the reply; support arranges the port on its own schedule, separately from the code cutover.
6. Run local and intercepted tests first. When authorized, perform the agreed bounded integration tests, inspect accepted and final outcomes separately, and report failures or uncertainty. Do not claim a delivery receipt proves reading or that request idempotency guarantees exactly-once delivery.
7. Keep production cutover and retiring the old provider as explicit steps in the approved rollout. Finish with the diff, evidence, unresolved requirements and the next action.Map the send call
| What it does | Twilio | Bird |
|---|---|---|
| Recipient | To | to (one per request) |
| Sender | From or MessagingServiceSid | from |
| Body | Body | text |
| Content template | ContentSid + ContentVariables | review content separately; Bird system templates are not an import of Twilio Content |
| Intent | (none) | category, required on free text |
| Filterable labels | (none) | tags: {name, value} pairs |
| Round-trip context | your own store, keyed by SID | metadata: arbitrary JSON, echoed on every event |
| Delivery reports | StatusCallback | a workspace webhook subscribed to the delivery events below |
| Transliteration | SmartEncoded | options.smart_encoding (default false) |
| Safe retries | (none on Messages) | Idempotency-Key header |
| Scheduling | ScheduleType + SendAt | no equivalent: scheduled_at is rejected |
| Media | MediaUrl | no equivalent: media_urls is rejected |
| Validity | ValidityPeriod | no equivalent: validity_period is rejected |
| Link shortening | ShortenUrls | no equivalent |
The three rejected fields are reserved and answer 422 SMSUnsupportedFeature. Keep scheduling and media handling where they are for now.
Porting notes:
- Resolve the Messaging Service behaviors separately. Twilio resolves the sender pool, sticky sender, and geomatch behind the SID. Bird takes the sender itself in from, so pick the sender per send, or use a template send, which selects a valid sender for the destination and rejects from.
- A character limit becomes a segment cap. The lengths work out close for GSM-7 text, but the failure does not: Bird never truncates, so an over-length body is rejected with a 422 instead of trimmed.
- Nothing on the Messages API corresponds to category. Decide per message type whether it is transactional, marketing, authentication, or service. Authentication traffic in particular should be labelled as such rather than left in a marketing default.
- Twilio's test credentials map onto simulated destinations. The magic numbers you already test with, including +15005550006 and +15005550001, produce synthesized outcomes here too, with two differences: there is no separate test credential, and the sends are billed. The outcomes are listed in the main guide.
Carry over opt-outs
Twilio can scope an opt-out to a number or a Messaging Service. A service-wide request can cover multiple senders. Preserve that breadth when importing into Bird's sender-and-subscriber suppressions, or use the appropriate workspace preference for a genuinely workspace-wide request.
Twilio's Advanced Opt-Out documentation says blocked-number reporting is not exposed through its Console or REST API. Request an export through the available support process and reconcile it with your own preference records, inbound logs and support requests. A keyword log alone may be incomplete.
Import the reviewed result through the suppression workflow. A manual suppression blocks every category for that pair, so check the intended scope instead of silently narrowing or broadening it.
Twilio's 21610 signals an opted-out recipient. In Bird, a suppressed pair is refused at admission with E12077 SMSRecipientSuppressed, before a message exists. The delivery error recipient_opted_out instead reports a downstream opt-out. Check Bird's keyword coverage before retiring any existing handler, and preserve opt-out mechanisms outside the built-in catalogue.
Translate delivery statuses
Use this table to compare lifecycle concepts, not to rename events mechanically. Bird chooses a failure event from the reported status and reason. A refused API request creates no message; a rejection after acceptance can produce sms.rejected, including a carrier rejection. Missing delivery evidence remains unknown. Preserve the raw provider status and code alongside your normalized outcome.
| Outcome | Twilio MessageStatus | Bird |
|---|---|---|
| API accepted the message | queued, accepted | sms.accepted |
| Handed to the carrier | sending, sent | sms.sent |
| Carrier confirmed delivery | delivered | sms.delivered |
| Carrier reported non-delivery | undelivered | sms.undelivered |
| Permanent failure | failed | sms.failed |
| Request refused at admission | request error | HTTP error; no message or event |
| Validity window elapsed | (none) | sms.expired |
| Scheduled or cancelled | scheduled, canceled | no equivalent yet |
Three mechanics change with the names:
- Endpoints replace callback URLs. Twilio posts to the StatusCallback on the message or the Messaging Service. Bird delivers to endpoints your workspace registers, each subscribed to the event types it wants, so a new consumer is a new subscription rather than a redeploy.
- Signed JSON replaces form-encoded posts. Twilio sends application/x-www-form-urlencoded with an X-Twilio-Signature header; Bird sends JSON signed per Standard Webhooks. Swap the verification for the recipe in Webhooks & events.
- Inbound messages arrive as events. Twilio's per-number "A message comes in" webhook expects a TwiML response your app can use to auto-reply. Bird emits sms.received to the same subscribed endpoint as everything else, and there is no response body that sends a reply: answer by calling the send endpoint, or let keyword rules answer for you.
Register the endpoint once, naming the event types your handler wants: the sms.* events in the table above are the list to subscribe to, and there is no wildcard that stands in for them. Create an endpoint has the command, why the catalog has to be enumerated, and the one thing to get right on the first call, which is storing the signing secret the response shows exactly once.
Twilio's numeric error codes have no one-to-one map. Bird reports a failure with a standardized error code such as invalid_destination, content_rejected, provider_unavailable, or recipient_opted_out; the full list is on the events page. Map your alerting to those rather than to the 30000-range codes.
Cut over
Destinations, senders, and the traffic ramp are provider-independent and covered in the main guide. Two Twilio-specific items belong on the cutover plan: your 10DLC brand and campaign are registered with The Campaign Registry through Twilio and do not automatically become Bird registrations. Confirm the applicable migration or registration procedure before submitting paid work. Numbers you own at Twilio need a port that support arranges, on its own schedule rather than yours.
For the Bird-side requirements, start from Register for 10DLC: it covers what each field means, the entity types the registry recognizes, and the requirements call that tells you what to supply before you create the brand, which is the chargeable step.
Next steps
-
Compare Bird and Twilio for SMS: product evaluation and migration considerations
-
Sending SMS: the payload you are porting to, in full
-
Opt-outs and keywords: keyword coverage per country and suppression management
-
SMS events: the event vocabulary your status handler moves to
-
Webhooks & events: endpoint setup and Standard Webhooks verification
Related resources
Continue with the documentation, guides and examples for this topic.