Skip to contentSkip to navigationSkip to topbar
Rate this page:
On this page

Voice JavaScript SDK: Changelog


(information)

Info

This is the Changelog for v2 of the Voice Javascript SDK. Click here to see the v1 Changelog.

(warning)

Warning

If you are upgrading to version 2.3.0 or later and have firewall rules or network configuration that blocks any unknown traffic by default, you need to update your configuration to allow connections to the new DNS names and IP addresses. Please refer to this changelog for more details.


2.10.2 (February 14, 2024)

2102-february-14-2024 page anchor

Bug Fixes

bug-fixes page anchor

2.10.1 (January 12, 2024)

2101-january-12-2024 page anchor

2.10.0 (January 5, 2024)

2100-january-5-2024 page anchor
  • Added tags to client logs for easier filtering
  • Added log statements to API calls and events for debugging purposes

2.9.0 (November 28, 2023)

290-november-28-2023 page anchor

Audio Processor APIs

audio-processor-apis page anchor

The SDK now includes Audio Processor APIs, enabling access to raw audio input and the ability to modify audio data before sending it to Twilio. With this new feature, the following use cases can now be easily achieved on the client side:

  • Background noise removal using a noise cancellation library of your choice
  • Music playback when putting the call on hold
  • Audio filters
  • AI audio classification
  • ... and more!

Please visit this page for more details about the Audio Processor APIs.


2.8.0 (October 16, 2023)

280-october-16-2023 page anchor

Added a new feature flag enableImprovedSignalingErrorPrecision to enhance the precision of errors emitted by Device and Call objects.


_10
const token = ...;
_10
const device = new Device(token, {
_10
enableImprovedSignalingErrorPrecision: true,
_10
});

The default value of this option is false.

When this flag is enabled, some errors that would have been described with a generic error code are now described with a more precise error code. With this feature, the following errors now have their own error codes. Please see this page for more details about each error.

Device Error Changes


_10
const device = new Device(token, {
_10
enableImprovedSignalingErrorPrecision: true,
_10
});
_10
device.on('error', (deviceError) => {
_10
// the following table describes how deviceError will change with this feature flag
_10
});

Device Error NameDevice Error Code with Feature Flag EnabledDevice Error Code with Feature Flag Disabled
GeneralErrors.ApplicationNotFoundError3100153000
GeneralErrors.ConnectionDeclinedError3100253000
GeneralErrors.ConnectionTimeoutError3100353000
MalformedRequestErrors.MissingParameterArrayError3110153000
MalformedRequestErrors.AuthorizationTokenMissingError3110253000
MalformedRequestErrors.MaxParameterLengthExceededError3110353000
MalformedRequestErrors.InvalidBridgeTokenError3110453000
MalformedRequestErrors.InvalidClientNameError3110553000
MalformedRequestErrors.ReconnectParameterInvalidError3110753000
SignatureValidationErrors.AccessTokenSignatureValidationFailed3120253000
AuthorizationErrors.NoValidAccountError3120353000
AuthorizationErrors.JWTTokenExpirationTooLongError3120753000
ClientErrors.NotFound3140453000
ClientErrors.TemporarilyUnavilable3148053000
ClientErrors.BusyHere3148653000
SIPServerErrors.Decline3160353000

Call Error Changes


_10
const device = new Device(token, {
_10
enableImprovedSignalingErrorPrecision: true,
_10
});
_10
const call = device.connect(...);
_10
call.on('error', (callError) => {
_10
// the following table describes how callError will change with this feature flag
_10
});

Call Error NameCall Error Code with Feature Flag EnabledCall Error Code with Feature Flag Disabled
GeneralErrors.ConnectionDeclinedError3100231005
AuthorizationErrors.InvalidJWTTokenError3120431005
AuthorizationErrors.JWTTokenExpiredError3120531005

IMPORTANT: If your application logic currently relies on listening to the generic error code 53000 or 31005, and you opt into enabling the feature flag, then your applicaton logic needs to be updated to anticipate the new error code when any of the above errors happen.


2.7.3 (October 6, 2023)

273-october-6-2023 page anchor
  • Fixed an issue(link takes you to an external page) where, sometimes a TypeError is raised while handling an incoming call under the following circumstances:
    • Network interruptions
    • updating the token before accepting the call

2.7.2 (September 21, 2023)

272-september-21-2023 page anchor

Updated November 1, 2023

We have identified an issue on Chromium-based browsers running on MacOS 14 (Sonoma) where the audio deteriorates during a call. This issue happens due to the excessive calls to MediaDevices: enumerateDevices() API. With this release, the SDK calls this API only when necessary to avoid audio deterioration.

  • Fixed an issue(link takes you to an external page) where audio in the Chrome browser is choppy when another application is also using the audio devices.
  • Added missing documentation for the following events:
    • call.on('ringing', handler)
    • call.on('warning', handler)
    • call.on('warning-cleared', handler)
    • device.on('destroyed', handler)

2.7.1 (August 3, 2023)

271-august-3-2023 page anchor

2.7.0 (August 1, 2023)

270-august-1-2023 page anchor

ECMAScript Module Support

ecmascript-module-support page anchor

Currently, the SDK is imported as a CommonJS Module (CJS)(link takes you to an external page) using the root path @twilio/voice-sdk. With this release, the SDK contains an experimental feature that allows it to be imported as an ECMAScript Module (ESM)(link takes you to an external page) using the @twilio/voice-sdk/esm path. As this is an experimental feature, some frameworks using bundlers like Vite and Rollup may not work. Full support for ESM will be available in a future release and will become the default import behavior of the SDK.

Example:


_10
import { Device } from '@twilio/voice-sdk/esm';


2.6.1 (July 7, 2023)

261-july-7-2023 page anchor
  • Fixed some security vulnerabilities shown by npm audit .
  • Removed unused dependencies.
  • Replaced deprecated dependencies.
  • Fixed an issue(link takes you to an external page) where calling device.updateOptions would reset the device.audio._enabledSounds state.
  • Fixed an issue where custom DTMF sounds would not play. With this release, custom DTMF sounds should now play when configured during device initialization.

_10
const device = new Device(token, {
_10
sounds: {
_10
dtmf8: 'http://mysite.com/8_button.mp3',
_10
// Other custom sounds
_10
},
_10
// Other options
_10
});


2.6.0 (June 20, 2023)

260-june-20-2023 page anchor
  • The SDK now builds on NodeJS versions 16 and above without the --legacy-peer-deps flag.
  • Removed usage of NodeJS modules from the SDK and some dependencies. With this change, the SDK should now work with some of the latest frameworks that use the latest versions of bundlers such as Vite and Webpack.
  • The AudioPlayer dependency has been incorporated into the SDK as part of a migration. This change fixes an issue where source maps are not properly loaded.
  • Removed unnecessary files from the generated npm package.
  • Links to source maps are now included in the generated npm package.
  • The ws package has been moved to devDependencies .
  • The SDK no longer depends on the xmlhttprequest npm package.

2.5.0 (May 9, 2023)

250-may-9-2023 page anchor

WebRTC API Overrides (Beta)

webrtc-api-overrides-beta page anchor

The SDK now allows you to override WebRTC APIs using the following options and events. If your environment supports WebRTC redirection, such as Citrix HDX(link takes you to an external page)'s WebRTC redirection technologies(link takes you to an external page), your application can use this new beta feature for improved audio quality in those environments.


2.4.0 (April 6, 2023)

240-april-6-2023 page anchor
  • Updated the description of Device.updateToken API. It is recommended to call this API after Device.tokenWillExpireEvent is emitted, and before or after a call to prevent a potential ~1s audio loss during the update process.
  • Updated stats reporting to stop using deprecated RTCIceCandidateStats - ip and deleted .

2.3.2 (February 27, 2023)

232-february-27-2023 page anchor

2.3.1 (February 3, 2023)

231-february-3-2023 page anchor

2.3.0 (January 23, 2023)

230-january-23-2023 page anchor

This release includes updated DNS names for Twilio Edge Locations. The Voice JS SDK uses these Edge Locations to connect to Twilio's infrastructure via the parameter Device.Options.edge. The current usage of this parameter does not change as the SDK automatically maps the edge value to the new DNS names.

Additionally, you need to update your Content Security Policies (CSP)(link takes you to an external page) if you have it enabled for your application. You also need to update your network configuration such as firewalls, if necessary, to allow connections to the new DNS names and IP addresses.


2.2.0 (December 5, 2022)

220-december-5-2022 page anchor

Call Message Events (Beta)

call-message-events-beta page anchor

The SDK can now send and receive custom messages to and from Twilio's backend via the following new Call APIs.

Please visit this page for more details about this feature. Additionally, please see the following for more information on how to send and receive messages on the server.

NOTE: This feature should not be used with PII.

Example


_20
const device = new Device(token, options);
_20
_20
const setupCallHandlers = call => {
_20
call.on('messageReceived', message => messageReceivedHandler(message));
_20
call.on('messageSent', message => messageSentHandler(message));
_20
};
_20
_20
// For outgoing calls
_20
const call = await device.connect();
_20
setupCallHandlers(call);
_20
_20
// For incoming calls
_20
device.on('incoming', call => setupCallHandlers(call));
_20
await device.register();
_20
_20
// For sending a message
_20
const eventSid = call.sendMessage({
_20
content: { foo: 'foo' },
_20
messageType: Call.MessageType.UserDefinedMessage,
_20
});


2.1.2 (October 26, 2022)

212-october-26-2022 page anchor
  • Fixed an issue where insights data stops getting published after calling device.updateOptions .

2.1.1 (February 18, 2022)

211-february-18-2022 page anchor
  • Ignoring a call will now properly stop the ringing sound
  • NPM versioning has been fixed to specify >=12 rather than exactly 12
  • Use DOMException instead of DOMError, which has been deprecated
  • Removed npm util from the package, instead favoring native functions

2.1.0 (January 6, 2022)

210-january-6-2022 page anchor

Signaling reconnection support

signaling-reconnection-support page anchor

The Voice JavaScript SDK now fully supports Call reconnection. If the media connection or signaling websocket is lost, the SDK is able to attempt to reconnect the Call. A Call can now potentially be recovered up to 30 seconds after a media or signaling connection loss.

The Twilio.Device will emit a 'reconnecting' event when a connectivity loss occurs, and a 'reconnected' event upon successful reconnection.

There exists a limitation such that Signaling Reconnection and Edge Fallback are mutually exclusive. To opt-in to the Signaling Reconnection feature, a new option can be passed to the SDK: maxCallSignalingTimeoutMs. If this value is not present in the options object passed to the Device constructor, the default value will be 0. Reconnection can only happen with an up-to-date AccessToken.

(warning)

Warning

Customers relying on edge fallback, along with a small subset of customers using the 'roaming' edge, will not automatically benefit from this feature without additional configuration. Go to the Edge Locations page for more information.

Keep AccessToken up to date with tokenWillExpire event and DeviceOptions.tokenWillExpire

keep-accesstoken-up-to-date-with-tokenwillexpire-event-and-deviceoptionstokenwillexpire page anchor

The Voice JavaScript SDK now provides two additional features to help keep your AccessTokens up to date:

  1. The 'tokenWillExpire' event, which will be emitted by the Twilio.Device before its associated AccessToken is set to expire. By default, it will be emitted 10 seconds before the AccessToken's expiration.
  2. The DeviceOptions.tokenRefreshMs property that can configure the timing of the 'tokenWillExpire' event.

You can use these new features in conjunction with the device.updateToken() method to automatically keep an AccessToken up to date.

In the following example, the 'tokenWillExpire' event will be emitted 30 seconds (3000 milliseconds) before the AccessToken is set to expire, and the event listener for the 'tokenWillExpire' event will retrieve a new AccessToken and update the Device's AccessToken with the device.updateToken() method.


_10
const device = new Device(token, {
_10
// 'tokenWillExpire' event will be emitted 30 seconds before the AccessToken expires
_10
tokenRefreshMs: 30000,
_10
});
_10
_10
device.on('tokenWillExpire', () => {
_10
return getTokenViaAjax().then(token => dev.updateToken(token));
_10
});

Support for Twilio Regions

support-for-twilio-regions page anchor

The Twilio Voice JavaScript SDK now supports Twilio Regions.

If you are part of the Twilio Regions Pilot and wish to specify a home region when using the Voice JavaScript SDK, you will need to:

  1. Create AccessTokens with API Keys and API Key Secrets that are stored in the specified Twilio Region, and include the Region name when creating the AccessToken. See example below.
  2. Use an edge location that matches your specified Region when instantiating your Twilio.Device .

Below is an example of how you would use the Node.js Helper Library to create AccessTokens for the Voice JavaScript SDK for a Region.


_16
const accessToken = const accessToken = new twilio.jwt.AccessToken(
_16
credentials.accountSid,
_16
credentials.apiKeySid,
_16
credentials.apiKeySecret, {
_16
identity,
_16
ttl,
_16
region: 'au1',
_16
},
_16
);
_16
_16
const grant = new VoiceGrant({
_16
outgoingApplicationSid: credentials.twimlAppSid,
_16
incomingAllow: true,
_16
});
_16
_16
accessToken.addGrant(grant);

Note: The API Key and Secret above must be created within the au1 region. It's recommended that the TwiML App used in the Voice Grant is also created in the same Region.

The example below shows how to pass the au1-related edge location to the Twilio.Device constructor.


_10
const device = new Device(accessToken, {
_10
edge: 'sydney',
_10
});

The new Twilio.Device.home accessor will return a string value of the home region of the device instance, given that it successfully connected with Twilio.

(information)

Info

Existing EU customers can now migrate their Voice use-cases to the data center in Ireland to establish data residency within the region. In addition, new customers may now select Ireland as their region of choice for Voice related use cases. There is no additional cost to use the new data center in Ireland. To learn more about Regional Voice in Ireland, check out our blog post(link takes you to an external page) or head over to our Global Infrastructure docs to get started.

The Voice JavaScript SDK now exposes a Twilio.Device.identity accessor.

Given that a Twilio.Device has registered successfully with Twilio, the Twilio.Device.identity accessor will return a read-only string containing the identity that was passed to the AccessToken used to instantiate the Twilio.Device.

  • Updated ws version to fix a potential security vulnerability
  • All event listeners will now be properly cleaned up after calling Twilio.Device.destroy()
  • When Insights fails to post an event, the SDK now logs a warning rather than an Uncaught Promise Rejection

2.0.0 (July 9, 2021)

200-july-9-2021 page anchor

Device singleton behavior removed

device-singleton-behavior-removed page anchor

Device must now be instantiated before it can be used. Calling Device.setup() will no longer work; instead, a new Device must be instantiated via new Device(token, options?).

Connection renamed to Call

connection-renamed-to-call page anchor

As Connection is an overloaded and ambiguous term, the class has been renamed Call to better indicate what the object represents and be more consistent with Mobile SDKs and our REST APIs.

Signaling connection now lazy loaded

signaling-connection-now-lazy-loaded page anchor

Device.setup() has been removed, and new Device(...) will not automatically begin connecting to signaling. There is no need to listen for Device.on('ready'). Instead, the signaling connection will automatically be acquired in one of two scenarios:

  1. The application calls Device.connect() , creating an outbound Call. In this case, the state of the signaling connection will be represented in the Call.
  2. The application calls Device.register() , which will register the client to listen for incoming calls at the identity specified in the AccessToken.
Note on token expiration
note-on-token-expiration page anchor

As long as outgoing calls are expected to be made, or incoming calls are expected to be received, the token supplied to Device should be fresh and not expired. This can be done by setting a timer in the application to call updateToken with the new token shortly before the prior token expires. This is important, because signaling connection is lazy loaded and will fail if the token is not valid at the time of creation.

Example:


_10
const TTL = 600000; // Assuming our endpoint issues tokens for 600 seconds (10 minutes)
_10
const REFRESH_TIMER = TTL - 30000; // We update our token 30 seconds before expiration;
_10
const interval = setInterval(async () => {
_10
const newToken = await getNewTokenViaAjax();
_10
device.updateToken(newToken);
_10
}, REFRESH_TIMER);

The Device states have changed. The states were: [Ready, Busy, Offline]. These have been changed to more accurately and clearly represent the states of the Device. There are two changes to Device state:

  1. The states themselves have changed to [Registered, Registering, Unregistered, Destroyed] . This removes the idea of "Busy" from the state, as technically the Device can have an active Call whether it is registered or not, depending on the use case. The Device will always starty as Unregistered . In this state, it can still make outbound Calls. Once Device.register() has been called, this state will change to Registering and finally Registered . If Device.unregister() is called the state will revert to Unregistered . If the signaling connection is lost, the state will transition to Registering or `Unregistered' depending on whether or not the connection can be re-established.

The destroyed state represents a Device that has been "destroyed" by calling Device.destroy. The device should be considered unusable at this point and a new one should be constructed for further use.

  1. The busy state has been moved to a boolean, Device.isBusy . This is a very basic shortcut for the logic return !!device.activeConnection .

The events emitted by the Device are represented by the Device.EventName enum and represent the new Device states:


_10
export enum EventName {
_10
Destroyed = 'destroyed',
_10
Error = 'error',
_10
Incoming = 'incoming',
_10
Unregistered = 'unregistered',
_10
Registering = 'registering',
_10
Registered = 'registered',
_10
}

Note that unregistered, registering, and registered have replaced offline and ready. Although frequently used to represent connected or disconnected, ready and offline actually were meant to represent registered and unregistered, which was quite ambiguous and a primary reason for the change.

When the device is destroyed using Device.destroy, a "destroyed" event will be emitted.

The construction signature and usage of Device has changed. These are the new API signatures:


_22
/**
_22
* Create a new Device. This is synchronous and will not open a signaling socket immediately.
_22
*/
_22
new Device(token: string, options?: Device.Options): Device;
_22
_22
/**
_22
* Promise resolves when the Device has successfully registered.
_22
* Replaces Device.registerPresence()
_22
* Can reject if the Device is unusable, i.e. "destroyed".
_22
*/
_22
async Device.register(): Promise<void>;
_22
/**
_22
* Promise resolves when the Device has successfully unregistered.
_22
* Replaces Device.unregisterPresence()
_22
* Can reject if the Device is unusable, i.e. "destroyed".
_22
*/
_22
async Device.unregister(): Promise<void>;
_22
/**
_22
* Promise resolves when signaling is established and a Call has been created.
_22
* Can reject if the Device is unusable, i.e. "destroyed".
_22
*/
_22
async Device.connect(options?: Device.ConnectOptions): Promise<Call>;

Listening for incoming calls
listening-for-incoming-calls page anchor

_10
const device = new Device(token, { edge: 'ashburn' });
_10
_10
device.on(Device.EventName.Incoming, call => { /* use `call` here */ });
_10
await device.register();


_10
const device = new Device(token, { edge: 'ashburn' });
_10
const call = await device.connect({ To: 'alice' });

Device CallOptions and Call AcceptOptions standardized

device-calloptions-and-call-acceptoptions-standardized page anchor

The arguments for Device.connect() and Call.accept() have been standardized to the following options objects:


_11
interface Call.AcceptOptions {
_11
/**
_11
* An RTCConfiguration to pass to the RTCPeerConnection constructor.
_11
*/
_11
rtcConfiguration?: RTCConfiguration;
_11
_11
/**
_11
* MediaStreamConstraints to pass to getUserMedia when making or accepting a Call.
_11
*/
_11
rtcConstraints?: MediaStreamConstraints;
_11
}


_10
interface Device.ConnectOptions extends Call.AcceptOptions {
_10
/**
_10
* A flat object containing key\:value pairs to be sent to the TwiML app.
_10
*/
_10
params?: Record<string, string>;
_10
}

Note that these now take a MediaStreamConstraints(link takes you to an external page) rather than just the audio constraints. For example:


_10
device.connect({ To: 'client:alice' }, { deviceId: 'default' });

might be re-written as:


_10
device.connect({
_10
params: { To: 'client:alice' },
_10
rtcConstraints: { audio: { deviceId: 'default' } },
_10
});

Moved to new Error format

moved-to-new-error-format page anchor

For backward compatibility, the new error format was attached to the old format under error.twilioError:


_10
class oldError extends Error {
_10
//...
_10
code: number;
_10
message: string;
_10
twilioError: TwilioError;
_10
}

The new Error format is:


_41
class TwilioError extends Error {
_41
/**
_41
* A list of possible causes for the Error.
_41
*/
_41
causes: string[];
_41
_41
/**
_41
* The numerical code associated with this Error.
_41
*/
_41
code: number;
_41
_41
/**
_41
* A description of what the Error means.
_41
*/
_41
description: string;
_41
_41
/**
_41
* An explanation of when the Error may be observed.
_41
*/
_41
explanation: string;
_41
_41
/**
_41
* Any further information discovered and passed along at run-time.
_41
*/
_41
message: string;
_41
_41
/**
_41
* The name of this Error.
_41
*/
_41
name: string;
_41
_41
/**
_41
* The original Error received from the external system, if any.
_41
*/
_41
originalError?: Error;
_41
_41
/**
_41
* A list of potential solutions for the Error.
_41
*/
_41
solutions: string[];
_41
}

With the transition, the following error codes have changed:

  • 31003 -> 53405 | When ICE connection fails
  • 31201 -> 31402 | When getting user media fails
  • 31208 -> 31401 | When user denies access to user media
  • 31901 -> 53000 | When websocket times out in preflight

Previously, Device.setup() could only be used the set options once. Now, we've added Device.updateOptions(options: Device.Options) which will allow changing the Device options without instantiating a new Device. Note that the edge cannot be changed during an active Call.

Example usage:


_10
const options = { edge: 'ashburn' };
_10
const device = new Device(token, options);
_10
_10
// Later...
_10
_10
device.updateOptions({ allowIncomingWhileBusy: true });

The resulting (non-default) options would now be:


_10
{
_10
allowIncomingWhileBusy: true,
_10
edge: 'ashburn',
_10
}

This function will throw with an InvalidStateError if the Device has been destroyed beforehand.

The SDK now uses the loglevel(link takes you to an external page) module. This exposes several new features for the SDK, including the ability to intercept log messages with custom handlers and the ability to set logging levels after instantiating a Device. To get an instance of the loglevel Logger class used internally by the SDK:


_10
import { Logger as TwilioClientLogger } from '@twilio/voice-client-sdk';
_10
...
_10
TwilioClientLogger.setLogLevel('DEBUG');

Please see the original loglevel(link takes you to an external page) project for more documentation on usage.

  • Removed Connection.mediaStream . To access the MediaStreams, use Connection.getRemoteStream() and Connection.getLocalStream()
  • Removed Connection.message in favor of the newer Connection.customParameters . Where .message was an Object, .customParameters is a Map .
  • Removed the following private members from the public interface:
    • Connection.options
    • Connection.pstream
    • Connection.sendHangup
  • Fixed Connection.on('cancel') logic so that we no longer emit cancel in response to Connection.ignore() .

Device Option Deprecations

device-option-deprecations page anchor

Some deprecated Device options have been removed. This includes:

  • enableIceRestart
  • enableRingingState
  • fakeLocalDtmf

The above three removed options are now assumed true. The new Device.Options interface is now:


_15
export interface Options {
_15
allowIncomingWhileBusy?: boolean;
_15
appName?: string;
_15
appVersion?: string;
_15
audioConstraints?: MediaTrackConstraints | boolean;
_15
closeProtection?: boolean | string;
_15
codecPreferences?: Connection.Codec[];
_15
disableAudioContextSounds?: boolean;
_15
dscp?: boolean;
_15
edge?: string[] | string;
_15
forceAggressiveIceNomination?: boolean;
_15
maxAverageBitrate?: number;
_15
rtcConfiguration?: RTCConfiguration;
_15
sounds?: Partial<Record<Device.SoundName, string>>;
_15
}

The formula used to calculate the mean-opinion score (MOS) has been fixed for extreme network conditions. These fixes will not affect scores for nominal network conditions.


Rate this page: