Skip to main content
Refactoring a Twilio app to Vobiz is mostly a tab-swap: same call flow, a few renamed methods, and an explicit auth_id. Each task below shows the Twilio code and the equivalent Vobiz code in Python and Node — copy the Vobiz tab.
Set your credentials once: AUTH_ID is your Vobiz Auth ID (MA_…), AUTH_TOKEN is your Auth Token. In the Vobiz SDK, api_key is the Auth ID, and every account-scoped method takes that auth_id explicitly.

Set up the client

from twilio.rest import Client

client = Client(ACCOUNT_SID, AUTH_TOKEN)

Make an outbound call

The classic “change a few lines” migration: calls.createcalls.make_call, add auth_id, and url/methodanswer_url/answer_method.
call = client.calls.create(
    to='+919876543210',
    from_='+14155551234',
    url='https://example.com/answer.xml',
    method='POST',
)
print(call.sid)

Answer a call with XML (TTS + menu)

The XML builder maps verb-for-verb: VoiceResponse()ResponseElement(), and Gather keeps its name. On the builder, response.gather(...)resp.add_gather(...), nested .say().add_speak(), and str(response)resp.to_string(). Twilio’s input/timeout become Vobiz’s input_type/execution_timeout; num_digits carries over unchanged.
from twilio.twiml.voice_response import VoiceResponse, Gather

resp = VoiceResponse()
menu = resp.gather(input='dtmf', num_digits=1, timeout=10,
                   action='https://example.com/menu', method='POST')
menu.say('Press 1 for sales, 2 for support.')
print(str(resp))
Vobiz emits the same document shape your TwiML app already returns — see the full verb table in TwiML → VobizXML, and validate the incoming request on your answer URL as shown in Webhooks.

Serve the answer URL (Flask / FastAPI / Express)

When Vobiz rings your number it fetches your answer_url over HTTP — return the VobizXML from any web framework, exactly as your Twilio voice URL returned TwiML. Just build the response with vobizxml instead of VoiceResponse, and send it as application/xml.
from flask import Flask, Response
from twilio.twiml.voice_response import VoiceResponse

app = Flask(__name__)

@app.route('/answer', methods=['POST'])
def answer():
    resp = VoiceResponse()
    resp.say('Hello from Twilio.')
    return Response(str(resp), mimetype='application/xml')
Building the XML is the only thing that changes — swap VoiceResponse()/str(resp) for vobizxml.ResponseElement()/resp.to_string() and keep the same route. The full verb map is in TwiML → VobizXML, and validating the signed request on this endpoint is covered in Webhooks.

Control a live call

This is the biggest shape change — and the one that makes Vobiz code clearer. Twilio funnels every mid-call action back through client.calls(sid).update(...) (redirecting to fresh TwiML or setting status) and starts recording with client.calls(sid).recordings.create(). Vobiz gives each action its own resource keyed by (auth_id, call_uuid), so the intent lives on the method name.
ActionTwilioVobiz
Play audioclient.calls(sid).update(twiml="<Play>…")client.play_audio.call(auth_id, call_uuid, urls=)
Speak textclient.calls(sid).update(twiml="<Say>…")client.speak_text.call(auth_id, call_uuid, text=)
Send DTMFclient.calls(sid).update(twiml="<Play digits=…>")client.dtmf.send_dtmf(auth_id, call_uuid, digits=)
Start recordingclient.calls(sid).recordings.create()client.record_calls.start_recording(auth_id, call_uuid)
Hang upclient.calls(sid).update(status="completed")client.live_calls.hangup_call(auth_id, call_uuid)
# Every mid-call change re-enters client.calls(sid).update(...)
client.calls(sid).update(                                            # play audio
    twiml='<Response><Play>https://example.com/hold.mp3</Play></Response>')
client.calls(sid).update(                                            # speak text
    twiml='<Response><Say>Please hold.</Say></Response>')
client.calls(sid).update(                                            # send DTMF
    twiml='<Response><Play digits="1234"></Play></Response>')
client.calls(sid).recordings.create()                                # start recording
client.calls(sid).update(status='completed')                         # hang up
Each Vobiz method name states the action, so live-call logic reads top-to-bottom. For the full resource map, see Voice Call API; to convert the TwiML you inline via update(twiml=…), use the TwiML → VobizXML reference.

Look up a live call

Twilio reads live and completed calls off one client.calls resource, filtered by status. Vobiz gives in-flight calls their own live_calls resource — pass status="live" to fetch or list what’s currently on the wire. Completed-call history lives in cdr.
one = client.calls('CA0123...').fetch()
live = client.calls.list(status='in-progress')

Search and buy a phone number

Twilio searches the live carrier catalog with available_phone_numbers('US').local.list(...), reads .phone_number off a result, then provisions it with incoming_phone_numbers.create(phone_number=...). Vobiz browses ready-to-buy stock with list_inventory_numbers(...) and buys by E.164 with purchase_from_inventory(...) — a deterministic two-step flow where the E.164 number is the key.
available = client.available_phone_numbers('US').local.list(limit=1)
number = available[0].phone_number   # e.g. '+14155551234'
incoming = client.incoming_phone_numbers.create(phone_number=number)

Manage a conference

Twilio tracks a room by its CF… SID and each leg by its CallSid. Vobiz uses the room name as the join key and a member_id per participant — get_conference returns the room details and its member list in one call, and every control is its own verb.
conf = client.conferences("CFxxxxxxxx")

# Who is in the room?
for p in conf.participants.list():
    print(p.call_sid, "muted:", p.muted)

# Mute, hold, and remove one participant
conf.participants("CAaaaa").update(muted=True)
conf.participants("CAaaaa").update(hold=True, hold_url="https://example.com/hold.mp3")
conf.participants("CAaaaa").delete()

# End the whole conference
conf.update(status="completed")
Record the whole room on demand by name — start and stop whenever you like:
# Twilio sets recording on the <Conference> verb:
#   <Conference record="record-from-start"
#               recordingStatusCallback="https://example.com/rec">SalesRoom</Conference>
The full per-verb map — mute/unmute, deaf/undeaf, kick/hangup, and the <Conference> element — is in Conferences.

List call records

Completed-call history is a first-class cdr resource on Vobiz, with rich filters for reporting.
records = client.calls.list(status="completed", limit=20)
for r in records:
    print(r.sid, r.to, r.duration, r.price)
Filter CDRs by from_number, to_number, call_direction, hangup_cause, bridge_uuid, or sip_call_id — purpose-built for reporting. See Voice Call API for the live-vs-history split (live_calls for in-flight legs, cdr for completed history).

Validate a webhook signature

Both platforms sign inbound webhooks so you can prove the request came from them. Twilio’s RequestValidator rebuilds the full URL plus every sorted POST field and HMAC-SHA1s it against X-Twilio-Signature. Vobiz signs a short, deterministic string — baseURL + "." + nonce (query stripped) — with HMAC-SHA256, and sends it as X-Vobiz-Signature-V3 with the random nonce in X-Vobiz-Signature-V3-Nonce. No param-sorting step, reproducible in any language with the standard library.
from flask import Flask, request, abort
from twilio.request_validator import RequestValidator

app = Flask(__name__)
AUTH_TOKEN = "your_twilio_auth_token"

@app.route("/voice", methods=["POST"])
def voice():
    validator = RequestValidator(AUTH_TOKEN)
    # HMAC-SHA1 over full URL + sorted POST params
    valid = validator.validate(
        request.url,
        request.form,
        request.headers.get("X-Twilio-Signature", ""),
    )
    if not valid:
        abort(403)
    # ... return TwiML
On sub-account callbacks Vobiz also adds X-Vobiz-Signature-MA-V3, signed with the parent (main-account) token, so a parent can verify child traffic with the same validator. Full mapping and the Node version: webhooks & signatures.

Handle errors

Catch the Vobiz error types (all subclasses of ApiError) the same way you caught Twilio’s TwilioRestException — with the HTTP status and response body available on the exception.
from twilio.base.exceptions import TwilioRestException

try:
    client.calls.create(
        to="+14165553434",
        from_="+14155551234",
        url="https://example.com/answer.xml",
        method="POST",
    )
except TwilioRestException as e:
    print(e.status, e.code, e.msg)
Full verb table: TwiML → VobizXML.
Need a method that isn’t here? The full per-resource map is in Voice Call API, TwiML → VobizXML, and the rest of the Twilio migration section.