Voximplant. Blog

Automated telephone surveys and notifications

Automation

Building automated telephone surveys and notifications has never been easier – with VoxImplant you can build even complex scenarios in minutes. Special feature called CallLists should be used for that purpose. Every record in CSV file sent with the CreateCallList request will be processed with VoxEngine scenario, the result can be stored and accessed using GetCallListDetails method of HTTP API.

The process can be divided into a few parts:
1. Creating CSV file with the list of phone numbers and additional data that can be passed to the scenario
2. Creating VoxEngine scenario that will process every line of CSV file, and creating VoxImplant application and rule
3. Making request to HTTP API to start automated calling process

Designing CSV-file structure

First of all you need to design your CSV file that will be sent to HTTP API, its data will be passed to VoxEngine scenario as customData in form of JSON-string, that can be converted into JavaScript object. First line of the file will contain parameter names, let’s see the example for sending automated notifications about scheduled appointment:

  1. first_name;last_name;phone_number;appointment_date
  2. John;Snow;16501234567;November 15th
  3. David;Ericsson;16501234568;November 16th

The CSV-file contains name of the callee, his phone number and appointment date, first string describes parameter names.

VoxEngine scenario

Ok, we have our CSV-file design, now we can create VoxEngine scenario that will process every string of the file and make a call and depending on the result report to CallList processor if we can consider this call successful or not. If we report that it wasn’t successful CallList processor can try again after some time (that can be set in HTTP request).

  1. require(Modules.CallList); // Enable CallList module
  2.  
  3. var call,
  4. first_name,
  5. last_name,
  6. phone_number,
  7. appointment_date,
  8. playbackCounter = 0;
  9.  
  10. // AppEvents.Started dispatched for each CSV record
  11. VoxEngine.addEventListener(AppEvents.Started, function (e) {
  12. var data = VoxEngine.customData(); // <-- data from CSV string in JSON format
  13. data = JSON.parse(data);
  14. first_name = data.first_name;
  15. last_name = data.last_name;
  16. phone_number = data.phone_number;
  17. appointment_date = data.appointment_date;
  18. Logger.write("Calling " + first_name + " " + last_name + " on " + phone_number);
  19. // Make a call
  20. call = VoxEngine.callPSTN(phone_number);
  21. // Trying to detect voicemail
  22. call.detectProgressTone(true);
  23. // Add event listeners
  24. call.addEventListener(CallEvents.Connected, handleCallConnected);
  25. call.addEventListener(CallEvents.Failed, handleCallFailed);
  26. call.addEventListener(CallEvents.Disconnected, handleCallDisconnected);
  27. call.addEventListener(CallEvents.ToneDetected, handleToneDetected);
  28. });
  29.  
  30. function handleToneDetected(e) {
  31. // Voicemail?
  32. if (e.VoicemailTone) {
  33. VoxEngine.CallList.reportError("Voicemail", VoxEngine.terminate);
  34. return;
  35. }
  36. }
  37.  
  38. // Call connected successfully
  39. function handleCallConnected(e) {
  40. connected = true;
  41. setTimeout(function () {
  42. e.call.say("Hello " + first_name + "! Your appointment scheduled for "+appointment_date, Language.US_ENGLISH_FEMALE);
  43. }, 500);
  44. e.call.addEventListener(CallEvents.PlaybackFinished, handlePlaybackFinished);
  45. }
  46.  
  47. function handleCallDisconnected(e) {
  48. // Tell CallList processor about successful call result
  49. CallList.reportResult({
  50. result: true,
  51. duration: e.duration,
  52. rating: rating,
  53. }, VoxEngine.terminate);
  54. }
  55.  
  56. // Playback finished
  57. function handlePlaybackFinished(e) {
  58. e.call.removeEventListener(CallEvents.PlaybackFinished, handlePlaybackFinished);
  59. playbackCounter++;
  60. // If the message was played 4 times - hangup
  61. if (playbackCounter == 4) {
  62. e.call.hangup();
  63. } else {
  64. // Play one more time
  65. setTimeout(function () {
  66. e.call.say("Your appointment scheduled for "+appointment_date, Language.US_ENGLISH_FEMALE);
  67. e.call.addEventListener(CallEvents.PlaybackFinished, handlePlaybackFinished);
  68. }, 2000);
  69. }
  70. }
  71.  
  72. function handleCallFailed(e) {
  73. // Tell CallList processor that we couldn't get call connected
  74. // depending on the request options it will either try to launch the scenario again after some time
  75. // or will write the result (failed call) into result_data column of the CSV file with results
  76. CallList.reportError({
  77. result: false,
  78. msg: "Failed",
  79. code: e.code
  80. }, VoxEngine.terminate);
  81. }

Save the scenario, create VoxImplant application and application rule that will attach the scenario to the application. No need to change the Pattern field – it’s not used when the scenario launched via HTTP request.

Launching automated dialing via HTTP API

API method to launch dialing is described at CreateCallList HTTP API, let’s take a look at the parameters:
account_name — your VoxImplant account name
api_key — your VoxImplant API KEY
rule_id — id of the application rule that has the assigned scenario
priority — dialing priority, if you have more than one CallList
max_simultaneous — maximum number of simultaneously processed records from CSV file
num_attempts — number of dialing attempts, new attempt will happen in case of CallList.reportError
name — CallList name, useful to differentiate different CallLists from each other
file_content — CSV file should be sent in request body
interval_seconds — time for the next attempt (in seconds)
encoding — CSV file encoding, should be specified if different from UTF-8
delimiter — CSV file columns delimiter, default is ;

Let’s create the PHP file that will create a CallList:

  1. <?php
  2. define("API_URL", "https://api.voximplant.com/platform_api/");
  3. define("API_KEY", "your api key"); // <-- don't forget to change
  4. define("ACCOUNT_NAME", "your account name"); // <-- don't forget to change
  5. define("RULE_ID", rule-ID); // <-- don't forget to change
  6.  
  7. function httpRequest($url,$params) {
  8. $ch = curl_init();
  9. curl_setopt($ch, CURLOPT_URL, $url);
  10. curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
  11. if (isset($params["post"])) curl_setopt($ch, CURLOPT_POST, 1);
  12. if (isset($params["post_data"])) curl_setopt($ch, CURLOPT_POSTFIELDS, $params["post_data"]);
  13. curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: text/csv'));
  14. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  15. $server_output = curl_exec ($ch);
  16. curl_close ($ch);
  17. return $server_output;
  18. }
  19.  
  20. function createCallList($file) {
  21. $url = API_URL . "CreateCallList/?" .
  22. "account_name=" . ACCOUNT_NAME .
  23. "&api_key=" . API_KEY .
  24. "&rule_id=" . RULE_ID .
  25. "&max_simultaneous=10" .
  26. "&num_attempts=2" .
  27. "&interval_seconds=60" .
  28. "&priority=1" .
  29. "&name=CallList";
  30.  
  31. $data = file_get_contents($file);
  32. $params = array('post' => true, 'post_data' => $data);
  33. $result = httpRequest($url, $params);
  34. echo $result;
  35. }
  36.  
  37. function getCallListDetails($list_id, $output = "json") {
  38. $url = API_URL . "GetCallListDetails/?" .
  39. "account_name=" . ACCOUNT_NAME .
  40. "&api_key=" . API_KEY .
  41. "&list_id=" . $list_id .
  42. "&output=" . $output;
  43.  
  44. $params = array();
  45. $result = httpRequest($url, $params);
  46. echo $result;
  47. }
  48.  
  49. createCallList(CSV-file-URL);
  50. //getCallListDetails($list_id, "csv");
  51. ?>

getCallListDetails request result will look similar to (if call was connected successfully):

  1. "appointment_date";"last_name";"phone_number";"first_name";"__end_execution_time";"__start_execution_time";"result_data";"last_attempt";"attmepts_left";"status_id";status
  2. November 15th;John;16501234567;Snow;;;"{""result"":true,""duration"":27}";"2014-11-24 19:21:39";1;2;Processed
  3. November 16th;David;16501234568;Ericsson;;;"{""result"":true,""duration"":20}";"2014-11-24 19:21:39";1;2;Processed

What about surveys?

Ok, now we know how to make telephone notifications, but what if we need to make a telephone survey? It’s rather easy to change the scenario we created before to start collecting data from callee input – we will modify handleCallConnected, handleCallDisconnected and will add a new handleToneReceived fucntion:

  1. function handleCallConnected(e) {
  2. connected = true;
  3. e.call.handleTones(true); // <-- enable input processing
  4. setTimeout(function () {
  5. e.call.say("Hello " + first_name + "! Thank you for visiting our store, "+
  6. "please rate the customer service quality from 1 to 5.", Language.US_ENGLISH_FEMALE);
  7. }, 500);
  8. e.call.addEventListener(CallEvents.PlaybackFinished, handlePlaybackFinished);
  9. e.call.addEventListener(CallEvents.ToneReceived, handleToneReceived);
  10. }
  11.  
  12. var rating;
  13.  
  14. function handleToneReceived(e) {
  15. e.call.removeEventListener(CallEvents.PlaybackFinished, handlePlaybackFinished);
  16. e.call.stopPlayback();
  17. rating = e.tone;
  18. e.call.say("Thank you for your answer!", Language.US_ENGLISH_FEMALE);
  19. e.call.addEventListener(CallEvents.PlaybackFinished, function(e) {
  20. e.call.hangup();
  21. });
  22. }
  23.  
  24. function handleCallDisconnected(e) {
  25. CallList.reportResult({
  26. result: true,
  27. duration: e.duration,
  28. rating: rating,
  29. }, VoxEngine.terminate);
  30. }
  31.  
  32. // Playback finished
  33. function handlePlaybackFinished(e) {
  34. e.call.removeEventListener(CallEvents.PlaybackFinished, handlePlaybackFinished);
  35. playbackCounter++;
  36. if (playbackCounter == 4) {
  37. e.call.hangup();
  38. } else {
  39. setTimeout(function () {
  40. e.call.say("Please rate the customer service quality from 1 to 5.", Language.US_ENGLISH_FEMALE);
  41. e.call.addEventListener(CallEvents.PlaybackFinished, handlePlaybackFinished);
  42. }, 2000);
  43. }
  44. }

And the result:

"last_name";"phone_number";"first_name";"__end_execution_time";"__start_execution_time";"result_data";"last_attempt";"attmepts_left";"status_id";status
John;16501234567;Snow;;;"{""result"":true,""duration"":27,""rating"":""3""}";"2014-11-24 20:17:13";1;2;Processed

You can always use ASR module to let your customers answer using voice or make calls via your SIP infrastructure by changing the callPSTN function to callSIP. Don’t forget that you can also make HTTP requests right from the scenario to pass the data to your backend in real-time. Feel free to change the scenarios according your needs.

Comments