Cognitive Services, CRM, USD

Adding a Sentiment Widget to USD

Recently, Microsoft announced a partnership with the good folks over at CafeX around bringing their Chat and Co-browse capability called LiveAssist to be a part of our Dynamics 365 patform.  This exciting new partnership extends our platform’s ability to engage customers across additional channels, with “built-in” support for multi-session Chat and Co-browse today, and video & voice support coming soon. Having these new features at our disposal also opens up new opportunities to extend them in combination with some of our other key platform features.

Microsoft Cognitive Services, as an example, provides a rich platform for extending our applications to take advantage of a number of interesting services like sentiment analysis, facial recognition, translation and more. In this article I’m going to show you how to leverage the sentiment analysis API to build a simple widget for Unified Service Desk that will display the sentiment for incoming chat messages from our LiveAssist platform and track themm for the current session, on a graph.

NOTE: This work is heavily based on some great stuff already produced by my teammates Geoff Innis and Kate Michel – all I’ve done is built on what they’ve already outlined to an evolutionary “next step”. I greatly appreciate everything they have already done in this area, and am pleased to be able to leverage some of their great work, including Kate’s idea to capture sentiment over time in a chart visualization, as a foundation to build on for this solution example.

ALSO NOTE: This article contains SAMPLE CODE – not production-ready code, bug-free code, nor any guaranteed code. You are free to use this software to enhance your Unified Service Desk experience AT YOUR OWN RISK, with NO WARRANTIES EXPRESSED OR IMPLIED. If something goes wrong, I warned you 🙂

Now that I think I’ve covered all those bases, let’s get started with an overview of this solution.

Part 1 – Cognitive Services

We start out with a Cognitive Services account – if you don’t have one, you can get one for free at the following location:

For sentiment analysis, we’ll be leveraging the Text Analysis API to capture the sentiment for incoming chat messages. Although this API is a paid service, there is a “Free Tier” you can sign up for that allows you to develop and test your components with reasonable limits applied to the API.

Once you get signed up for this API, you’ll want to capture your API Key – this will be used later in the code we’ll write to request the sentiment for a given incoming message.


The widget I’ve created is a simple HTML document that has some JS code to fetch the sentiment score for a given incoming text message and display it a couple of different ways. The sample code can be downloaded at the following link:

As of this writing, this coincides with my original “v4” of this component (that note is more for my benefit, than for yours).

The steps to leverage this component are pretty simple:

  1. Either point to or download the above file. You can put it in a CRM Web Resource, or you can, as I have done, leverage it from an Azure Blob Storage location so all of your instances can have easy access to it.
  2. Set a few query string parameters to configure the component
  3. Update the Unified Service Desk configuration to define a new Hosted Control for the chart, and a set of actions on top of the LiveAssist chat control to connect the chats to the sentiment chart.

I will breifly go through each of these areas in the sections below.

Pulling parameters from the Query String

In order to support multiple users, and multiple languages, I’ve included query string parameter support allowing users to configure the core behavior of the component. Those parameters are outlined below:

  1. Language (l) – This is a two-letter abbreviation for the language upong which to base the sentiment analysis. The codes are listed in the Text Analytics API documentation page. As of this writing, there are 4 supported languages:English (en)
    Spanish (sp)
    French (fr)
    Portuguese (pt)
  2. Sentiment Key (s) – this is the key you saved in the step above from registering for the API. My key is 30 characters of mixed letters and numbers.

When fully assembled, the url will look something like this:


The Code for the Chart

The chart is essentially an HTML, JavaScript and CSS component that leverages the Text Analytics API to derive a sentiment score for an incoming text message, and display it as a raw score, adds it to a historical sentiment score chart of the current conversation and adds in a moving average of the overall conversation sentiment.

The HTML and CSS are pretty straight-forward, but I’ll outline a couple of interesting aspects of the JavaScript here:

First, the code that is used to capture the Sentiment from the API. It’s a simple POST back to the Text Analytics API, with the addition of some specific headers to define the structure of the call and supply the authentication token. There is also a payload sent to the API that follows a specific structure.

function getSentimentForText(inputText) {

	// display the incoming chat message in a hidden area for debugging
	// HACK: there is currently a bug in the Chat control where all messages
	//       coming from either the Agent or the Customer are sent through
	//       the Chat Control's "ChatMessageReceived" event. This will be 
	//       fixed in the future, but we're doing this now to compensate
	if (inputText == lastChat) {
		// do nothing

	lastChat = inputText;

	// URL for the Text Analytics API
	var url = "";

	var inputData = {
		"documents": [{
			"language": languageKey,
			"id": "1",
			"text": inputText

	var stringData = JSON.stringify(inputData);

		type: "POST",
		beforeSend: function(request) {
			request.setRequestHeader("Accept", "application/json");
			request.setRequestHeader("Content-Type", "application/json");
			request.setRequestHeader("Ocp-Apim-Subscription-Key", sentimentApiKey);
		url: url,
		data: stringData,
		success: function(outputData, textStatus, jqXHR) {
			var score = outputData.documents[0].score;
			var roundedScore = Math.round(score * 100) / 100;
			updateSentiment(inputText, roundedScore);
		error: function(jqXHR, textStatus, errorThrown) {
			alert("ERROR: " + errorThrown);


Note that the header section includes a Ocp-Apim-Subscription-Key header – this value is the API Key supplied in through the “s” parameter, as well as the language key (which my code defaults to “en”, for English) to specify the source language for your incoming text.

For the chart, I opted to use the free Combo Chart component from our friends at Google Charts. This chart component accomplished exactly what I was looking for – a bar chart for the individual sentiment scores combined with a line chart for displaying the average. You can review the code for the updateSentiment() method in the downloaded component. Basically it accomplishes a couple of things:

  1. Display the sentiment score along with an accompanying graphic indicating the overall sentiment of the message (in my case, it’s a smile, “meh” or frowny face icon, courtesy of the guys and gals over at Font Awesome)
  2. Update the Average Sentiment score for the whole of the converation
  3. Update the chart with both the latest score (bar) and the updated average (line)

Oh, and before I forget – for retrieving the query string parameters, I leveraged some community-edited code from Stack Overflow to make this job really easy:

 function getParameterByName(name, defaultValue, url) {
	// process defaults and initialize
	if (!defaultValue) defaultValue = "";
	if (!url) url = window.location.href;
	name = name.replace(/[[]]/g, "\$&");
	// set up regex and retrieve values 
	var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
		results = regex.exec(url);
	if (!results) return defaultValue;
	if (!results[2]) return defaultValue;
	return decodeURIComponent(results[2].replace(/+/g, " "));

Configuring Unified Service Desk

This is the most complex part of implementing this solution, but only because it has the most steps:

  1. Define the Hosted Control for the Chart component
  2. Define an action to load the hosted control when a new session starts
  3. Define the unexposed event on the Chart control to capture incoming chat messages
  4. Define an action to call into the Chat component to retrieve the last message received and put it into the session context
  5. Define an action that passes the most recent chat message from the Chat component to the Chart component

Define the Hosted Control

The hosted control definition is pretty straight-forward. This is a webpage, so we’ll use Standard Web Application. We want it to be part of the Session, so it’s marked as dynamic, but not global. I’ve also placed it in the LeftPanel1 location, but you can put it wherever you like.

New Session Action

Once the Hosted Control is defined, we now need to show it when a new session is created. This is done through a simple Action Call triggered on the CRM Global Manager’s NewSession event:


This action call retrieves the last message retrieved from the Chat Control and puts it where USD can leverage it. We have to ask for it specifically because there could be multiple chat sessions currently active, and we need to make sure we’re pulling the chat message from the correct session:

Call Sentiment Chart Component

Now that we have the chat message safely in session, we can push it into the Chart control through a “send” sub-action assigned to the previous “get” action.

Note: For those new to USD, each action can have a set of sub-actions defined. The first picture below is the definition for the sub-action’s Action Call, and the second picture is the inclusion of that sub-action call to the parent Get Sentiment for Last Chat Message. More information about Sub-Action calls can be found here.

This Action Call deserves a little explanation:

This Action is defined as a RunScript command that basically executes JavaScript inside the target panel. This allows us to call functions, change CSS properties, get HTML attribute values, and more.  In our case, we’re pulling the last chat message out and sending it to the getSentimentForText method which is where all the magic happens.

ChatMessageReceived Event

Finally, we can wire up the last two actions we created to the missing Event definition from the Chat control. The component does raise this event when new messages are received, but if it’s not defined in the Events list in USD, you can’t respond to it. So, we add it with very little configuration, which enables us to wire up the last two Actions.


The new LiveAssist chat and co-browse capabilities are a great addition to our arsenal of tools in Dynamics 365 and the Unified Service Desk. Extending their functionality with our Azure-hosted Cognitive Services adds additional value to chat-based interactions with customers. The range of applications here are virtually endless – everything from agent training based on their tracked sentiment scores, to auto-routing of chat requests for historically “difficult” customers, and everything inbetween.

So – what are you going to build? Try something crazy and let me know what you’ve done – I’d love to see it in action!