Rate this page:

Voice JS SDK v1: Best Practices

You’re viewing the 1.X version of the Voice JavaScript SDK (formerly called Twilio Client). Click here for information on how to migrate to the 2.X version.


Twilio Client allows you to build high-quality calling experiences directly into web and mobile applications. Twilio Client provides a JavaScript SDK that can be used to build use cases like contact centers, sales dialers, peer-to-peer calling and others using familiar web and mobile development tools.

There are a few things you need to keep in mind to get the most out of Twilio Client. Following these best practices will ensure your users have a seamless calling experience. They will also make it easier to troubleshoot connection and call quality issues.

To get the most out of this guide, use it in conjunction with Twilio Client quickstarts and documentation.

Give users feedback when device state changes

The SDK relies on events following the EventEmitter interface to control the calling experience. Alerting the user to an incoming call requires listening for the Device.on('incoming') event for example. Similarly, the SDK also provides events for monitoring the Twilio Client Device state.

Surfacing changes in the device state to the UI using these events can often be the difference between a smooth calling experience and an extremely frustrating one.

Device is ready for calls: .on('ready', handler)

The Device.on('ready') event is fired once the Device has been successfully setup using a valid capability token. Use this event to change a UI element, like a status indicator for example. This ensures the user is aware that your application is online and ready to start making and receiving calls.

Device is not available for calls: .on('offline', handler)

Similarly, it’s important to notify the user if your application goes offline at any point of time. Use the Device.on('offline') event to change the status indicator to offline to alert the user. This event is triggered if the connection to Twilio drops for some reason or if the capability token expires. You should also use this event to attempt to reconnect using Device.setup().

Something’s wrong: .on('error', handler)

Handling this event allows you to catch and handle device errors gracefully. You can see the full list of errors surfaced by this handler here. Some commonly encountered errors are-

  • Errors with the capability token, either due to expiration or invalidation
  • The user denying your application access to the microphone. You can use this to disable the call button and instruct the user to provide microphone access

Gracefully handle no-answer situations

It’s also important to gracefully handle situations where a call to the Client goes unanswered in spite of the it being online. This depends on how the Client is being brought into the call-

Using <Dial>

Incoming calls can be connected to the Client using the <Dial> verb’s <Client> noun. In this case, you should set the timeout attribute to a value that works best for your use case. You should also configure an action URL using the action attribute. Twilio will make a request to this url with these parameters once the call gets concluded, which include the outcome of the call.

Using the REST API

The REST API can also be used to bring the Client into the call. This is involves first placing an outbound call to the client. When the Client picks up, the Url parameter retrieves TwiML that is used to set up the call. You can learn more about using the REST API here. It’s important to set a Timeout on the API request that works best for your use case. Note that the max is 60 seconds for calls made to Client. Be sure to configure a status callback URL using the StatusCallback parameter and specify the call progress event webhooks using the StatusCallbackUrl parameter. This ensures your application knows the outcome of the call.

If the call outcome in both situations is no-answer, it’s important this is conveyed to the caller. One way to do this is by directing them to voicemail. You can use the <Record> verb to set up voicemail. If the call is unanswered, the caller is directed to TwiML that uses the <Record> verb to leave a voicemail.

Working with microphones and getUserMedia

The SDK will automatically choose the default input and output devices when placing or receiving calls. However, we recommend setting the input device as early as possible to avoid problems at call connect or accept time e.g. hardware issues or permissions related issues. The following code snippet demonstrates how to work with input devices and set the mic to use as early as possible. Please note, this is not supported on Firefox due to this bug.

// Our UI is a Dropdown that shows the available input devices (microphones).
// The user can select the input device.
const micOptions = document.createElement('select');
micOptions.addEventListener('change', () => {;

// Update UI with the updated list of available devices
const updateMicOptions = () => {
  micOptions.innerHTML = ''; => {
    const option = document.createElement('option');
    option.value = d.deviceId;
    option.innerText = d.label;

// We want to detect if the device list changes e.g. a headset was plugged in/out.
// We set up handlers to update our dropdown list with the new device list
Twilio.Device.on('ready', () => {
  // Subscribe to the event for when the list of devices changes'deviceChange', () => updateMicOptions());
  // Now it's time to Call getUserMedia to get the input device names.
  // This is needed to get the labels. Otherwise, we will only have device IDs.
  // It's also recommended to ensure we know which mic to use when the call comes in.
  // Furthermore, performing this action here, allows for capturing gUM errors early
  // before accepting/receiving a call and it's possible to create a much better user experience
  navigator.mediaDevices.getUserMedia({ audio: true }).then((stream) => {
    // Calling getUserMedia will start the media track selected.
    // This is not desired as the user may get the impression the mic is in use.
    // Therefore, we want to avoid having tracks started when they're not needed.
    // We only wanted to get the input device list so we stop the tracks immediately.
    stream.getTracks().forEach(track => track.stop());
  }).catch(error => {
    // Handle error. Tell the user there's a a mic issue. You could also tell
    // your backend or raise an alert for the system admin to resolve this issue.
  // When handling incoming calls, use the device that was selected earlier
  Twilio.Device.on('incoming', (connection) => {
    // Now we can set the input device that we read in updateMicOptions.
    // `Device` will store this internally. This will avoid getUserMedia calls.
      .then(() => connection.accept())
      .catch(error => {
        // The audio device could not be set. Something has failed,
        // possibly a hardware (headset) failure.
        // Inform the user and try again or hang up the call.
        // Here you can also report this to your backend or system admin to help with the issue

Monitor call quality with Voice Insights

Voice Insights for Client provides call quality analytics for client calls. It provides a REST API for retrieving historical call quality statistics such as jitter, MoS and packet loss. It also provides a component in the JavaScript SDK that fires events when call quality drops below acceptable thresholds. This can be used to notify the user in real time about issues with call quality.

Voice Insights fires two types of events on the front end - network warnings and audio level warnings.

  • Network warnings are fired when there is a reduction in call quality as indicated by three measures - round trip time or RTT, mean opinion score or MOS, jitter, and packet loss.
  • Audio level events are fired when Insights detects unchanged audio levels. While these could indicate an issue, they usually indicate that the audio has been muted on the microphone or input device.

By implementing handlers for these events and surfacing them in the UI, you can notify the user about degradation in call quality or issues with audio input. This can be used to prompt the user to take remedial action like checking their internet connection or input device audio.

Implementing Voice Insights for Client can also make troubleshooting issues a lot easier. The Client Insights dashboard in the console provides aggregate call quality metrics across all client calls. This is useful in seeing trends in your call quality stats. For example, you could see that Client’s with a particular browser version are seeing more issues with quality. It also records call setup events, allowing you to diagnose issues with call connection. The same data is also made available for individual calls.

You can learn more about Voice Insights by checking out the docs.

Manage the calling environment

VoIP call quality is heavily influenced by environmental factors like firewall configuration, network conditions and available bandwidth, browser version (for webRTC) and OS and microphone and speaker hardware. It’s important you review our deployment best practices and connectivity requirements documentation before taking your app to production.

If possible, you should also take advantage of the DSCP support enabled in Twilio Client 1.3 onwards. DSCP, or Differentiated Services Code Point allows packets to be tagged to prioritize them on the network. Browsers that support DSCP are capable of tagging call media packets sent by the Client in this manner. Your router or network element can then use these tags to prioritize call media packets over other traffic on the network. Also, note that your router or network element needs to also be DSCP-compliant.

DSCP is currently supported only by Google Chrome currently. For help setting DSCP on a Windows machine, please see this Zendesk article.

Use the closest Twilio data center

Twilio has a global presence with data centers in the US, Ireland, Brazil, Singapore, Tokyo and Sydney. This minimizes latency by allowing your Twilio Client Device to connect to the closest point of presence. There are two ways you can set up your Client to connect to Twilio-

  • Use Twilio’s Global Low Latency routing to let Twilio use latency-based DNS lookups to pick the closest data center. You can do this by omitting the edge parameter while calling Twilio.Device.setup().
  • Force edge selection by using the edge parameter while calling Twilio.Device.setup(). You can find the list of edges and their IP addresses here. This approach makes sense if all Twilio Clients are going to be based in the same edge location.

Rate this page:

Need some help?

We all do sometimes; code is hard. Get help now from our support team, or lean on the wisdom of the crowd by visiting Twilio's Stack Overflow Collective or browsing the Twilio tag on Stack Overflow.

Loading Code Sample...

        Thank you for your feedback!

        Please select the reason(s) for your feedback. The additional information you provide helps us improve our documentation:

        Sending your feedback...
        🎉 Thank you for your feedback!
        Something went wrong. Please try again.

        Thanks for your feedback!