---
title: Integration Guide
slug: voice-apis/1O2p-integration-guide
docTags: 
createdAt: 2025-10-03T04:57:10.591Z
---

## *Introduction*

ExotelVoice flutter SDK 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.

## *Licensing&#x20;*

Create a trial [account](https://my.exotel.com) 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.

![](https://api.archbee.com/api/optimize/SK2urIgCC2ml99YyqWOSl/csGk37kqH5Vc7BmY_F6Jp_.blob)

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**

## *Flutter  SDK Integration&#x20;*

### SDK Architecture&#x20;

&#x20; &#x20;

![](https://api.archbee.com/api/optimize/SK2urIgCC2ml99YyqWOSl/IisqYDk6oSEfcVOM2iU6b_upload.png)

### Getting Started&#x20;

***Software Package***

- SDK (tar.gz file) includes android , ios, lib directories &#x20;
- integration guide
- sample app for reference

***Add SDK to your project***

1. download the SDK from GitHub - [exotel/exotel-voip-sdk-flutter  ](https://github.com/exotel/exotel-voip-sdk-flutter)
2. untar SDK&#x20;

`tar -xzvf flutter-sdk.tar.gz`

3. copy the following directories in your flutter app
   - ` flutter-sdk/android`
   - `flutter-sdk/ios`
   - `flutter-sdk/lib`
4. add native sdk
   - to `android` directory
     - go to android directory and run&#x20;

:::Paragraph{indent="4"}
`make deps`
:::

:::Paragraph{indent="4"}
it will download and add the aar file(exotel-voice-release.aar) at \<flutter path>/android/exotel-voice-sdk/
**Note: you might need to change&#x20;**&#x45;XOTEL\_SDK\_VERSIO&#x4E;**&#x20;in Makefile to get latest version**
:::

:::Paragraph{listStyleType="circle" indent="2"}
to ios directory
:::

:::Paragraph{listStyleType="square" indent="3"}
go to `ios` directory and run&#x20;
:::

:::Paragraph{indent="4"}
`make deps`
:::

:::Paragraph{indent="4"}
it will download and add the aar file(ExotelVoice.xcframework) at \<flutter path>/ios/
**Note: you might need to change&#x20;**&#x45;XOTEL\_SDK\_VERSIO&#x4E;**&#x20;in Makefile to get latest version**
:::

***SDK File structure***

After extracting the sdk , it contains the following directories&#x20;

- `android` : android native code that has integrated exotel android sdk.
  - `app/src/main/java/com/exotel/voice_sample`
    - `MainActivity.java` : register the flutter engine and create instance of ExotelSDKChannel.
    - `ExotelSDKChannel.java `: it is android channel interface class which communicate with flutter via Method Channel.
    - `VoiceAppService.java `: this class is communicating with android sdk as per  [Android SDK Integration](docId:4X75LaOyftuOMB4T4yvFd).
- `ios` : ios native code that has integrated exotel ios sdk.
  - `Runner/AppDelegate.swift` : register the flutter engine and create instance of ExotelSDKChannel
  - `Runner/Translator`
    - ExotelSDKChannel.swift \:it is ios channel interface class which communicate with flutter via Method Channel. it is also communicating with ios sdk as per ios [Integration Guide](docId\:Htyc-lApAfhK4IMgBv6jq).
- `lib` : contains flutter dart files&#x20;
  - `exotelSDK `
    - ExotelVoiceClientFactory.dart: it is factory class of SDK which provide ExotelVoiceClient using method  getExotelVoiceClient().
    - ExotelVoiceClient.java: it Exotel Voice Client interface which exposes SDK APIs.

:::BlockQuote
abstract class ExotelVoiceClient \{
void setExotelSDKCallback(ExotelSDKCallback callback);
void registerPlatformChannel();
Future getDeviceId();
Future initialize(String hostname, String subsriberName, String displayName, String accountSid,String subscriberToken);
Future reset();
Future stop();
Future dial(String dialTo, String message);
Future mute();
Future unmute();
Future enableSpeaker();
Future disableSpeaker();
Future enableBluetooth();
Future disableBluetooth();
Future hangup();
Future answer();
Future sendDtmf(String digit);
Future postFeedback(int? rating, String? issue);
Future getVersionDetails();
Future uploadLogs(DateTime startDate, DateTime endDate, String description);
void relaySessionData(Map\<String, dynamic> data) \{}
}
:::

:::Paragraph{listStyleType="square" indent="3"}
`ExotelSDKClient.dart `: it is SDK’s implementation class which has implemented `ExotelVoiceClient`. it is also flutter channel interface class which communicate with android and ios via Method Channel.
:::

:::Paragraph{listStyleType="square" indent="3"}
`ExotelSDKCallaback.dart`: it is SDK Callback or Observer interface that must be implement by your flutter app to handle callbacks from ExotelSDKClient.&#x20;
:::

:::BlockQuote
abstract class ExotelSDKCallback \{
&#x20; void onInitializationSuccess();
&#x20; void onInitializationFailure(String loginStatus);
&#x20; void onDeinitialized();
&#x20; void onAuthenticationFailure(String loginStatus);
&#x20; void onCallInitiated();
&#x20; void onCallRinging();
&#x20; void onCallEstablished();
&#x20; void onCallEnded();
&#x20; void onMissedCall();
&#x20; void onMediaDisrupted();
&#x20; void onRenewingMedia();
&#x20; void onCallIncoming(String callId, String destination);
}
:::

:::Paragraph{listStyleType="square" indent="3"}
`MethodChannelInvokeMethod.dart`: it contains the Channel Method strings which is used to invoke native method of android/ios from flutter.
:::

:::Paragraph{listStyleType="circle" indent="2"}
Service
:::

:::Paragraph{listStyleType="square" indent="3"}
`PushNotificationService.dart` : sample push notification class.
:::

***Dependency***

- add following dependecny in pubspec.yaml of your flutter project&#x20;
  - Add permission handling dependency.&#x20;

:::Paragraph{indent="4"}
`permission_handler: ^11.3.0`
:::

:::Paragraph{listStyleType="circle" indent="2"}
Add firebase dependency.&#x20;
:::

:::Paragraph{indent="4"}
`firebase_messaging: ^14.7.20`
:::

***Permissions***

- flutter :&#x20;
  how to request permission

:::BlockQuote
static Future requestPermissions() async \{
// You can request multiple permissions at once
Map\<Permission, PermissionStatus> statuses = await \[
Permission.phone,
Permission.microphone,
Permission.notification,
Permission.nearbyWifiDevices,
Permission.accessMediaLocation,
Permission.location,  Permission.bluetoothScan,
Permission.bluetoothConnect,
// Add other permissions you want to request
].request();
// Check permission status and handle accordingly
}
:::



- Android&#x20;
  android native code must add following permission in `AndroidManifest.xml `

:::BlockQuote
&#x20; \<uses-permission android\:name="android.permission.INTERNET" />
&#x20; \<uses-permission android\:name="android.permission.ACCESS\_NETWORK\_STATE" />
&#x20; \<uses-permission android\:name="android.permission.BROADCAST\_STICKY" />
&#x20; \<uses-permission android\:name="android.permission.RECORD\_AUDIO" />
&#x20; \<uses-permission android\:name="android.permission.MODIFY\_AUDIO\_SETTINGS"/>
&#x20; \<uses-permission android\:name="android.permission.READ\_PHONE\_STATE" />
&#x20; \<uses-permission android\:name="android.permission.WAKE\_LOCK" />
&#x20; \<uses-permission android\:name="android.permission.DISABLE\_KEYGUARD" />
&#x20; \<uses-permission android\:name="android.permission.FOREGROUND\_SERVICE" />
&#x20; \<uses-permission android\:name="android.permission.POST\_NOTIFICATIONS"/>
&#x20; \<uses-permission android\:name="android.permission.BLUETOOTH"/>
&#x20; \<uses-permission android\:name="android.permission.BLUETOOTH\_ADMIN"/>
&#x20; \<uses-permission android\:name="android.permission.BLUETOOTH\_CONNECT" />
&#x20; \<uses-permission android\:name="android.permission.FOREGROUND\_SERVICE" />
&#x20; \<uses-permission android\:name="android.permission.FOREGROUND\_SERVICE\_PHONE\_CALL"/>
&#x20; \<uses-permission android\:name="android.permission.MANAGE\_OWN\_CALLS"/>
&#x20; \<uses-permission android\:name="android.permission.RECEIVE\_BOOT\_COMPLETED"/>
:::

- iOS&#x20;
  add following depedencies in your `Info.plist`

:::BlockQuote{indent="2"}
\<key>UIBackgroundModes\</key>
\<array>&#x20;
\<string>audio\</string>  &#x20;
\<string>fetch\</string>  
\<string>remote-notification\</string>  &#x20;
\<string>voip\</string>&#x20;
\</array>&#x20;
\<key>NSMicrophoneUsageDescription\</key>&#x20;
\<string>Enable microphone access to allow other people to hear you.\</string>
:::

The permission\_handler plugin use *macros* to control whether a permission is enabled.

You must list the permission you want to use in your application:
Add the following to your `Podfile` file:&#x20;

:::BlockQuote
post\_install do |installer|
&#x20; installer.pods\_project.targets.each do |target|
&#x20;   flutter\_additional\_ios\_build\_settings(target)
&#x20;   target.build\_configurations.each do |config|
&#x20;         config.build\_settings\['GCC\_PREPROCESSOR\_DEFINITIONS'] ||= \[
&#x20;           '$(inherited)',

&#x20;           \## dart: PermissionGroup.microphone
&#x20;           'PERMISSION\_MICROPHONE=1',
&#x20;         ]
&#x20;       end
&#x20; end
en

:::



please refer  [permission\_handler | Flutter package](https://pub.dev/packages/permission_handler) to know in detail about permission handling.

### SDK Initialization&#x20;

Exotel Flutter SDK provide a interface class `ExotelVoiceClient` that exposes `initialize` api to inialize the SDK(Android/iOS).&#x20;

:::BlockQuote
// get ExotelVoiceClient object from Factory class.
ExotelVoiceClient exotelVoiceClient = ExotelVoiceClientFactory.getExotelVoiceClient();

var mApplicationUtil =  ApplicationUtils.getInstance(context); // implemented ExotelSDKCallback
exotelVoiceClient.setExotelSDKCallback(mApplicationUtil);
exotelVoiceClient.registerPlatformChannel();
:::

:::BlockQuote
//initialization
try \{
&#x20; exotelVoiceClient.initialize(hostname!, subscriberName!, displayName!,mAccountSid!, subscriberToken!)
} catch (e) \{
&#x20; log("Error while login");
}
:::

| **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.<br />Subscriber token format example:<br />\{ "refresh\_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJleG90ZWwiLCJzdWIiOiJBcmNoaXQiLCJpYXQiOjE1NzY2NDc5OTksImV4cCI6MTU3Njc0Nzk5OSwiY2xpZW50X2lkIjoiNUUwNTg2NUUifQ.Hc3umVfFlKIPiJ8R9kcP9o9hE9he51le08rO22u7eqs","access\_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJleG90ZWwiLCJzdWIiOiJBcmNoaXQiLCJpYXQiOjE1NzY2NDc5OTksImV4cCI6MTU3Njc0Nzk5OSwic2NvcGUiOiJ2b2ljZSIsImNsaWVudF9pZCI6IjVFMDU4NjVFIn0.uQd\_aHGBIT4XqTPfl-567dmjQrKlmLtPag0KpGB2QMA"} |

**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.

:::BlockQuote
String deviceId = exotelVoiceClient.getDeviceId();
:::


In Above code,&#x20;

::::BlockQuote
:::Paragraph{listStyleType="decimal" indent="1"}
first you need to get the ExotelSDKClient instance
:::

:::Paragraph{listStyleType="decimal" listStart="2" indent="1"}
register the Method Handler to handle the callback from android/iOS native module
:::

:::Paragraph{listStyleType="decimal" listStart="3" indent="1"}
set the callback so that ExotelSDKClient can send callback to UI for some events like *onLoggedInSuccess*, *onLoggedInFailure.&#x20;*&#x48;ere it is assumed that ApplicationUtil must implement `ExotelSDKCallback`
:::

:::Paragraph{listStyleType="decimal" listStart="4" indent="1"}
call the login api with required fields&#x20;
userId : sub
Internal working for `initialize` api
:::

:::Paragraph{listStyleType="circle" indent="2"}
invoking Method "`initialize`" via MethodChannel with required arguments&#x20;
:::

:::Paragraph{listStyleType="circle" indent="2"}
on Method "`initialize`" , Android/iOS native method handler will initialize the android/iOS library as mentioned in [Android SDK Integration.](docId:4X75LaOyftuOMB4T4yvFd)
:::
::::

When login is successful,&#x20;

::::BlockQuote
:::Paragraph{listStyleType="disc" indent="1"}
client SDK will send callback `onInitializationSuccess()`
:::

:::Paragraph{listStyleType="disc" indent="1"}
Android/iOS native method handler will then invoke flutter Method `"inialize-result"`
:::

:::Paragraph{listStyleType="disc" indent="1"}
on flutter Method `"inialize-result"`, ExotelSDKClient will check status and then send `onInitializationSuccess()` callback.
:::
::::

When login is fail,

::::BlockQuote
:::Paragraph{listStyleType="disc" indent="1"}
client SDK will send callback onInitializationFailure()/onAuthenticationFailure()
:::

:::Paragraph{listStyleType="disc" indent="1"}
Android/iOS native method handler will then invoke flutter Method `"inialize-result"`
:::

:::Paragraph{listStyleType="disc" indent="1"}
on flutter Method `"inialize-result"`, `ExotelSDKClient` will check status and then send `onInitializationFailure()/onAuthenticationFailure()` callback.
:::
::::

**Stopping SDK**

To de-initialize the sdk, ExotelVoiceClient  exposes stop api to de-initialize the SDK.

:::BlockQuote
&#x20;exotelVoiceClient?.stop();&#x20;
:::

when sdk gets de-initialized,

::::BlockQuote
:::Paragraph{listStyleType="disc" indent="1"}
client SDK will send callback `onDeinitialized()`
:::

:::Paragraph{listStyleType="disc" indent="1"}
Android/iOS native method handler will then invoke flutter Method `"on-deinitialized"`
:::

:::Paragraph{listStyleType="disc" indent="1"}
on flutter Method `"on-deinitialized"`, `ExotelSDKClient` will check status and then send `on-deinitialized()` callback
:::
::::

### Managing Calls&#x20;

After successful initialization, the library is ready to handle calls. Dialing-out calls or receiving incoming calls is managed through same class ExotelSDKClient.

***Make Outbound Calls***

To make outgoing calls, Provide from username and dial to number in dial() API of ExotelSDKClient Interface to make outgoing calls.&#x20;

`exotelVoiceClient.dial(dialTo,message);`

| **Param** | **Mandatory** | **Description**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| --------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `dialTo`  | 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.<br />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 - \{}()\[]                                                                                                                                                                                                                                                                                                                                                                      |

In Above Code,

::::BlockQuote
:::Paragraph{listStyleType="decimal" indent="1"}
`ExotelSDKClient` will invoke "`dial`" method
:::

:::Paragraph{listStyleType="decimal" listStart="2" indent="1"}
on Method "`dial`" , Android/iOS native method handler will dial the exophone and set the destination number as call context of current user.
:::
::::

When destination user is getting dialed,

::::BlockQuote
:::Paragraph{listStyleType="disc" indent="1"}
client SDK will send callback `onCallRinging()`
:::

:::Paragraph{listStyleType="disc" indent="1"}
Android/iOS native method handler will then invoke flutter Method `"on-call-ringing"`
:::

:::Paragraph{listStyleType="disc" indent="1"}
on flutter Method `"on-call-ringing"`, `ExotelSDKClient` will check status and then send `onCallRinging()` callback.
:::
::::

When destination user has accepted the call,

::::BlockQuote
:::Paragraph{listStyleType="disc" indent="1"}
client SDK will send callback `onCallEstablished()`
:::

:::Paragraph{listStyleType="disc" indent="1"}
Android/iOS native method handler will then invoke flutter Method `"on-call-established"`
:::

:::Paragraph{listStyleType="disc" indent="1"}
on flutter Method `"on-call-established"`, `ExotelSDKClient` will check status and then send `onCallEstablished()` callback.
:::
::::

***Receive Incoming Calls***

The Mobile application receives incoming call data in Push Notification sent by your application backend. Refer section **Push Notification Endpoint** for details.

![](https://api.archbee.com/api/optimize/SK2urIgCC2ml99YyqWOSl/bWfkVrGnjr7Dpz6DEtyl6_upload.png)

 

 

To Receive Push Notification from Push Notification Server, FirebaseMessaging must be initialized and subscribed for firebase message. refer Following PushNotificationService class which is provided as part of sdk.&#x20;

:::BlockQuote
// main.dart
main() async \{
&#x20;await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
}
:::

:::BlockQuote
// PushNotificationService.dart
class PushNotificationService \{

&#x20; static final PushNotificationService \_instance = PushNotificationService.\_internal();

&#x20; FirebaseMessaging? \_fcm;

&#x20; static PushNotificationService getInstance() \{
&#x20;   return \_instance;
&#x20; }
&#x20; PushNotificationService.\_internal()\{
&#x20;   \_fcm = FirebaseMessaging.instance;
&#x20; }

&#x20; Future initialize() async \{
&#x20;   FirebaseMessaging.onMessage.listen((RemoteMessage message) \{
&#x20;     ...
&#x20;   }
&#x20;   );
&#x20; }
&#x20; ...
}
:::

The sample application uses Firebase Cloud Messaging to push call data to the device. Once received at the mobile app, it is sent to *ExotleClientSDK&#x20;*&#x20;through `relaySessionData` API.&#x20;

:::BlockQuote
FirebaseMessaging.onMessage.listen((RemoteMessage message) \{
&#x20;   exotelVoiceClient.relaySessionData(message.data);
});
:::

in Above code,&#x20;

::::BlockQuote
:::Paragraph{listStyleType="decimal" indent="1"}
you must inialize the `PushNotificationService` class
:::

:::Paragraph{listStyleType="decimal" listStart="2" indent="1"}
when push notification comes, data will be relayed to ExotelSDKClient using `relayFirebaseMessagingData()`
:::

:::Paragraph{listStyleType="decimal" listStart="3" indent="1"}
&#x20; `ExotelSDKClient` will invoke `"relay-session-data"` Method.
:::

:::Paragraph{listStyleType="decimal" listStart="4" indent="1"}
on Method `"relay-session-data"`, Android/iOS native method handler will relay data to client SDK. which will be processes by client SDK and client SDK send callback `onIncomingCall()`.
:::

:::Paragraph{listStyleType="decimal" listStart="5" indent="1"}
Android/iOS native method handler will then invoke flutter Method `"on-incoming-call" `
:::

:::Paragraph{listStyleType="decimal" listStart="6" indent="1"}
on flutter Method `"on-incoming-call"`,  `ExotelSDKClient` will send callback `onCallIncoming() `.
:::
::::

***Hangup&#x20;***

The application can call hangup() API on the call object to decline an incoming call or reject ongoing call.

`exotelVoiceClient.hangup();`

in Above code,

::::BlockQuote
:::Paragraph{listStyleType="decimal" indent="1"}
`ExotelSDKClient` will invoke `"hangup"` Method.
:::

:::Paragraph{listStyleType="decimal" listStart="2" indent="1"}
on Method `"hangup"`, Android/iOS native method handler will decline and terminate the call.
:::
::::

when call is successfully rejected,

::::BlockQuote
:::Paragraph{listStyleType="disc" indent="1"}
client SDK will send callback `onCallEnded()`
:::

:::Paragraph{listStyleType="disc" indent="1"}
Android/iOS native method handler will then invoke flutter Method `"on-call-ended"`.&#x20;
:::

:::Paragraph{listStyleType="disc" indent="1"}
on flutter Method `"on-call-ended"`, `ExotelSDKClient` will check call status and then send `onCallEnded()` callback.
:::
::::

### Audio Management&#x20;

Before the call begins, Exotel’s SDK checks the current audio output mode and sets the audio route accordingly. By default, it is in the earpiece mode. For example, If a wired headset is plugged in, audio will get routed via wired headset.

Speaker phone mode can be enabled and disabled using `ExotelSDKClient` object APIs. 

:::BlockQuote
// change audio route to speaker
exotelVoiceClient.enableSpeaker();
// change audio route to phone earpiece
exotelVoiceClient.disableSpeaker();
:::

Bluetooth mode can be enabled and disabled using `ExotelSDKClient` object APIs. This will only work when any bluetooth device is connected to the phone.&#x20;

:::BlockQuote
// enable the bluetooth
exotelVoiceClient.enableBluetooth();
// disable the bluetooth
exotelVoiceClient.disableBluetooth();
:::

Local mic can be muted and unmuted during in-call using `ExotelSDKClient` object APIs.&#x20;

:::BlockQuote
// mute the call
exotelVoiceClient.mute();
// unmute the call
exotelVoiceClient.unmute()
:::

### Reporting Problems&#x20;

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 `ExotelSDKClient` .&#x20;

:::BlockQuote
// Upload logs with description from startDate and endDate exotelVoiceClient.uploadLogs(startDate, endDate, description);
:::

The API triggers onUploadLogSuccess() or onUpLoadLogFailure() callback based on the success or failure of the operation.

### Reporting Call Quality Feedback&#x20;

Call quality can be queried from the user and reported to the exotel platform at the end of the call.

:::BlockQuote
// Upload logs with description from startDate and endDate
int rating = 3
CallIssue issue = BACKGROUND\_NOISE
exotelVoiceClient.postFeedback(rating, 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&#x20;*

### Customer API Endpoints&#x20;

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&#xA0;***

Host a HTTP endpoint which will be queried by the Exotel platform to get to the destination user.

**Method:** `GET`

**Request URL (Example):&#x20;**`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

**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&#x20;

:::BlockQuote
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\&CurrentTime=2019-11-23+22:00:37
:::

Response

```json
{
    "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&#xA0;***

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 as payload & payloadVersion to your push notification endpoint that needs to be pushed to the client device from your application backend. 

Refer section *Receive Incoming Call* for handling of push notification payload.

Note - Headers are not supported in the notify endpoints.

**Method:** POST

**Request URL (Example):&#x20;**`https://<your_api_key>:<your_api_token>@company.com/v1/accounts/<accountsid>/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&#x20;

***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,

1. 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.
2. 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 Authorization*

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.

![](https://api.archbee.com/api/optimize/SK2urIgCC2ml99YyqWOSl/6dQzf-Culm1NEKCElWigx_upload.png)

On receiving the *onAuthenticationFailure&#x20;*&#x65;vent, the application should request a new *subscriber\_token* from its backend and reinitialize the SDK as shown in section **Initialize Library**. Contact [Exotel support](https://hello@exotel.com) 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
