Web Client SDK APIs
Receive Calls
Once the registration has happened, and when there is an incoming call, the callback registered for “Call Events” would get an event along with the details of the incoming call.
API Name | CallListenerCallback | ||
|---|---|---|---|
Args | Params | Type | Values |
callObj | Object | { callId, callState, callDirection, callStartedTime, remoteDisplayName } callId {String}: sip call-id callState {String}: incoming callDirection {String}: incoming callStartedTime {String}: call start time remoteDisplayName {String}: callee name | |
eventType | String | incoming : shows incoming call message connected : open dialer callEnded : close dialer activeSession: session continuity | |
phone | String | Username identifying the phone to which the call is coming. | |
Exported To | Application UI | ||
In the following snippet,the UI states are appropriately modified based on the call events.
function CallListenerCallback(callObj, eventType, phone) {
if (eventType === 'incoming') {
// Incoming Call
setCallComing(true)
// callSid / legSid are already parsed for you — prefer these over sipHeaders.
const { callSid, legSid, sipHeaders } = callObj;
console.log('callSid:', callSid, 'legSid:', legSid);
// Reading a header directly from sipHeaders? SIP.js title-cases each dash
// segment when it stores the header, so the wire's mixed-case name is
// never present as a key. This looks up 'X-Exotel-CallSid' and misses:
console.log(sipHeaders['X-Exotel-CallSid']); // undefined
// The stored key is 'X-Exotel-Callsid' (lowercase "sid"):
console.log(sipHeaders['X-Exotel-Callsid']); // matches callObj.callSid
} else if (eventType === 'connected') {
// Call in connected state
setCallComing(false)
setCallState(true)
} else if (eventType === 'callEnded') {
// Call ended
setCallComing(false)
setCallState(false)
} else if (eventType === 'terminated') {
// Call terminated
setCallComing(false)
setCallState(false)
}
}Accept Calls
Once the incoming call event is received, based on the user action the call can be accepted. The API to do so is “Call.Answer” as below. The call object could be obtained by invoking the “getCall()” method in exWebClient object.
API Name | Call.Answer |
|---|---|
Args | None |
Exported To | Application UI |
function acceptCallHandler() {
call = exWebClient.getCall()
call.Answer();
}The response event to accept calls comes in “CallListenerCallback”. Successful acceptance results in a “connected” event. Other possible events are:
“callEnded” : Call ended locally.
“terminated” : Call terminated remotely.
function CallListenerCallback(callObj, eventType, phone) {
if (eventType === 'incoming') {
// Incoming Call
setCallComing(true)
} else if (eventType === 'connected') {
// Call in connected state
setCallComing(false)
setCallState(true)
} else if (eventType === 'callEnded') {
// Call ended
setCallComing(false)
setCallState(false)
} else if (eventType === 'terminated') {
// Call terminated
setCallComing(false)
setCallState(false)
}
}Hangup / Reject Calls
Once the incoming call event is received, based on the user action the call can be rejected. The API to do so is “Call.Hangup” as below.
API Name | Call.Hangup |
|---|---|
Args | None |
Exported To | Application UI |
function rejectCallHandler() {
call = exWebClient.getCall()
call.Hangup();
}
For call hangup by local user, the response comes as a “callEnded” event. For call hangup by remote user,, the event comes as “terminated”.
function CallListenerCallback(callObj, eventType, phone) {
if (eventType === 'callEnded') {
// Call ended
setCallComing(false)
setCallState(false)
} else if (eventType === 'terminated') {
// Call terminated
setCallComing(false)
setCallState(false)
}
}Mute / Unmute Calls
Once the call is in progress, based on the user action the call can be muted/unmuted. The APIs to do so are “Call.Mute” and “Call.UnMute” as below.
API Name | Call.Mute |
|---|---|
Args | None |
Exported To | Application UI |
API Name | Call.UnMute |
|---|---|
Args | None |
Exported To | Application UI |
You can also use a single API to toggle the mic using Call.MuteToggle.
API Name | Call.MuteToggle |
|---|---|
Args | None |
Exported To | Application UI |
function muteHandler() {
call = exWebClient.getCall()
//call.MuteToggle(); // or
if (!callOnMute) {
call.Mute()
callOnMute = true
} else {
call.UnMute()
callOnMute = false
}
}There is no callback event for mute. Call remains “connected” but with the mic muted. This is a toggle operation.
Hold / Resume Calls
Once the call is in progress, based on the user action the remote user can be set on hold/unhold. The API to do so is “Call.Hold” and “Call.UnHold” as below.
API Name | Call.Hold |
|---|---|
Args | None |
Exported To | Application UI |
API Name | Call.UnHold |
|---|---|
Args | None |
Exported To | Application UI |
You can also use a single API to hold the remote caller using Call.HoldToggle.
function holdHandler() {
call = exWebClient.getCall()
//call.HoldToggle(); // or
if (!callOnHold) {
call.Hold()
callOnHold = true
} else {
call.UnHold()
callOnHold = false
}
}There is no callback event for call hold. Call remains in “connected” but in sendonly mode. This is a toggle operation.
Send DTMF
Once the call is in progress, based on the user action the remote user can be sent DTMF digits. The API to do so is “Call.sendDTMF” as below
API Name | Call.sendDTMF |
|---|---|
Args | Params Values Type |
digit | String 0-9, A-D, *, # |
Exported To | Application UI |
call = exWebClient.getCall()
if (call) {
call.sendDTMF(digit);
}Multitab Scenarios
When the webrtc sdk is loaded in multiple tabs then all the instances will register with the backend and receive incoming call alerts. There are two ways to avoid this,
- Maintain a single login session for the user in your webapp so that only one instance of the webrtc sdk is loaded. This is preferred.
- Maintain a parent-child relationship across the tabs in your webapp so that call is handled only in the parent tab.
Exotel Webrtc-SDK supports multiple tab sessions using Broadcast Channel. In this feature, session listener and session callbacks can be used to get the indication on child tabs. A SessionListener creates a broadcast channel and sends a broadcast event to each child tab for the following events, incoming / connected / callEnded / re-register. When a child tab (as per the logic maintained by the “Application Backend”.) receives a session event, it may notify but not handle the callback.
When the parent tab is destroyed, a re-register event comes to child tabs, based on the logic of the client, a child can opt to be the new parent.
Note 1: The logic of maintaining the parent and child tabs has to be in “Application Backend” by the “Customer”.
Note 2: If multitab scenario is not used, there is no need to handle the events in the session callback.
API Name | SessionListener |
|---|---|
Args | None |
Exported To | Application UI |
SessionListener(); // To be called during initialization.
API Name | SessionCallback | ||
|---|---|---|---|
Args | Params | Type | Values |
callState | String | incoming / connected / callEnded / re-register incoming : child tab to show a notification message connected : child tab to close the notification message callEnded : child tab to close the notification message re-register : child tab to register when parent tab is closed ice_gathering_state_<state>: ICE gathering state changed. <state> will be the new ICE gathering state (e.g., new, gathering, complete). ice_connection_state_<state>: ICE connection state changed. <state> will be the new ICE connection state (e.g., new, checking, connected, disconnected, failed, closed). Media_permission_denied: User denied media (microphone/camera) permissions. | |
phone | String | username identifying the phone to which the call is coming. | |
Exported To | Application UI | ||
A sample code snippet for session callback is as below.
function SessionCallback(callState, phone) {
/**
* SessionCallback is triggered whenever an incoming call arrives
* which needs to be handled across tabs
*/
switch(callState){
case 'incoming':
console.log('incoming call' + phone)
/**
* Display a different notification popup in case of child tabs
*/
if(window.sessionStorage.getItem('activeSessionTab') !== 'parent0'){
const message = 'Incoming call from ' + phone + ' ,Switch tab to find dialpad'
setMessage(message);
}
break;
case 'callEnded':
/**
* When call is either accepted or rejected then this is gets shutdown
*/
console.log('call ended' + phone)
setOpen(false);
break;
case 'connected':
/**
* When call is connected close the notification popup on child tabs
*/
console.log('call connected' + phone)
setOpen(false);
break;
case 're-register':
/**
* In case if the main/parent tab is closed then make the subsequent tab in the tab list as the parent tab
* and send register for the same and also make that tab as the master
*/
// ICE Gathering State Events
case 'ice_gathering_state_new':
case 'ice_gathering_state_gathering':
case 'ice_gathering_state_complete':
console.log('ICE state change:', callState, 'for call from:', phone);
// Handle ICE state changes as needed
break;
// Media Permission Error
case 'media_permission_denied':
console.log('Media permission denied for call from:', phone);
showErrorMessage('Microphone access is required for calls. Please allow microphone permissions.');
break;
window.sessionStorage.removeItem('activeSessionTab');
window.sessionStorage.setItem('activeSessionTab', 'parent0');
sendAutoRegistration();
}
break;
}
};Device and Network Diagnostics
Initialize diagnostics.
Diagnostics can be initialized by passing two callbacks, one troubleshooting logs and one to get the responses of diagnostics back.
API Name | ExotelWebClient.initDiagnostics | ||
|---|---|---|---|
Args | Params | Type | Values |
diagnosticsReportCallback | Object | Defined below | |
keyValueSetCallback | Object | Defined below | |
Exported To | Application UI | ||
The signature of the callbacks are as below.
diagnosticsReportCallback is for logs
function diagnosticsReportCallback(logStatus, logData) {
// logStatus : Additional information on the logs, can be ignored as of now.
// logData : Troubleshooting log to save in a file..
}diagnosticsKeyValueCallback is for test responses, described in the following sections.
function diagnosticsKeyValueCallback(key, status, description) {
// key : Indicates the type of response
// status: the value/status specific to key
// description : description specific to key
}Immediately after invoking the initDiagnostics, three parameters are returned through the diagnosticsKeyValueCallback callback.
browserVersion | browserName/browserVersion. Eg. Chrome/101.0.0.0 |
|---|---|
micInfo | Mic name returned by the browser. Eg. “Built-in Audio Analog Stereo” |
speakerInfo | Speaker Name returned by the browser. Eg. “Built-in Audio Analog Stereo” |
Speaker Test
Speaker test can be started by invoking the API “startSpeakerDiagnosticsTest”.
API Name | ExotelWebClient.startSpeakerDiagnosticsTest |
|---|---|
Args | None |
Exported To | Application UI |
This starts a test by playing a ringtone. And as the ring tone gets played, the volume levels are passed through the callback function “diagnosticsKeyValueCallback” which can then be used to render the UI to show volume meter.
function diagnosticsKeyValueCallback(key, status, description) {
//key:“speaker”
//status: a floating point value
//description : "speaker ok"/”speaker error”
}Once the user response is captured, it can be passed back to the API, “stopSpeakerDiagnosticsTest '' as below. The responses are passed as arguments. THe
API Name | ExotelWebClient.stopSpeakerDiagnosticsTest |
|---|---|
Args | Optional, if present, “yes”/”no”. |
Exported To | Application UI |
The response “yes” indicates that the user has heard the speaker's sound. “No” indicates that the user did not hear the speaker's sound. This response is further used to update the troubleshooting logs.
If no arguments are passed, only the test is terminated. No updates would be made to troubleshooting logs.
Mic Test
Mic test can be started by invoking the API “startMicDiagnosticsTest”.
API Name | ExotelWebClient.startMicDiagnosticsTest |
|---|---|
Args | None |
Exported To | Application UI |
This starts a test by capturing the audio spoken on the mic. And as the audio is analyzed, the volume levels are passed through the callback function “diagnosticsKeyValueCallback” with key as “mic” which can then be used to render the UI to show mic volume meter.
function diagnosticsKeyValueCallback(key, status, description) {
//key:“mic”
//status: a floating point value
//description : "mic ok"/”mic error”
}
Once the user response is captured, it can be passed back to the API, “stopMicDiagnosticsTest '' as below. The responses are passed as arguments. THe
API Name | ExotelWebClient.stopMicDiagnosticsTest |
|---|---|
Args | Optional, if present, “yes”/”no”. |
Exported To | Application UI |
The response “yes” indicates that the user has been captured by the mic. “No” indicates that the user's voice could not be captured. This response is further used to update the troubleshooting logs.
If no arguments are passed, only the test is terminated. No updates would be made to troubleshooting logs.
Network Diagnostics
Network diagnosis can be started by invoking the API “startNetworkDiagnostics”.
API Name | ExotelWebClient.startNetworkDiagnostics |
|---|---|
Args | None |
Exported To | Application UI |
This API starts network operations testing. The callback “diagnosticsKeyValueCallback” is called with appropriate keys after each test completion.
Results arrive on the key/value callback:
Key | Status | Description |
|---|---|---|
wss | connected / disconnected | WSS URL |
userReg | registered / unregistered | userName |
tcp | connected / disconnected | ICE candidate line for TCP |
udp | connected / disconnected | ICE candidate line for UDP |
host | connected / disconnected | ICE candidate for host (local facing) |
srflx | connected / disconnected | ICE candidate for reflexive (remote facing) |
Web Socket Connection Callback
Returns a WSS url with status “connected” on successful connectivity.
function diagnosticsKeyValueCallback(key, status, description) {
// key:“wss”
// status = connected/disconnected
// description = WSS URL
}User Registration Status Callback
Returns a status “connected” on successful registration of the configured “username”. This is the callback from the background registration requests. No explicit registration requests are sent specifically for diagnostics purposes.
function diagnosticsKeyValueCallback(key, status, description) {
// key:“userReg”
// status - "registered"/"unregistered"
// description - userName
}TCP connectivity callback
Returns a key value “tcp”with ice candidate information as description.
function diagnosticsKeyValueCallback(key, status, description) {
// key:“tcp”
// status: connected/disconnected
// description : ice candidate line for tcp connectivity/empty string
}
UDP connectivity callback
Returns a key value “udp” with ice candidate information as description.
function diagnosticsKeyValueCallback(key, status, description) {
// key:“udp”
// key: connected/disconnected
// description : ice candidate line for udp connectivity/empty string
}Host connectivity callback
Returns a key value “host” with ice candidate information as description for internal network.
function diagnosticsKeyValueCallback(key, status, description) {
// key:“host”
// key: connected/disconnected
// description : ice candidate for the host connectivity (local facing)/empty string
}Reflexive connectivity callback
Returns a key value “srflx” with ice candidate information as description for external network.
function diagnosticsKeyValueCallback(key, status, description) {
// key:“srflx”
// key: connected/disconnected
// description : ice candidate for the reflex connectivity (remote facing)/empty string
}Auto Reconnect / Retry
The SDK retries registration on its own after a dropped connection — a transport failure, or a silent network drop that surfaces as a registration-expiry event. Enabled by default, at a fixed 5s interval, until it succeeds or auto-retry is turned off. No app-side retry loop is needed; RegisterEventCallBackjust reports each attempt as it happens.
API | Args | Description |
|---|---|---|
ExotelWebClient.enableAutoRetry | None | Turn auto-retry on (the default). Takes effect from the next DoRegister(). |
ExotelWebClient.disableAutoRetry | None | Turn auto-retry off, including a retry already scheduled for the current session. |
UnRegister() does not disable auto-retry — a later DoRegister()still retries on failure unless disableAutoRetry()was called explicitly.
Sometimes due to network issues, websocket connections get disconnected. In that case the application has to retry the connection. To implement it we can store the state for shouldAutoRetry, and during doRegistration it could be set as true, and during explicit unregistration it could be set as false, and based on an unregistered event we can invoke doRegister API.
var shouldAutoRetry = false;
function registerToggle() {
if (document.getElementById("registerButton").innerHTML === "REGISTER") {
shouldAutoRetry = true;
UserAgentRegistration();
} else {
shouldAutoRetry = false;
exWebClient.unregister();
}
}function RegisterEventCallBack(state, sipInfo) {
document.getElementById("status").innerHTML = state;
if (state === 'registered') {
document.getElementById("registerButton").innerHTML = "UNREGISTER";
} else {
document.getElementById("registerButton").innerHTML = "REGISTER";
if (shouldAutoRetry) {
exWebClient.DoRegister();
}
}
}Check SDK Readiness
- To check the SDK readiness, whether SDK is ready to receive a call or not. We can invoke checkClientStatus API with a callback method.
- First it checks if the microphone is available or not, then it checks whether the websocket is connected or not, then it checks if the user is registered or not.
Args | Datatype |
|---|---|
clientStatusCallback | Callback function with status as String |
Event | Event Decription |
|---|---|
media_permission_denied | either media device not available, or permission not given |
not_initialized | sdk is not initialized |
websocket_connection_failed | websocket connection is failing, due to network connectivity |
unregistered,terminated | either your credential is invalid or registration keepalive failed. |
initial | sdk registration is progress |
registered | Ready to receive the calls |
unknown | something went wrong |
disconnected | websocket is not connected |
connecting | Trying to connect the websocket |
exWebClient.checkClientStatus(function (status) {
console.log("SDK Status " + status);
});Audio Device Selection
- To get the device ID when the default device got changed, we can register callbacks
- registerAudioDeviceChangeCallback function Argument
-
Argument | type | |
|---|---|---|
audioInputDeviceChangeCallback | function | manadatory |
audioOutputDeviceCallback | function | mandatory |
onDeviceChangeCallback | function | optional |
- In case we dont pass onDeviceChangeCallback then sdk will internally try to change the default input/output device
- If onDeviceChangeCallback is passed as third argument then sdk will not try to change the default audio/input device internally, however, OS may have change the default device at OS level.
exWebClient.registerAudioDeviceChangeCallback(function (deviceId) {
console.log(`demo:audioInputDeviceCallback device changed to ${deviceId}`);
}, function (deviceId) {
console.log(`demo:audioOutputDeviceCallback device changed to ${deviceId}`);
});
- During the call or before the call, to change the audio output device
- You can optionally set the `forceDeviceChange` parameter to `true`. This action will bypass the system's internal auto-switching mechanisms.
exWebClient.changeAudioOutputDevice(
selectedDeviceId,
() => console.log(`Output device changed successfully`),
(error) => console.log(`Failed to change output device: ${error}`), true // optional
);- During the call or before the call, to change the audio input device
- You can optionally set the `forceDeviceChange` parameter to `true`. This action will bypass the system's internal auto-switching mechanisms.
function changeAudioInputDevice() {
const selectedDeviceId = document.getElementById('inputDevices').value;
exWebClient.changeAudioInputDevice(
selectedDeviceId,
() => console.log(`Input device changed successfully`),
(error) => console.log(`Failed to change input device: ${error}`),
true // optional
);
}- If you want the SDK to automatically detect and switch to newly plugged in audio input/output devices, you can enable this option by passing a 4th argument to `initWebrtc`.
exWebClient.initWebrtc(
sipAccountInfo,
RegisterEventCallBack,
CallListenerCallback,
SessionCallback,
true // optional: Enables auto audio device change handling
); Noise Suppression
- The SDK provides control over noise suppression for improved call quality. By default, noise suppression is disabled.
- Call after DoRegister().
API Name | Args |
|---|---|
exWebClient.setNoiseSuppression | Boolean (true / false) |
// Enable noise suppression
exWebClient.setNoiseSuppression(true);
// Disable noise suppression
exWebClient.setNoiseSuppression(false);
Logger Callback
To get the SDK logs item as a callback event we can register own logger. registerLoggerCallback is static methods in ExotelWebClient
RegisterLoggerCallback args
Args | Datatype |
|---|---|
type | string |
message | String |
args | Array |
exotelSDK.ExotelWebClient.registerLoggerCallback(function (type, message, args) {
switch (type) {
case "log":
console.log(`demo: ${message}`, args);
break;
case "info":
console.info(`demo: ${message}`, args);
break;
case "error":
console.error(`demo: ${message}`, args);
break;
case "warn":
console.warn(`demo: ${message}`, args);
break;
default:
console.log(`demo: ${message}`, args);
break;
}
});Audio Volume Control
- The SDK provides granular control over different audio elements including call audio, ringtone, ringback tone, DTMF tone, and beep tone volumes.
- Volume values are normalized between 0.0 (silent) and 1.0 (maximum)
- Volume settings persist during the session but reset when the page is reloaded
- Since the notifications are global, these functions are static
Notification Audio Volume Control
- Methods to control audio output volume for notification sounds.
API Name | Args | Returns | Description |
|---|---|---|---|
ExotelWebClient.setAudioOutputVolume | - audioElementName (string) - value (number 0.0-1.0) | None | Set notification sound volume |
ExotelWebClient.getAudioOutputVolume | - audioElementName (string) | Current volume (number 0.0-1.0) | Get notification sound volume |
- Valid audioElementName values:
- "ringtone" - Incoming call ringtone
- "ringbacktone" - Outgoing call ringback tone
- "dtmftone" - DTMF keypad tones
- "beeptone" - System beep sounds
// eg: Set ringtone volume to 50%
exotelSDK.ExotelWebClient.setAudioOutputVolume("ringtone", 0.5);
// Get current ringtone volume
const volume = exotelSDK.ExotelWebClient.getAudioOutputVolume ("ringtone");Call Audio Volume Control
- Methods to control audio output volume for call audio.
- Since this method is call volume per account, this function is not static
API Name | Args | Returns | Description |
|---|---|---|---|
exWebClient.setCallAudioOutputVolume | - value (number 0.0-1.0) | None | Set call audio volume |
exWebClient.getCallAudioOutputVolume | - None | Current volume (number 0.0-1.0) | Get call audio volume |
// eg: Set call audio volume to 80%
exWebClient.setCallAudioOutputVolume(0.8);
// Get current call audio volume
const callVolume = exWebClient.getCallAudioOutputVolume();WebSocket disconnect event
When the SIP WebSocket connection drops unexpectedly, the SDK calls SessionCallbackwith callState === "websocket_disconnected".
Param | Type | Description |
|---|---|---|
callState | String | "websocket_disconnected" |
phone | String | Same phonevalue as the other SessionCallbackstates |
error | Object | { message, code }, described below |
When it fires
- Only when the connection closes unexpectedly: a network drop, a server close or a failed connect.
- It does not fire when your app tears the connection down itself with UnRegister()(since 3.0.16).
- The SDK reconnects on its own. With auto-retry on (the default, see 6.14), it calls DoRegister()again every 5 seconds until registration succeeds. Each attempt is reported on RegisterEventCallBack, and each failed attempt can fire this event again.
- A silent network outage may not close the socket, so this event may never fire. The SDK notices when registration expires and reports unregisteredon RegisterEventCallBackinstead. Auto-retry covers both cases.
- The event is for UI and diagnostics. Use error.codeto decide whether to let the retries continue, or to call disableAutoRetry()and show an error when retrying won't help.
The errorobject
Field | Type | Description |
|---|---|---|
message | String | WebSocket closed <server URL> (code: <n>). If no socket could be opened at all, this is the browser's error text instead. |
code | Number or null | The WebSocket close code, read from message. nullwhen there is no close code (see the last row below). |
Close codes
Close codes come from the browser's WebSocket CloseEvent, as defined in RFC 6455 §7.4. The SDK does not define codes of its own. For security reasons, browsers hide the reason a connection could not be opened, so most connect-time failures show up as 1006.
Transient means auto-retry will usually recover the connection, so just show a "reconnecting" state. Stop retrying means the next attempt will fail the same way: call disableAutoRetry()and show an error.
Code | Name | Likely cause | Type | Recommended action |
|---|---|---|---|---|
1006 | Abnormal closure | The most common code. The connection was lost without a close handshake: a network drop, a Wi-Fi or VPN switch, the device waking from sleep, a proxy or firewall blocking wss, a TLS or certificate error, DNS failure, or the server being unreachable. | Transient | Let auto-retry run. If it fails on every attempt from the start, check that the WebSocket host is reachable on port 443 from the user's network. |
1000 | Normal closure | Usually the SDK's own connect timeout: if the server does not accept the connection within 5 seconds, the SDK closes the socket with 1000. Can also be a clean close from the server. | Transient | Let auto-retry run. If it keeps happening, the network is slow or is blocking the WebSocket host. |
1001 | Going away | The server is shutting down or restarting, or the browser is unloading the page. | Transient | Let auto-retry run. |
1005 | No status received | The server closed the connection without sending a code. | Transient | Treat as a drop and let auto-retry run. |
1011 | Internal error | A server-side fault. | Transient | Let auto-retry run. Contact Exotel support if it persists. |
1012 | Service restart | The server is restarting. | Transient | Let auto-retry run. |
1013 | Try again later | The server is temporarily overloaded. | Transient | Let auto-retry run. |
1008 | Policy violation | The server rejected the connection by policy. | Stop retrying | Check the account and SIP configuration with Exotel support. |
1002, 1003, 1007, 1009, 1010 | Protocol errors | A malformed, unsupported or oversized message, or a failed extension negotiation. These are unexpected between the SDK and Exotel's servers. | Stop retrying | Report to Exotel support with SDK logs. |
1015 | TLS handshake failure | The TLS handshake failed. Most browsers report this as 1006instead. | Stop retrying | Check for a TLS-intercepting proxy or a certificate problem. |
null | — | No socket was opened, for example because the WebSocket URL is malformed. messageholds the browser's error. | Stop retrying | Fix the configuration. |
Ring tone control
The SDK plays a ring tone on an incoming call. Since v3.0.13/3.0.14 the duration is configurable and the application can take over when the ring starts.
All six are instance methods, not static, and are available only after initWebrtchas run. Called before that, the setters log a warning and return false, and the getters return the defaults (30 and true).
API | Args | Returns | Description |
|---|---|---|---|
exWebClient.setRingingDuration | seconds (number or numeric string) | true on success, falseif rejected | How long the ring tone plays. Default 30 sec. |
exWebClient.getRingingDuration | None | number (seconds) | Configured ringing duration. |
exWebClient.setRingToneAutoStart | enabled (boolean, or "true"/"false") | true on success, falseif rejected | Whether the SDK auto-plays the ring tone on an incoming session. Enabled by default. |
exWebClient.getRingToneAutoStart | None | boolean | Current auto-start setting. |
exWebClient.startRingTone | None | None | Start the ring tone. Never blocked by setRingToneAutoStart(false). |
exWebClient.stopRingTone | None | None | Stop the ring tone and cancel its auto-stop timer. |
Shared process-wide, not per account. The ring tone audio element and its state (duration and auto-start) are global. In a multi-account setup, setting them on one ExotelWebClientapplies to every account in the page, and one stopRingTone()stops the single shared ring.
Ringing duration
The default is 30 seconds; earlier versions capped the ring at a hardcoded ~15 seconds.
A number or a numeric string is accepted, which helps when the value comes from a config blob or an input field. Any other type (true, [5], "", null, non-numeric text) and any value <= 0is rejected: the call returns false, logs an error, and the previous duration is kept.
Changing the duration mid-ring reschedules the auto-stop against time already spent ringing — it can shorten a ring in progress but never extend it past the new total. A duration shorter than the elapsed ring time stops the ring immediately.
Starting and stopping
The SDK starts the ringtone automatically on an incoming SIP INVITE. You can also drive it yourself, for example to align ringing with your own UI or push-notification sync.
You do not need stopRingTone()on the normal transitions: the SDK stops the ring itself when the call is answered or terminated (answered, hung up, or cancelled by the caller), and in any case it stops once the configured duration elapses. stopRingTone()is for stopping earlier on an application signal — gating logic deciding a call is stale, or the UI dismissing it. It is safe to call when nothing is ringing.
A browser may reject the first play()on a page that has not yet seen a user gesture. The SDK retries every 500 ms until the ring starts or is stopped, so a call arriving before any interaction still rings once the page becomes eligible.
Gating the automatic ring
By default the SDK rings as soon as an incoming session arrives. An app that must first reconcile its own state — say, waiting for a push notification and the SIP INVITE to agree on the same AppServer call — disables auto-start and rings when ready. Disabling auto-start never blocks an explicit startRingTone(); it governs only the automatic start.
Booleans and the strings "true"/"false" (case-insensitive) are accepted — config layers commonly supply strings. Any other value is rejected and the previous setting kept.
Disabling Built-in Logging
- The SDK provides a way to control all built-in logging (console output, SDK logs, and SIP.js logs) using the setEnableConsoleLogging method.
- setEnableConsoleLogging is static methods in ExotelWebClient:
// Disable all SDK and SIP.js logs
exotelSDK.ExotelWebClient.setEnableConsoleLogging(false);- Default: Logging is enabled (true).
- Effect: When set to false, all SDK logs, SIP.js logs, and internal logger callbacks are suppressed.
- Note: This should be called before initializing or registering the client to ensure no logs are printed.
Messages — sipAccountInfo reference
Field | Type | Description |
|---|---|---|
authUser | String | SIP username to register |
userName | String | Unique map index for phones; same as authUser |
displayName | String | Local display name on the dialer |
secret | String | SIP password |
sipdomain | String | SIP public domain |
security | String | "wss" / "ws"— typically "wss" |
port | String | 443 for WebSockets |
sipUri | String | Complete SIP URI (constructed internally; leave blank) |
contactHost | String | IP address to contact back (found via STUN; leave blank) |
Integration with Exotel APIs
Refer to https://developer.exotel.com/api/make-a-call-api for exotel platform integration APIs. For example, to make a outbound call from a webclient to a pstn phone the request will be
curl -s -X POST https://<your_api_key>:<your_api_token><subdomain>/v1/Accounts/<your_account_sid>/Calls/connect -d "From=<sip_user_id>" -d "CallerId=<caller_id>" -d "To=<phone number>"