Migration from Twilio
Are you thinking of switching from Twilio to Telnyx? This document describes some key differences between the platforms as well as implementation details for standard SDK features to make your experience as smooth as possible.
How Twilio’s Voice SDK works
With Twilio, a lot of setup is required before you can get started making calls. because users are required to have their own backend in place, with Telnyx this is not needed.
TwiML is similar to Call Control that Telnyx provides - but it isn’t a requirement when using our Voice SDK.
In short, Twilio’s Voice SDK flow is as follows:
- Your browser / mobile device connects to Twilio
- Twilio connects to your pre-deployed server node application which can generate a token and receive voice webhooks
- Twilio sends you a webhook to get TwiML instructions
- Your backend server node responds with a set of TwiML instructions that you have defined for certain use cases (eg. call a number, connect to a conference)
- Twilio receives your TwiML instructions and executes them on your behalf. (eg. Dial a number contained in your TwiML instructions)
- Twilio creates a VoIP connection between your callee and your application.
Flow looks like this:
Caller > Twillio Voice SDK > Twillio Servers > customer backend (Webhook receiver) > Twillio Servers > Callee
It's much easier with the Telnyx Voice SDK
Telnyx’s Voice SDK has been designed to be as simple as possible to make/receive calls in a matter of minutes. Unlike Twilio, there is no backend setup required.
Simply, implement the Voice SDK library on the platform of your choice, and log in with your Telnyx Connection and you’re all set.
To summarize:
- Your browser / mobile device connects to Telnyx
- You send an invitation from the client
- If accepted, Telnyx creates a VoIP connection between your callee and your application.
Flow looks like this:
Caller > Telnyx WebRTC SDK > Telnyx servers > Callee
Optionally, if you would like to control the call, like you do with TwiML, you can use Call Control.
Pricing
Below is a comparison table for the United States region. (Note regional prices may vary).
SERVICE | TELNYX ORIGINATION | TWILIO ORIGINATION | TELNYX TERMINATION | TWILIO TERMINATION |
Local Calls | 0.0070/ min | 0.0140/ min | 0.0055/ min | 0.0085/ min |
Toll-Free Call | 0.0020/ min | 0.0140/ min | 0.0170/ min | 0.0220/ min |
Browser / App Calling | 0.0020/ min | 0.0040/ min | 0.0020/ min | 0.0040/ min |
SIP Interface | 0.0020/ min | 0.0040/ min | 0.0020/ min | 0.0040/ min |
Secure Media | included | included | included | included |
Sources: https://telnyx.com/pricing/call-control https://www.twilio.com/en-us/voice/pricing/us
Web
Comparative Table (Web SDK)
Telnyx RTC | Twilio Voice SDK | |
Portal and server initial configuration | No server setup is required. Create SIP connections Buy a phone number and assign it to a SIP connection. | Create a TwiML App and find the TwiML App SID and configure a webhook endpoint to make outbound calls Buy a phone number to make outbound and receive inbound calls and configure a webhook to be able to receive incoming calls. Create an API KEY to be able to generate an Access Token Run a server node application to generate token and to receive the voice webhook to exec dial command. |
SDK Installation | npm i @telnyx/webrtc | npm i twillio (backend) npm i @twilio/voice-sdk (front-end) |
To make calls | The SDK is connected using a SIP username or JWT Token
It calls directly between the browsers with the command call.newCall({...}) | The SDK is connected using a Token. When connecting it will return a Call object that will send a POST in Twillio Sever to /voice endpoint and in the local backend API it will exec the dial command |
Connect
Telnyx
// Initialize the client
const client = new TelnyxRTC({
/* Use a JWT to authenticate (recommended) */
login_token: login_token,
/* or use your Connection credentials */
// login: username,
// password: password,
});
// Connect and login
client.connect();
NoteAfter pasting the above content, Kindly check and remove any new line added
Twilio
import twilio from 'twilio';
// Download the helper library from https://www.twilio.com/docs/node/install
// Find your Account SID and Auth Token at twilio.com/console
// and set the environment variables. See http://twil.io/secure
const accountSid = process.env.TWILIO_ACCOUNT_SID;
const authToken = process.env.TWILIO_AUTH_TOKEN;
const client = twilio(accountSid, authToken);
NoteAfter pasting the above content, Kindly check and remove any new line added
Make a Call
Telnyx
const call = client.newCall({
// Destination is required and can be a phone number or SIP URI
destinationNumber: '18004377950',
callerNumber: '155531234567',
});
NoteAfter pasting the above content, Kindly check and remove any new line added
Twilio
client.calls
.create({
url: 'https://example.com',
to: '+15558675310',
from: '+15017122661'
})
.then(call => console.log(call.sid));
NoteAfter pasting the above content, Kindly check and remove any new line added
Answer an incoming Call
Note: in Twilio’s case, a backend server needs to be setup. We have included a Python flask example as well as the client implementation
Telnyx
client.on('telnyx.notification', (notification) => {
const call = notification.call;
if (notification.type === 'callUpdate' && call.state === 'ringing') {
call.answer();
}
});
NoteAfter pasting the above content, Kindly check and remove any new line added
Twilio
// Backend Server, in this case Python with flask
@app.route('/handle_calls', methods=['POST'])
def call():
p.pprint(request.form)
response = VoiceResponse()
dial = Dial(callerId=twilio_number)
if 'To' in request.form and request.form['To'] != twilio_number:
print('outbound call')
dial.number(request.form['To'])
else:
print('incoming call')
caller = request.form['Caller']
dial = Dial(callerId=caller)
dial.client(twilio_number)
return str(response.append(dial))
NoteAfter pasting the above content, Kindly check and remove any new line added
// Client
device.on("incoming", function (conn) {
conn.accept();
});
NoteAfter pasting the above content, Kindly check and remove any new line added
Android
Comparative Table (Android SDK)
Telnyx RTC | Twilio Voice SDK | |
Portal and server initial configuration | No server setup is required. Create SIP connections Buy a phone number and assign it to a SIP connection. | Deployment of a TwiML. This will generate an Application SID. Create a token using the Application SID. Buy a phone number to make PSTN calls |
SDK Installation | JitPack | Maven Central |
Android Minimum API Level | Android 23 (6) and higher | Android 16 (4.1) and higher |
Java Compatibility | sourceCompatibility 1.8 targetCompatibility 1.8 | sourceCompatibility 1.8 targetCompatibility 1.8 |
Language | Kotlin SDK Kotlin Sample app Compose Android Sample App | Java SDK Java Sample app |
To make calls | The client is connected while the app is opened, creating an instance of TelnyxClient. With this instance of TelnyxClient you can create and receive multiple calls | The SDK is connected using a Token. When connecting it will return a Call object The SDK creates a call object once you have connected. Meaning you authenticate per call. (Or at least include a valid access token per call, tokens live for an hour) |
Receiving calls | Incoming calls when logged in are handled as socket messages which the SDK is listening for. When receiving an invite socket message, a Call object is made which can be answered or declined. Incoming calls when logged out are handled via FCM. | All incoming calls are delivered via FCM |
Push notifications setup (CLI) | Create a firebase server key Create push credential in portal with firebase server key Attach push credential to a SIP connection. | Create a firebase server key Use Twillio CLI to create Push Credential Twilio CLI returns a Push Credential ID You can now include Credential ID with access token request via a Voice Grant. |
Push notification setup on the client application | Include firebase in your project with google-services.json that includes server key Generate a FCM token at launch via standard getInstance() method. Include FCM token with either credntialLogin or tokenLogin method. The device can now receive notifications when being called | Include firebase in your project with google-services.json that includes server key This access token mentioned above is then used with a Voice.register() method which will register your mobile application with the FCM device token as well as the access token. The device can now receive notifications when being called |
Connect
Note: in Twilio’s case, for their mobile SDKs, they connect and authenticate per call rather than one initial connection
Telnyx
val telnyxClient = TelnyxClient(context)
telnyxClient.connect()
telnyxClient.credentialLogin(credentialConfig)
NoteAfter pasting the above content, Kindly check and remove any new line added
Twilio
val contact = (dialog as AlertDialog).findViewById<EditText>(R.id.contact)
params.put("to", contact.text.toString())
val connectOptions: ConnectOptions = Builder(accessToken)
.params(params)
.build()
activeCall = Voice.connect(this@VoiceActivity, connectOptions, callListener)
NoteAfter pasting the above content, Kindly check and remove any new line added
Make a Call
Telnyx
telnyxClient.call.newInvite(callerName, callerNumber, destinationNumber, clientState)
NoteAfter pasting the above content, Kindly check and remove any new line added
Twilio
val contact = (dialog as AlertDialog).findViewById<EditText>(R.id.contact)
params.put("to", contact.text.toString())
val connectOptions: ConnectOptions = Builder(accessToken)
.params(params)
.build()
activeCall = Voice.connect(this@VoiceActivity, connectOptions, callListener)
NoteAfter pasting the above content, Kindly check and remove any new line added
Answer an incoming Call
Telnyx
mainViewModel.getSocketResponse()
?.observe(this, object : SocketObserver<ReceivedMessageBody>() {
SocketMethod.INVITE.methodName -> {
val inviteResponse = data.result as InviteResponse
telnyxClient.call.acceptCall(inviteResponse.callId, inviteResponse.callerIdNumber)
}
})
NoteAfter pasting the above content, Kindly check and remove any new line added
Twilio
// Receive Intent from notification:
private fun handleIncomingCallIntent(intent: Intent?) {
if (intent != null && intent.action != null) {
val action = intent.action
activeCallInvite = intent.getParcelableExtra<Parcelable>(Constants.INCOMING_CALL_INVITE)
activeCallNotificationId = intent.getIntExtra(Constants.INCOMING_CALL_NOTIFICATION_ID, 0)
when (action) {
Constants.ACTION_ACCEPT -> answer()
else -> {}
}
}
}
// answer the call
private fun answer() {
activeCallInvite.accept(this, callListener)
}
NoteAfter pasting the above content, Kindly check and remove any new line added
iOS
Comparative Table (iOS SDK)
Telnyx RTC | Twilio Voice SDK | |
Portal and server initial configuration | No server setup is required. Create SIP connections Buy a phone number and assign it to a SIP connection. | Deployment of a TwiML. This will generate an Application SID. Create a token using the Application SID. Buy a phone number to make PSTN calls |
SDK Installation | Cocoapods | SPM: Recommended. Cocoapods Carthage Framework |
To make calls | The client is connected while the app is opened. While connected you can make calls. | The SDK is connected using a Token. When connecting it will return a Call object |
Push notifications setup (Portal) | Create an APNS certificate Upload the APNS certificate Assign the APNS certificate to a SIP connection. | Create an APNS certificate Upload the APNS certificate: This will create a CR_ID. Assign the CR_ID to the APPLICATION_SID |
Push notification setup on the client APP | To register a device for PN: We need to wait until APNS assign a new token and then connect to the client to send the PN parameters over the login message. Unregister a device from the PN is not supported The last registered device for a SIP connection is the one that will receive the push notification. | Register a device for PN: Requires a deviceToken and the PushToken. The device is a relation between the TwiML app, the user and the device. You can unregister to stop getting PN The same user can be registered on multiple devices |
Connect
Note: in Twilio’s case, for their mobile SDKs, they connect and authenticate per call rather than one initial connection
Telnyx
let telnyxClient = TxClient()
do {
try telnyxClient.connect(txConfig: txConfigToken)
} catch let error {
print("ViewController:: connect Error \(error)")
}
NoteAfter pasting the above content, Kindly check and remove any new line added
Twilio
let connectOptions = ConnectOptions(accessToken: accessToken) { builder in
builder.params = [twimlParamTo: self.outgoingValue.text ?? ""]
builder.uuid = uuid
}
let call = TwilioVoiceSDK.connect(options: connectOptions, delegate: self)
NoteAfter pasting the above content, Kindly check and remove any new line added
Make a Call
Telnyx
self.currentCall = try self.telnyxClient?.newCall(callerName: "Caller name", callerNumber: "155531234567",
// Destination is required and can be a phone number or SIP URI
destinationNumber: "18004377950",
callId: UUID.init())
NoteAfter pasting the above content, Kindly check and remove any new line added
Twilio
let connectOptions = ConnectOptions(accessToken: accessToken) { builder in
builder.params = [twimlParamTo: self.outgoingValue.text ?? ""]
builder.uuid = uuid
}
let call = TwilioVoiceSDK.connect(options: connectOptions, delegate: self)
NoteAfter pasting the above content, Kindly check and remove any new line added
Answer an incoming Call
Telnyx
extension ViewController: TxClientDelegate {
//....
func onIncomingCall(call: Call) {
// We are automatically answering any incoming call as an example, but
// maybe you want to store a reference of the call, and answer the call after a button press.
self.myCall = call.answer()
}
}
NoteAfter pasting the above content, Kindly check and remove any new line added
Twilio
// Listen for telephony notification (after prior setup)
extension ViewController: CXProviderDelegate {
func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
NSLog("provider:performAnswerCallAction:")
performAnswerVoiceCall(uuid: action.callUUID) { success in
if success {
NSLog("performAnswerVoiceCall() successful")
} else {
NSLog("performAnswerVoiceCall() failed")
}
}
action.fulfill()
}
}
// answer the call
func performAnswerVoiceCall(uuid: UUID, completionHandler: @escaping (Bool) -> Void) {
guard let callInvite = activeCallInvites[uuid.uuidString] else {
NSLog("No CallInvite matches the UUID")
return
}
let acceptOptions = AcceptOptions(callInvite: callInvite) { builder in
builder.uuid = callInvite.uuid
}
let call = callInvite.accept(options: acceptOptions, delegate: self)
}
NoteAfter pasting the above content, Kindly check and remove any new line added