Client SDK is a browser based SDK written in JavaScript and powered by WebRTC Technology. Using the Client SDK, Developers can integrate voice calling capabilities into their core applications. Please note that in order to control the behavior of incoming/outgoing calls, Voice API’s call flows can be used to provide the expected call logic. Please check our Voice Calling API for more information
The client SDK is distributed on npm. To install the SDK and start using it in your app, install the package @messagebird/client
npm install @messagebird/client# Or if you use yarnyarn add @messagebird/client
In order to authenticate your Clients to use our platform, There are 2 steps that are needed:
The Following section explains the details of each of the previous steps:
Once you create a Messagebird account, you will be able to create, manage and retreive your Access keys from the Messagebird Developers Dashboard.
Clients can be given capabilities to perform specific actions. Capabilities are represented by a capability and their attributes. The attributes are represented as URL parameters. Example structure of a capability: capability?attribute1=foo&attribute2=bar.
In order to provide control on what your Client can do, currently our Client SDK supports 2 different capabilities:
You can choose to add either or both of them to your JWT.
This capability gives the Client the capability to initiate an outgoing call to our platform, upon which a call flow will be executed to process the call and apply the desired call logic. At this moment, a call can only be processed by a Voice API Call flow. In the future, Flow Builder will also be supported in order to execute the desired call logic.
attribute | type | description | required |
---|---|---|---|
callProcessor | string | The type of service that should process this call. At this moment we only support callflow, which can be created through the Voice-API. | Yes |
callProcessorId | string | The ID of the call flow object above. This ID can be retrieved through Voice-API and will also be represented to you when creating the call flow. | Yes |
variables | string | An urlencoded object of parameters that will be sent the incoming call processor (key\<>value). | No |
identity | string | The alpha-numeric caller-id of this connection. This will show up in the calls/legs in the API and will show up as source in fetchCallFlow steps and webhooks. When empty, anonymous will be used. Max-length: 30. | No |
An example of a client.outgoing can be found on the right. This example would process the call with a call-flow with ID de3ed163-d5fc-45f4-b8c4-7eea7458c635. The client would be identified as "Bert" and the variables "foo=bar&bar=foo" would be passed through with a call flow fetch. Note that the variables will show up in the call flow fetch also in the variables parameter.
This capability gives the client the capability to receive an incoming call from our platform via the Client SDK. Calls can be forwarded to the Client by using client:foobar in the destination parameter when doing a transfer-step or when creating an outgoing call through the Voice API.
attribute | type | description | required |
---|---|---|---|
identity | string | The alpha-numeric identity of this connection. This identity can be used to receive calls trough the destination parameter like client:foobar, if the identity would be foobar. Max-length: 30. | Yes |
The payload of a JWT should contain accessKeyID, capabilities, iat and exp.
The token should be signed with the MessageBird accessKey. Please make sure that the accessKey is not exposed on the client SDK side or in the JWT itself, only use it for signing the JWT. Please refer to JSON Web Tokens for help with generating JWT's in your current programming language.
{"accessKeyID": "example", // ID of a MessageBird AccessKey"capabilities": ["client.outgoing" +"?callProcessor=callflow" +"&callProcessorId=de3ed163-d5fc-45f4-b8c4-7eea7458c635" +"&identity=Bert" +"&variables=foo%3Dbar%26bar%3Dfoo"],"iat": 1516239022, // issued at"exp": 1516239022, // Expiry time}
The next step after the authentication is done, is to instantiate a new MessageBirdClient. You will use the MessageBirdClient instance to set up a connection to our platform, and to initiate and receive calls. After instantiating the client, call client.setup() to start the connection to our platform and be able to make and receive calls. A valid JWT has to be passed to the .setup() method for the authentication to succeed.
import { MessageBirdClient } from '@messagebird/client';/*** The "refreshToken" can be called multiple times over time.* This function should return a promise that resolves with a valid JWT.* If needed, you can also fetch a new JWT within this function.* */const refreshToken = async () => {if (isTokenExpired(myCurrentToken)) {return await refreshToken();}return myCurrentToken;}const client = new MessageBirdClient({refreshToken,});// start a connection to the MessageBird WebRTC APIclient.setup({ jwt: myCurrentToken })
The client will emit a status event when the connection status of the client changes. Possible status values are:
value | Description |
---|---|
offline | the initial status of the client after it is initialized . Calling destroy() resets the status to initial. |
connecting | the client is in the process of establishing a connection to our platform |
connected | the client is connected to our platform and ready for initiating and receiving calls |
error | An error occured. The error event is also fired containing the error that occured |
const handleStatusChange = (status) => {switch(status) {case 'connected':break;case 'error':break;case 'connecting':break;case 'initial':break;}};// Start listening for status updatesclient.on('status', handleStatusChange);// Stop receiving updates for status changesclient.removeEventListener('status', handleStatusChange);
If the connection to our platform fails after calling setup(), or if the client disconnects over time (for example when the browser loses internet connection), the client will emit an error event. Use this event to determine what your application should do next to ensure the client can reconnect to our platform.
These are the possible errors:
Code | Message | Description |
---|---|---|
403 | Forbidden, Token not found or expired. | JWT has expired or missing in the .setup() method |
406 | Invalid identity provided in client.incoming capability, only alphanumeric and underscore characters are allowed | client.incoming identity has to be alphanumeric characters only so that the Client can be identified by our platform and receive incoming calls. |
500 | Internal error | An unexpected internal error occurred. See the error object for more information |
const handleClientError = (error) => {switch(error.code) {case 403:// Unauthorized. Try refreshing the JWT and try againbreak;case 406:// Invalid identity. An error in "identity" capability of the the JWTbreak;case 500:// An internal error on our platform.break;}}client.on('error', handleClientError);client.removeEventListener('error', handleClientError);
The client.startCall() method can be called to start an outgoing call. Any additional data can be passed as arguments to the '.startCall()' method. This method will throw an error if you attempt to start a call while the client is not yet connected to our platform.
const { call } = client.startCall({// Any additional data can be passed as arguments to the 'startCall' methodfoo: 'bar'});call.on('status', handleCallStatus);call.on('error', handleCallError);call.endCall();
Listening to the client.on('incoming') event will allow you to respond to incoming calls on your UI. The callback function retrieves an instance of IncomingCall as its first argument, which can be used to respond to the incoming call by either calling:
client.on('incoming', incomingCall => {// Update your UI to display an incoming call.// Accept the incoming connection and start a callconst call = incomingCall.accept();// Reject the incoming callincomingCall.reject();});client.on('canceled', () => {// The incoming call was hung up from the other side.// Here, you can update UI to stop showing the incoming call.})
After having accepted an incoming call, or started an outgoing call, you can access the call instance. You can use this instance to subscribe to updates about the status of the call, and to modify the state of the call.
Just like the client, the call instance will emit status event that notifies you when the status of a call has changed. The status of the call can have the following values:
value | Description |
---|---|
connecting | Call is in the process of being connected |
accepted | Call is accepted by the called party |
confirmed | The call is accepted by called party and connection is established |
failed | Call failed. An error event was emitted containing details of the failure |
ended | Call ended without any failures |
ringing | Call is ringing on the called party end |
const handleStatusChange = (status) => {// Update the UI based on the status.setClientStatus(status);switch(status) {case 'connecting':case 'accepted':case 'confirmed':case 'failed':case 'ended':case 'ringing':}}call.on('status', handleStatusChange);call.removeEventListener('status', handleStatusChange);
When a call fails to establish, or unexpectedly disconnects during the call, an error event will be emitted notifying you of the failure reason.
const handleCallError = (error) => {// Update UI based on the error that occurred within the call}call.on('error', handleCallError);call.removeEventListener('error', handleCallError)
It is possible to enumerate the user's available media devices using the MediaSources class that is exposed by the SDK. This class exposes a public method called get() which will return the list of user devices, as well as a getter method called devices which performs the same function.
import { MediaSources } from '@messagebird/client';const sources = new MediaSources();// Enumerates the list of devicesconst devices = sources.get()// Provides the same device list as the above get() callconst theSameDevices = sources.devices
Because available devices may change over time as users connect/remove equipment, the MediaSources class makes available an event listener method on which accepts only the devicechange event. A corresponding off method can be used to remove any registered callbacks for this event, and it takes the same arguments. The callback to this method returns the full list of devices that are currently available, any handling of differences is therefore left up to the author.
import { MediaSources } from '@messagebird/client';const sources = new MediaSources();function handleChange(devices) {console.log({ devices })}sources.on('devicechange', handleChange)sources.off('devicechange', handleChange)
Device listing methods, including the callbacks to the event listeners, will always respond with the following structure where MediaDeviceInfo follows the standard implementation. Note that video input streams are not currently supported by the SDK, but are still available in the enumeration methods.
{inputs: {audio: MediaDeviceInfo[]video: MediaDeviceInfo[]};outputs: {audio: MediaDeviceInfo[]};}
Once devices have been enumerated, it is possible to select a particular device to be used in the call by using the setInputSource method available on a call object. To learn how to initialize a call, and therefore have access to this interface, see Starting a new Call.
The setInputSource method accepts a single argument that specifies the MediaStreamConstraints that should be used. After enumerating available devices, the identifiers of the user's media sources will be available, and these can be used to select specific devices.
const sources = new MediaSources();const [anAudioDevice] = sources.devices.audio;call.setInputSource({ audio: { deviceId: anAudioDevice.deviceId } })
The setOutputSource method of a call allows you to specify the audio output that should be used on a particular call. Because the selection of this device must be unique, this method only accepts the deviceId property of a MediaSource.
const sources = new MediaSources();const [anAudioDevice] = sources.devices.audio;call.setOutputSource(anAudioDevice.deviceId)
You can mute your microphone (or the selected audio device) by calling the .mute() method on a call. If you need to unmute, .unmute() can be called on the same call instance. In both cases muted event is fired when the change has been successfully applied. The event has the following values:
value | Description |
---|---|
true | The audio device is muted |
false | The audio device is not muted |
const handleMutedChange = (isMuted) => {// Update UI to show a call was muted or unmuted.}call.on('muted', handleMutedChange);call.removeEventListener('muted', handleMutedChange)call.mute(true); // Mute the callcall.mute(false); // unmute the call