Cisco Spark API – PHP Function

This method is no longer functional with the Webex move away from TLS1.0 earlier this year. I am utilizing an updated function using CURL that I will make a available once I get time to tweak it more.

For Cisco Spark bots there are “easy button” packages available for Python and Node.JS, but PHP is lacking.  I’ve worked a bit to put together a basic function that handles the Spark API.  It is important that you still understand the underlying API and when to use a GET vs POST vs PUT, and know what resource you are targeting within the API. The Cisco Spark for Developers page does a great job and walking you through each API call, when and how to use them and even a “test mode” to test different calls real time.

This function can be placed within the working script as a standalone file <?php include(‘sparkAPI.php’); ?> or just included into your main app.  When calling the function, syntax should be

send_to_spark([Token],[Method],[Resource],[Data(opt)]);

The Spark Token will be the authentication Token you get from the BOT or Integration page under your developer account.  HTTP Method supports GET, POST, PUT and DELETE.  Spark Resource would be the API resource you are sending to, so if you want to get a list of rooms you belong to you would use send_to_spark($token,”GET”,”Rooms”); .  Again reference Cisco Spark for Developers for more information.

Data can also be passed to the function for query strings or for POST and PUT messages.  The data field should be an array with the needed values and the script takes care of converting it to the appropriate query string or json data.  Successfull responses will be returned as an object and then be further parsed as needed.  DELETE methods respond with a NULL value when executed.  If there is an error on the API call, the header code is return to standard output and standard error log for addressing and the function returns false.

//Send query to get 1 rooms sorted by last activity

$queryData = array("max"=>"1",
                   "sortBy"=>"lastactivity");
$sparkResponse = send_to_spark($token,"GET","rooms",$queryData);

var_dump($sparkResponse);

/*OUTPUT
object(stdClass)#2 (1) {
  ["items"]=>
  array(1) {
    [0]=>
    object(stdClass)#1 (7) {
      ["id"]=>
      string(76) "Y2lzY29zcGFyazovL3VzL1JPT00vNWI"
      ["title"]=>
      string(10) "Jon Snipes"
      ["type"]=>
      string(6) "direct"
      ["isLocked"]=>
      bool(false)
      ["lastActivity"]=>
      string(24) "2018-03-13T20:15:53.231Z"
      ["creatorId"]=>
      string(79) "Y2lzY29zcGFyazovL3VzL1BFT1BMRS8wZmY3MGE3"
      ["created"]=>
      string(24) "2017-12-01T04:15:25.181Z"
    }
  }
}
*/
//Send Message to room

$queryData = array("roomId"=>"Y2lzY29zcGFyazov",
                   "text"=>"Test Message ***");
$sparkResponse = send_to_spark($token,"POST","messages",$queryData);

var_dump($sparkResponse);

/*OUTPUT
object(stdClass)#1 (7) {
  ["id"]=>
  string(80) "Y2lzY29zcGFyazovL3VzL01FU"
  ["roomId"]=>
  string(76) "Y2lzY29zcGFyazovL3VzL1JPT0"
  ["roomType"]=>
  string(6) "direct"
  ["text"]=>
  string(25) "Test Message ***"
  ["personId"]=>
  string(79) "Y2lzY29zcGFyazovL3VzL1BFT1"
  ["personEmail"]=>
  string(22) "jon.snipes@sparkbot.io"
  ["created"]=>
  string(24) "2018-03-13T20:15:53.231Z"
}
*/

The Function (Download)

//Build Function for sending API calls to spark
//add to script or include()'filename')
//Required - Token,Method,Resource
//Optional - Data
function send_to_spark($sparkToken,$sparkMethod,$sparkResource,$sparkData = "") {

  $sparkURL      = "https://api.ciscospark.com/v1/";
  $sparkMethod   = strtoupper($sparkMethod);
  $sparkResource = strtolower($sparkResource);

  switch ($sparkMethod) {
  //Set vaiables and syntax based on API Method
  
    case "GET":
    //Send GET message to spark
      if ( is_array($sparkData) ) {
      //Check if data is an array - used for searches
        $sparkResource .= "?".http_build_query($sparkData);
      } elseif ( $sparkData != "" ) {
      //Check if there is no spark Data (Optional Field) and formats as needed
        $sparkResource .= "/".$sparkData;
      }
      $httpOptions = array(
            'http' => array(
                'header'  => "Authorization: Bearer ".$sparkToken." \r\nContent-type: application/json\r\n",
                'method'  => 'GET'
            ),
        );
        break;
        
    case "POST":
    // json_encode data and send POST to Spark
      $httpOptions = array(
          'http' => array(
              'header'  => "Authorization: Bearer ".$sparkToken." \r\nContent-type: application/json\r\n",
              'method'  => 'POST',
              'content' => json_encode($sparkData),
          ),
      );
      break;
      
    case "PUT":
    // json_encode data and send PUT to Spark
      $httpOptions = array(
          'http' => array(
              'header'  => "Authorization: Bearer ".$sparkToken." \r\nContent-type: application/json\r\n",
              'method'  => 'PUT',
              'content' => json_encode($sparkData),
          ),
      );
      break;
      
    case "DELETE":
    //send DELETE
      $sparkResource .= "/".$sparkData;
      $httpOptions = array(
          'http' => array(
              'header'  => "Authorization: Bearer ".$sparkToken." \r\nContent-type: application/json\r\n",
              'method'  => 'DELETE'
          ),
      );
      break;
      
  }
  //set http context
  $httpContext  = stream_context_create($httpOptions);

  // make API call to Spark
  //Loop created for Retry-After.
  //Send API Call if HTTP return is 2XX or error then break loop and return value
  //If HTTP Response is 429 - Check Retry-After timer and sleep for that duration and continue loop
  do {
    $sparkResult = @json_decode(file_get_contents($sparkURL.$sparkResource, false, $httpContext),0);

    if ( preg_match("/HTTP\/1\.. 20.*/",$http_response_header[0]) ) {
    //If 2XX response code, API call was successful.  Return Sprk response (can be NULL response like on deletes)
    //break loop
      $returnValue = $sparkResult;
      break;
      
    } elseif  ( preg_match("/HTTP\/1\.. 429*/",$http_response_header[0]) ) {
    //if received a 429 thottling notice check for "retry-after" key and sleep.
    //continue loop until definate success or error

      //Convert HTTP Response headers into string for processing
      $fullHeader = "";
      foreach ( $http_response_header as $header ){
        $fullHeader .= preg_replace("/:/","=",strtolower($header))."&";
      }
      //Convert string to array to parse Retry-After time, log message and sleep for retry time
      parse_str($fullHeader,$httpResponseHeaders);
      
      if ( @is_numeric(httpResponseHeaders['retry-after']) ) {
      //Confirm that Retries-After exists and is a number to continue loop
        error_log("*** Spark API Throttled - ".$httpResponseHeaders['retry-after']."second delay injected\n");
        echo "*** Spark API Throttled - ".$httpResponseHeaders['retry-after']."second delay injected\n";
        sleep($httpResponseHeaders['retry-after']);
        
      } else {
        error_log("*** Spark API Response ERROR: ".$http_response_header[0]."\n");
        echo "*** Spark API Response ERROR: ".$http_response_header[0]."\n";

        $returnValue = false;
        break;
        
      }

    } else {
    //All other codes are an error - Displays errors in error and standard out.  Return false
    //break loop
    
      error_log("*** Spark API Response ERROR: ".$http_response_header[0]."\n");
      echo "*** Spark API Response ERROR: ".$http_response_header[0]."\n";

      $returnValue = false;
      break;
    }
  } while (0);

  return $returnValue;
}

Published by

Jon Snipes

My career progressed from head butcher to Collaboration CCIE. There isn’t much technically that carries over between professions, but 8 years of direct customer service experience and squeezing margin out of processes provided a solid base and direction for the rest of my career. My focus today is deep into collaboration messaging, voice and video with a focus on developing processes and programmatic solutions to complex business problems. ------ Senior Technical Consultant with ROVE CCIE Collaboration 51786 Cisco Spark Professional Ambassador 2018 Cisco Champion