Skip to main content

Migration Toolkit

The Migration Toolkit is a set of developer tools that cut the effort of migrating a voice application from Twilio to Bandwidth. It works in three steps:

  1. Compatibility Check — scans your existing codebase and reports how your TwiML and Twilio SDK usage map to Bandwidth's BXML. Size the migration effort and identify blockers without writing a line of code.
  2. Real-Time Translator — completes a real Bandwidth call on your unchanged Twilio app by translating between TwiML and BXML live, so you can hear how well your app runs on Bandwidth today. It speaks Twilio's 2010-04-01 REST shape, TwiML, and webhook payloads on the front, and the Bandwidth Voice API on the back — an existing app runs on Bandwidth's network by changing one URL, with no rewrite. Anything it can't translate fails loudly, audibly and in the logs, rather than breaking a live call silently.
  3. BXML Generator — takes the raw TwiML from your call flow and generates the usable BXML you'll ship, cutting the hand-conversion work.

The rest of this page focuses on the Real-Time Translator, the core of the toolkit.

info

The everyday call-flow building blocks are proven end-to-end on real Twilio and Bandwidth calls. The status labels throughout these pages tell you exactly what's verified live, what's covered by automated tests, and what's still experimental, so you always know what you can rely on.

How it works

A phone call on either platform is driven by a small script the network reads step by step — "say a greeting → wait for a key press → if they press 1, transfer to sales." Twilio writes that script in TwiML; Bandwidth uses BXML for the same ideas. Roughly 80% maps one-to-one; the translator handles the rest.

The network doesn't get the whole script at once. Instead, it asks the app "what now?" one step at a time. The translator sits in the middle of that conversation:

  1. A call hits a Bandwidth number → Bandwidth asks the translator, in BXML, "what now?"
  2. The translator asks your unmodified Twilio app the same question, signing the request the way Twilio would, so your app trusts it.
  3. Your app replies with TwiML → the translator translates it to BXML on the spot and hands it back.
  4. Caller input (DTMF or speech) loops back through the same path for the life of the call.

The translation is deterministic and driven by a single compatibility matrix, not an AI guessing. When your app uses something the translator can't do yet, it fails loudly with a clear error rather than mistranslating a live call silently.

What you can do

TwiML verbs

VerbSupportNotes
Say, Play, Pause, Hangup, RedirectSSML touches supported on Say
GatherDTMF + speech; speech input needs enablement on your Bandwidth account
RecordactionrecordCompleteUrl; recordingStatusCallbackrecordingAvailableUrl
DialNumber, SipcallerId, timeout, action mapped
DialConference⚠️Mute + event callbacks; no hold music or start/end lifecycle
Connect/Start/Stop (Stream, Transcription)Media Streams bridge emulates Twilio's WebSocket protocol
Reject, Refer⚠️Reject → Hangup (with warning); Refer is inbound-SIP only
Queue, Client, Enqueue, Leave, PayNo Bandwidth equivalent — the translator fails loudly

REST API facade

Backend calls your Twilio SDK makes are served in Twilio's shape under /2010-04-01/Accounts/{accountSid} and translated to Bandwidth.

Twilio SDK callTranslator endpointStatus
calls.create()POST /Calls.json
calls(sid).update()POST /Calls/{sid}.json✅ hang up / redirect
calls(sid).fetch()GET /Calls/{sid}.json
calls(sid).recordings.list()GET /Calls/{sid}/Recordings.json
recordings(sid).fetch() / mediaGET /Recordings/{sid}.json / .mp3 / .wav
recordings(sid).update({status})POST /Calls/{sid}/Recordings/{recSid}.json✅ pause/resume (stopped unsupported)

Errors mirror Twilio: unknown resources return 20404, bad credentials 20003, missing params 21201/21205/21213.

The full machine-readable surface is published as an OpenAPI spec at openapi/twilio-compat-adapter.yml in the translator repo. It intentionally mirrors Twilio's API paths, JSON shapes, and error codes, so it diverges from Bandwidth API Standards v3 by design — byte-level Twilio compatibility is the whole point.

What you can't do yet

  • Call queues ("you're caller number 3, please hold") — Bandwidth has no queue primitive to translate to. The most significant current limitation.
  • Conference hold music — no Bandwidth equivalent.
  • Stopping a recording over REST — Bandwidth pauses and resumes over REST but has no REST stop (StopRecording is a BXML verb), so Status=stopped fails loudly.
  • Number provisioning — the translator does not provision numbers. Ordering, activation, and release are handled by the Bandwidth band CLI (https://dev.bandwidth.com/tools/cli/).

In every case, the translator surfaces a clear error rather than silently mistranslating. You can trust us to let you know where the gaps are.

What you need to get started

  • A Bandwidth App account with Voice enabled
  • OAuth2 client credentials (client ID and client secret) from The Bandwidth App, plus a Bandwidth Voice application ID
  • An existing Twilio voice application you want to migrate
  • Node.js 20+ to run the translator

Once you have credentials, head to the quickstart guide to run the translator and point your app at it.