Voice SDKs
Client IOS SDK
Integration Guide
61 min
introduction exotelvoice library enables you to add the voip calling feature into your app this document outlines the integration steps the library supports only peer to peer 2 way calls multi party conferencing use cases are not supported section 4 describes integration steps for exotelvoice ios sdk section 5 1 describes the webhooks that should be supported in your application backend for number masking workflow section 5 2 1 describes subscribers provisioning in the exotel platform licensing create a trial account in exotel to enable voip calling in the trial account, contact exotel support once exotel support enables the voip capability in the account , a voip exophone will be created as shown below and available in the account under the exophones section sip exophones discussed later in the section refers to exophones of voip type as shown above note sip and voip are used interchangeably in the document glossary terminology description app mobile application application backend customer backend service for the application client user / subscriber / client signing up to use the mobile app customer exotel’s customer licensing the sdk voip voice over ip ios sdk integration getting started software package the exotelvoice sdk software package includes ios exotelvoice framework zip file of the sdk integration guide sample application source code for reference exotel voice sample zip doc for sdk api reference exotel ios voice sample doc zip subscriber management api documentation subscriber management api add exotel voice sdk library to project configure exotelvoice framework unzip exotelvoice framework zip copy exotelvoice framework folder under your project root directory target platform supported ios version ios 10+ supported ios sdk arch arm64 permissions record permission notification permission proguard rules not applicable ios service the application needs to integrate exotelvoice in a service this should be run as a foreground service when a call is in progress so that the application is not killed when it moves to the background when the call is over, service should be moved to the background refer to voiceappservice in the sample application initialize library exotelvoice interface class provides an entry point to initialize the voice library and set it up for handling calls // get exotel voice client exotelvoiceclient = exotelvoiceclientsdk getexotelvoiceclient() //set the event listener for sdk logs exotelvoiceclient? seteventlistener(eventlistener self) // set listener to handle events from voice client extension voiceappservice exotelvoiceclienteventlistener { // initialization success handler public func oninitializationsuccess() { } // initialization failure handler public func oninitializationfailure(error exotelvoiceerror) { } /// indicates de initialization of the sdk public func ondeinitialized(){ } // logs reported by the voice library public func onlog(level loglevel, tag string, message string) { } // log upload success handler public func onuploadlogsuccess() { } // log upload failure handler public func onuploadlogfailure(error exotelvoiceerror) { } // authentication failure handler public func onauthenticationfailure(error exotelvoiceerror) { } } sdk initialization requires a subscribertoken generated by the exotel platform workflow for this token generation and management is described in section authentication and authorization // request token from application backend (example) string subscribertoken = getaccesstoken(); the subscribertoken is provided to exotelvoice during initialization // initialize client library let content = \["deviceid" uidevice current identifierforvendor? uuidstring, "devicetype" "ios"] exotelvoiceclient? initialize( context content as \[string any],// ios context hostname hostname, // exotel voice platform host url subscribername subscribername, // to generate token for subscriber displayname displayname, // display name of the subscriber accountsid accountsid, // exotel account sid subscribertoken subscribertoken // token fetched from exotel platform ) client library notifies oninitializationsuccess() on success and oninitializationfailure() on failure on expiry of subscribertoken , the library will post onauthenticationfailure() at which application should request new subscribertoken from application backend and program in the initialize() api parameter description hostname https //miles apac sg exotel in/v2 subscribername param “subscriber name” returned as part of the subscriber management api to create a subscriber displayname subscriber name accountsid account sid param from api settings page in the exotel dashboard subscribertoken can be gotten from the api in the `get subscriber token` section in the subscriber management api document in the subscribertoken , both the refresh token and access token are base64 encoded subscriber token format example "subscriber token" { "refresh token" "eyjhbgcioijiuzi1niisinr5cci6ikpxvcj9 eyjpc3mioijleg90zwwilcjzdwiioijbcmnoaxqilcjpyxqioje1nzy2ndc5otksimv4cci6mtu3njc0nzk5oswiy2xpzw50x2lkijoinuuwntg2nuuifq hc3umvfflkipij8r9kcp9o9he9he51le08ro22u7eqs", "access token" "eyjhbgcioijiuzi1niisinr5cci6ikpxvcj9 eyjpc3mioijleg90zwwilcjzdwiioijbcmnoaxqilcjpyxqioje1nzy2ndc5otksimv4cci6mtu3njc0nzk5oswic2nvcguioij2b2ljzsisimnsawvudf9pzci6ijvfmdu4njvfin0 uqd ahgbit4xqtpfl 567dmjqrklmltpag0kpgb2qma" } below are the sample methods to get the subsribertoken, customers can implement this in their own method use the below http apis to get the subscriber token and convert subscriber token to jobject and pass it to exotelvoiceclient initialize(); note copying manually generated subscriber token has issues best way is to make api calls in source code and pass them by converting to jobject to the exotelvoiceclient initialize(); pass the proper device id to the login api to avoid invalid refresh token error device id is the actual device id in which the app is running using below sample code "device id" uidevice current identifierforvendor? uuidstring generate access token refer “subscriber management api” documentation section 3 1 for creating subscriber token stopping sdk to de initialize the sdk, exotelvoiceclient exposes stop api to de initialize the sdk exotelvoiceclient? stop(); when sdk gets de initialized, client sdk will send callback ondeinitialized() managing calls after successful initialization, the library is ready to handle calls dialing out calls or receiving incoming calls is managed through callcontroller interface // get call controller interface callcontroller = self exotelvoiceclient? getcallcontroller() call progress events are notified to the application through the calllistener interface attached to the callcontroller object // attach call listener extension voiceappservice calllistener { // handler for incoming call alert public func onincomingcall(call call) { } // handler for outgoing call initiated event public func oncallinitiated(call call) { } // handler for call ringing event for outgoing call public func oncallringing(call call) { } // handler for call connected event public func oncallestablished(call call) { } // handler for call termination event public func oncallended(call call) { } // handler for missed call alert public func onmissedcall(remoteid string, time date) { } } each call object provides a call interface to manage the call lifecycle and perform operations like answer incoming calls, hangup calls, mute, unmute, and get call statistics // mute mcall? mute() // terminate call mcall? hangup() make outgoing call to make outgoing calls, the callcontroller interface must be created and a listener interface is attached as mentioned in section managing calls provide sip exophone number in dial() api of callcontroller interface to make outgoing calls // get call controller interface self callcontroller = self exotelvoiceclient? getcallcontroller() // attache call progress listener self callcontroller? setcalllistener(calllistener self) // dial out call to remoteid call = callcontroller? dial(remoteid \<value>, message \<value>) params for dial method param mandatory description remoteid yes exotelvoice library always routes the call via sip exophone configured for your account sip exophones are different from the pstn / mobile exophones they can be identified using sip prefix contact exotel support to enable sip exophone in your account similar to the number masking workflow, the application backend must maintain the call context between caller and callee on receiving the call at sip exophone, the exotel platform will request the callee details from the application backend to bridge the call refer section dial whom endpoint for details message no the string that will be passed here will be sent to the dialwhom endpoint under the “customfield” param all alphanumeric characters are allowed along with comma `,` colon ` ` and all kinds of brackets {}()\[] once initiated, call progress events can be monitored through calllistener interface and call control operations can be performed using call interface after dialing, the application can use hangup() api on the call object to cancel / terminate outgoing calls // cancel / terminate call mcall? hangup() receive incoming call the mobile application receives incoming call data in push notification sent by your application backend refer section push notification endpoint for details to handle the incoming call, the exotelvoice library should be in an initialized state refer to section initialize library after initialization, the callcontroller interface should be created and the listener be attached as mentioned in section managing calls // get call controller interface self callcontroller = self exotelvoiceclient? getcallcontroller() // attache call progress listener self callcontroller? setcalllistener(calllistener self) the sample application uses firebase cloud messaging to push call data to the device once received at the mobile app, it is sent to exotlevoiceclient through relaysessiondata api extension appdelegate unusernotificationcenterdelegate { func application( application uiapplication, didreceiveremotenotification userinfo \[anyhashable any ], fetchcompletionhandler completionhandler @escaping (uibackgroundfetchresult) > void) { // decode json and build session data structure sessiondata\["payload"] = payload sessiondata\["payloadversion"] = payloadversion sessiondata\["subscribername"] = userid exotelvoiceclient? relaysessiondata(payload sessiondata) } the voice library will process and validate the data and send an onincomingcall() event to the application with the call object the application can call the answer() api to accept the incoming call // accept call mcall? answer() the application can call hangup() api on the call object to decline an incoming call // decline / terminate call mcall? hangup() call kit integration if your app is integrated with call kit, refer to the following lifecycle diagram to ensure there are no conflicts user interaction on the left side, we see a user icon representing interactions with a sample app the user initiates actions like “click dial” for outgoing calls and “accept call” or “reject call” for incoming ones pre requisites callkit uuid will manage by your app private static var activeuuid uuid? cxproviderdelegate is subscriber by app to handle the call kit actions let providerconfiguration = cxproviderconfiguration(localizedname "exotel sample app") providerconfiguration maximumcallspercallgroup = 1 providerconfiguration supportsvideo = false providerconfiguration supportedhandletypes = \[ phonenumber] providerconfiguration ringtonesound = "ringtone aif" if let iconimage = uiimage(named "appicon") { providerconfiguration icontemplateimagedata = iconimage pngdata() } provider = cxprovider(configuration providerconfiguration) callcontroller = cxcallcontroller() providedelagete = providerdelegate(provider provider!) provider! setdelegate(providedelagete, queue nil) here providerdelegate is custom class which has implemented the cxproviderdelegate outgoing calls start outgoing calls by calling dial() api of exotel sdk callcontroller? dial(remoteid destination, message message) then after make sure to call the start transaction with cxstartcallaction (using the uuid) let handle = cxhandle(type phonenumber, value destination) activeuuid = uuid() let startcallaction = cxstartcallaction(call activeuuid!, handle handle) startcallaction isvideo = video let transaction = cxtransaction() transaction addaction(startcallaction) requesttransaction(transaction) call kit ui will start in background call kit delegate method for action cxstartcallaction will get executed func provider( provider cxprovider, perform action cxstartcallaction) { setactiveuuid(uuid action calluuid) action fulfill() } when sdk send oncallringing() callback to app, app will start call kit timer for ringing after updating the status to ringing state provider? reportoutgoingcall(with activeuuid!, startedconnectingat nil) timer will start in call kit ui incoming calls when relay session data (push notification data) triggers an incoming call notification, the callkit ui should be set up to display the incoming call with options to accept or reject users should be provided with the option to accept the call they can choose to accept the call using the function reportincomingcall class func reportincomingcall(uuid uuid, handle string, hasvideo bool = false, completion ((error?) > void)? = nil) { // construct a cxcallupdate describing the incoming call, including the caller let update = cxcallupdate() update remotehandle = cxhandle(type phonenumber, value handle) update hasvideo = hasvideo // report the incoming call to the system provider! reportnewincomingcall(with uuid, update update) { error in / only add an incoming call to an app's list of calls if it's allowed, i e , there is no error calls may be denied for various legitimate reasons see cxerrorcodeincomingcallerror / if let error = error { voiceapplogger error(tag callkitutils tag, message "error requesting transaction \\(error)") } else { voiceapplogger info(tag callkitutils tag, message "requested transaction successfully ") } } } upon choosing to accept the call, the system should invoke the cxanswercallaction to answer the call func provider( provider cxprovider, perform action cxanswercallaction) { voiceapplogger info(tag tag, message "cxanswercallaction") voiceappservice shared answer() // signal to the system that the action was successfully performed action fulfill() } alternatively, users should also be provided with the option to reject the call they can choose to reject the call using the cxendcallaction func provider( provider cxprovider, perform action cxendcallaction) { voiceapplogger info(tag tag, message "cxendcallaction") do { try voiceappservice shared hangup() } catch let error { voiceapplogger debug(tag tag, message "error \\(error localizeddescription)") } // signal to the system that the action was successfully performed action fulfill() } connected calls once a call is established, its status is updated in the ui the timer is started in the call kit once the call is connected ending calls both the sample app and the callkit ui can initiate the action when the user ends the call, the sample app starts a transaction with cxendcallaction (using the uuid) function hangup() is called public func hangup() throws { if (nil == mcall) { let message = "call object is null" voiceapplogger error(tag tag, message message) throw voiceapperror(module tag, localizeddescription message) } do { try mcall? hangup() } catch { voiceapplogger error(tag tag, message "exception in call hangup with callid \\(string(describing mcall? getcalldetails() getcallid()))") } } the home view updates after ending a call pushkit integration pushkit is used to manage voip call notifications and handle incoming call events with callkit initializing pushkit the pushkit registry is initialized inside the appdelegate class within the didfinishlaunchingwithoptions method var pushregistry pkpushregistry = pkpushregistry(queue dispatchqueue main) pushregistry delegate = self pushregistry desiredpushtypes = \[ voip] implementing pkpushregistrydelegate an extension of the appdelegate class conforms to the pkpushregistrydelegate protocol to handle pushkit related events extension appdelegate pkpushregistrydelegate { func pushregistry( registry pkpushregistry, didupdate pushcredentials pkpushcredentials, for type pkpushtype) { guard type == voip else { return } } func pushregistry( registry pkpushregistry, didreceiveincomingpushwith payload pkpushpayload, for type pkpushtype, completion @escaping () > void) { completion() } } receiving the pushkit token the pushregistry( didupdate\ for ) method is triggered upon app launch or whenever the pushkit token changes this token is unique for the device and must be sent to the backend extension appdelegate pkpushregistrydelegate { func pushregistry( registry pkpushregistry, didupdate pushcredentials pkpushcredentials, for type pkpushtype) { guard type == voip else { return } let token = pushcredentials token map { string(format "%02x", $0) } joined() if token isempty { voiceapplogger debug(tag tag, message "pushkit token is not generated yet!!") return } voiceapplogger debug(tag tag, message "pushkit token \\(token)") userdefaults standard set(token, forkey userdefaults keys firebasetoken rawvalue) } } handling incoming push notifications the pushregistry( didreceiveincomingpushwith\ for\ completion ) method is triggered when the app receives a pushkit notification it processes the payload and triggers the callkit ui to display the incoming call screen func pushregistry( registry pkpushregistry, didreceiveincomingpushwith payload pkpushpayload, for type pkpushtype, completion @escaping () > void) { voiceapplogger debug(tag tag, message "voip push received") applicationutils checkmicrophonepermission { isenabled in guard isenabled else { userdefaults standard set("false", forkey userdefaults keys isloggedin rawvalue) completion() return } } applicationutils checknotificationspermission { isenabled in guard isenabled else { userdefaults standard set("false", forkey userdefaults keys isloggedin rawvalue) completion() return } } guard let payloaddict = payload dictionarypayload as? \[string any], let callerid = payloaddict\["subscribername"] as? string else { voiceapplogger error(tag tag, message "invalid payload format or missing callerid") completion() return } voiceappservice shared pushnotificationdata = payloaddict let validatelogin = userdefaults standard string(forkey userdefaults keys isloggedin rawvalue) ?? "false" guard validatelogin != "false" else { voiceapplogger error(tag tag, message "user is not logged into app") completion() return } callkitutils displayincomingcall(handle callerid) completion() } answering the call when the user accepts the call via the callkit ui by tapping the accept button, the provider( perform\ cxanswerallaction) method in the cxprovider delegate is triggered this method initializes the audio session and call the sendpushnotificationdata() method func provider( provider cxprovider, perform action cxanswercallaction) { voiceapplogger info(tag tag, message "cxanswercallaction") guard let payload = voiceappservice shared pushnotificationdata, let userid = payload\["subscribername"] as? string, let payloadversion = payload\["payloadversion"] as? string, let payloaddata = payload\["payload"] as? string else { voiceapplogger error(tag tag, message "missing data in payload") return } voiceapplogger debug(tag tag, message "handling the notification on answer button tap") self startaudiosessionifneeded() dispatchqueue main asyncafter(deadline now()) { voiceappservice shared sendpushnotificationdata( payload payloaddata, payloadversion payloadversion, userid userid ) } action fulfill() } activating the audio session calling the startaudiosessionifneeded() method initializes and activates the avaudiosession to ensure it is correctly configured for voice call functionality it uses the avaudiosession framework to manage audio behaviors func startaudiosessionifneeded() { do { let session = avaudiosession sharedinstance() try session setcategory( playandrecord, mode voicechat, options \[ duckothers, allowbluetooth, defaulttospeaker]) try session setactive(true) } catch { voiceapplogger error(tag tag, message "error starting audio session \\(error)") } } incoming call management after receiving an incoming call notification and displaying the callkit ui, the app handles the call by playing a ringtone and invoking the answer() method to accept the call this is managed within the onincomingcall function public func onincomingcall(call call) { voiceapplogger debug(tag tag, message "incoming call received, callid \\(call getcalldetails() getcallid()) remoteid \\(call getcalldetails() getremoteid())") toneplayback playringtone() mcall = call dispatchqueue main asyncafter(deadline now() + 1) { self answer() } } audio management exotelvoice can detect the change in audio output selection default audio output mode is the earpiece when a wired headset is plugged in or a bluetooth headset is paired with a phone, it automatically routes audio over a new route speaker phone mode can be enabled using call object apis // enable speaker mode mcall? enablespeaker() // disable speaker mode mcall? disablespeaker() local mic can be muted and unmuted during in call using call object apis // enable speaker mode mcall? mute() // disable speaker mode mcall? unmute() bluetooth mode can be enabled and disabled using call object apis this will only work when any bluetooth device is connected to the phone /// enables bluetooth mode for the call mcall? enablebluetooth() /// disables bluetooth mode for the call mcall? disablebluetooth() call state the call state can be accessed by calling getlatestcalldetails() getcallstate() this state can be displayed on the calling screen to let the user know about the state of the call refer to the call state flow diagram call state description call back recommended message to display on the calling screen none this state occurs when a call object is created but not yet initiated outgoing initiated this state is set when an outgoing call is initiated it’s set after dialing and before the call starts ringing connecting early indicates early media reception before the call is answered ringing the receiver’s phone is ringing but hasn’t been answered yet oncallringing() ringing connecting the call starts connecting connecting incoming this state is set when there’s an incoming call, and the system isn’t idle onincomingcall() caller id, custom information related to the callee answering occurs when the user accepts the incoming call; it’s a transitional phase before the call gets established answering established indicates that both parties have accepted the call, and communication is established oncallestablished() connected media disrupted occurs if there’s a disruption in media transmission during an ongoing call due to issues like rtp loss to handle such disruptions effectively ensuring continuity or termination based on severity it initiates reconnection procedures and moves to reconnecting if rtp loss persists to ensure calls remain active during temporary disruptions it monitors rtp resumption or port renewal activities and returns to established upon successful media restoration onmediadisrupted() reconnecting renewing media this state indicates that media (rtp) is resuming or renewing after being disrupted onrenewingmedia() reconnecting ending the process of ending or dropping an ongoing or established call starts here call statistics application can query the call quality statistics periodically during the call by using getstatistics() api of the call object it provides info about codec and the call qos parameters like packet loss, jitter and round trip delay call details application can query the call details record of the call using getcalldetails() api of the call interface the cdr provides info on callid, call state, call duration, call end reason etc this api can be queried only for the latest call since sdk maintains only a single call context handling of pstn calls exotelvoice has following behavior during pstn calls when a pstn call is in progress, no outgoing calls are allowed by the voice client when a pstn call is in progress, any incoming voip call is automatically declined by the voice library and reported as a missed call to the application when a voip call is in progress and a pstn call lands on the phone, then the voip call continues if the user declines or does not respond to the pstn call if the user accepts the pstn call, then the voip call is automatically terminated by the voice client handling of pstn calls when call kit is integrated exotelvoice has following behavior during pstn calls when a pstn call is in progress, no outgoing calls are allowed by the voice client the following scenarios will the handled by the call kit instead of the sdk when a pstn call is in progress and a voip call is incoming when a voip call is in progress and a pstn call lands on the phone for these scenarios, call kit provides options to the end user to either reject and accept the incoming call or hold and accept the incoming call reporting problems the voice client library logs are stored in the application internal storage any issue along with the logs can be reported by app to exotel using uploadlogs() api of exotelvoice interface // upload logs with description from startdate and enddate exotelvoiceclient? uploadlogs(startdate startdate, enddate enddate, description description) the api triggers onuploadlogsuccess() or onuploadlogfailure() callback based on the success or failure of the operation reporting call quality feedback call quality can be queried from the user and reported to the exotel platform at the end of the call // upload logs with description from startdate and enddate int rating = 3 callissue issue = background noise mpreviouscall? postfeedback(rating rating, issue issue) the rating is a quality score that can take values 1 to 5 5 excellent quality no issues 4 good quality negligible issues 3 average quality minor audio noise 2 bad quality frequent choppy audio or high audio delay 1 terrible unable to communicate call drop, no audio or one way audio the call issue is a descriptive explanation of the issue no issue no issues observed background noise low audio clarity due to noisy audio choppy audio frequent breaks in audio or garbling in audio high latency significant delay in audio no audio no audio received from far user echo echo during the call platform integration customer api endpoints exotel provides full control to customers to implement call routing business logic for this, customers need to host the following http endpoints to handle callbacks from exotel platform dial whom endpoint host a http endpoint which will be queried by the exotel platform to get to the destination user method get request url (example) https //company com/v1/accounts/exotel/dialtonumber note this url needs to be provided to exotel or configured in the connect applet request body callsid unique call identifier callfrom caller username callto exophone customfield it will be passed only if you have sent the second optional param while making the call from the app expected response on success, the api should return with response code 200 ok the response body should be a string of type sip \<remoteid> “sip ” tag is needed to hint the exotel platform to connect calls over voip at present, sip and pstn intermixing is not supported any other response is treated as failure remoteid is the username with which the call destination subscriber was registered with exotel platform example request get /v1/accounts/exotelip2ipcalling1/dialtonumber?callsid=743ddcf5a0552050bc37b6d0ff9613bn\&callfrom=sip\ alice\&callto=sip 08040408080\&callstatus=ringing\&direction=incoming\&created=sat,+23+nov+2019+22 00 37\&dialcallduration=0\&starttime=2019 11 23+22 00 37\&endtime=1970 01 01+05 30 00\&calltype=call attempt\&dialwhomnumber=\&flow id=249196\&tenant id=113828\&from=sip\ alice\&to=sip 08040408080\¤ttime=2019 11 23+22 00 37 response { "fetch after attempt" false, "destination" { "numbers" \[ "sip 1234567890" ] }, "outgoing phone number" "08080808080", "record" false, "recording channels" "dual", "max ringing duration" 30, "max conversation duration" 3600, "request id" "2e48100e6b474714b1a64bfa9f5b7a55", "method" "get", "http code" 200, "code" null, "error data" null, "status" null } push notification endpoint exotel implements registration less dialing where a user need not periodically register with sip registrar for receiving incoming calls instead the call details are pushed to the device as push notification using which call can be established this method is beneficial as calls will reach the user even when the app is not in foreground or swipe killed it saves battery since it does not need to keep persistent voip connection when idle exotel will provide call data to this endpoint that needs to be pushed to the client device from your application backend note headers are not supported in the notify endpoints method post request url (example) https //\<your api key> \<your api token>@company com/v1/accounts/exotel/pushtoclient note this url needs to be configured in exotel platform request body subscribername name with which subscriber registered with exotel platform payload call data which should be passed to the client sdk payloadversion version of payload scheme expected response on success, the api should return with response code 200 ok any other response is treated as failure exotel api endpoints subscriber management customers can manage their subscribers in the exotel platform using the apis listed “subscriber management api” document for clients to be able to use voip calling features they need to be added as subscribers under your account there are two ways in which your client registration with exotel account happens, pre provisioning the customer pre configures the client accounts even before the app is installed by the client refer to the “subscriber management api” document for details on creating client accounts dynamic provisioning a client account is created after the app is installed by the user and signs up with the application backend refer to authentication and authorization for workflow details once client provisioning happens, clients can be managed using subscriber management apis authentication and authorisation access to the exotel platform by exotelvoice is authenticated using a set of bearer tokens subscriber token the application backend must obtain these tokens from the exotel platform and provide them to the client on request refer to the “subscriber management api” document for details on receiving the onauthenticationfailure event, the application should request a new subscriber token from its backend and reinitialize the sdk as shown in section initialize library contact exotel support if you get onauthenticationfailure even after token renewal onauthenticationerror() event provides following error types authentication invalid token token parameters are invalid authentication expired token token expired reference documents ios javadoc for the exotelvoice library api subscriber management api document for details on api to manage subscriber support contact please write to hello\@exotel in for any support required with integration troubleshooting guide outgoing call i am not getting any requests on the “dial whom” endpoint for outgoing calls? check if “dial whom” url was configured in connect applet and reachable check if the call is listed in your account dashboard if not, then check if the phone has internet connectivity check if the sip exophone number was set in callcontroller dial() api check if c alllistener oncallfailed() event was received with error type congestion error, which means rate limit quota was exceeded check if calllistener oncallinitiated() event was received from the sdk check if call getcalldetails() returns non null sessionid field check if report the issue to exotel support with sessionid (from app) and call reference id (if available, from account dashboard) i am hearing a ringing tone but the callee has not received the call? ringing tone implies that the call has reached the exotel platform incoming call my calls are not landing on the callee app? check if the call is listed in your account dashboard and dialout happened to the remote subscriber if not then, refer to the troubleshooting guide for outgoing calls check if the application received push notification with call payload from your application backend if not, then check the device logs check if push notification url is reachable and configured in the exotel platform check if push notification url got the call payload from exotel platform check if the call payload was sent to the target device from your backend if the call notification is received in the app, then check if the library is initialized using exotelvoiceclient? isinitialized() check if payload received in push notification was programmed in the library through exotelvoiceclient? relaysessiondata() report the issue to exotel support with call reference id (from account dashboard) authentication requesting subscriber token from exotel platform giving 4xx response? response code troubleshooting 400 malformed packet check subscriber name format it should be an alphanumeric string of size less than 16 characters 401 it requires basic authentication using api key\ token 403 voip calling is not enabled in your account contact exotel support why is sdk reporting onauthenticationfailure response for a subscriber token received from exotel platform? check the errortype in response if it is, authentication invalid token invalid token initialize new token authentication expired token token expired initialize new token contact exotel support if the issue persists
🤔
Have a question?
Our knowledgeable support team and an awesome community will get you an answer in a flash.
To ask a question or participate in discussions, you'll need to authenticate first.