Pull supplemental data from LivePerson

After receiving and processing a survey for a LivePerson interaction, Medallia Experience Cloud can retrieve supplemental information about the respondent from LivePerson by using OmniExporter as a webhook. OmniExporter is a service that uses HTTP to send and receive data from external systems.

Typically OmniExporter is used to send new or updated survey data to an external system, and then optionally process the response, possibly by updating the same survey record with new data. However, in this configuration OmniExporter is triggered when a new LivePerson survey result is available. It then sends information about the respondent to specific endpoint in LivePerson that treats the data as a request, and it then sends the supplemental information back to Medallia Experience Cloud as part of the response. OmniExporter then updates the survey record with the supplemental data.

To setup the webhook to send data to LivePerson, create an OmniExporter specification in Medallia Experience Cloud setup. You will need

  • LivePerson account ID

  • LivePerson login OAuth URL and credentials

  • Names of the survey fields to send to LivePerson, and the matching names of the LivePerson fields to receive the data

Trigger

The trigger identifies the condition that causes OmniExporter to make the request. In this case, the trigger happens when there is a new and complete LivePerson survey record.

Trigger propertyValue
Trigger byField value changes
Data objectSurvey
Trigger an event on record UPDATEOn
Triggering fieldse_status
Matching condition
e_bp_cc_record_type_liveperson_yn = 1 and e_status = 1 and e_bp_cc_liveperson_enrichment_completed_yn != 1

Login settings

These setting identify the OmniExporter request to LivePerson by passing the LivePerson account authentication information.

Login propertyValue
Login MethodOAuth
Login URL TypeStatic
Login URL
https://va-a.sentinel.liveperson.net/sentinel/api/account/{lp-account-id}/app/token?v=1.0
Login Headers TypeStatic
Login Headers
grant_type : client_credentials,
client_id : {lp-account-client-id}
client_secret : {lp-account-client-secret}

HTTP Request Assembly

This is the request that is sent to LivePerson. The URL (endpoint) is specific the company's LivePerson instance and includes the company's account ID, ID of the Lambda function to process the request, and ID of the admin account to run the request. The body of the request contains the the survey responses field values that identify the respondent.

HTTP Request Assembly propertyValue
HTTP MethodPOST
URL TypeStatic
URL
{LP-host-instance}/api​/v1​/account​/{LP-accountId}​/medallia​/metric

https://va-a.faasgw.liveperson.net/api/account/{LP-accountId}/lambdas/{LP-lamda-function-Id}/invoke?v=1&userId={LP-admin-Id}
Headers TypeStatic
Headers
'Authorization: Bearer $TOKEN$'
Fields used in the actione_bp_customer_id_txt
Body TypeJavaScript
BodyJavaScript code to build a JSON payload to send to LivePerson.

The JavaScript in the request body needs to return a JSON payload that maps the survey field values to their corresponding LivePerson fields, similar to this:

// Stringify the final json to send it
write(JSON.stringify((function(){
	/* Adjust the payload object below to match the structure you need */

	// This function return the JSON to send
	var generatePayload=function(record){
		var payload={	
			// Conversation ID as already received.
			"payload": {
				"conversationId":record.e_bp_cc_liveperson_conversation_id_txt
			},
			// Timestamp just needs to be provided - it doesn't have any purpose.
			"timestamp":"1621874976"
		};

		// Return the payload
		return payload;
	};

	/* Parsing and Helper functions - No need to change */

	/* Helper functions */

	// Function to return a date in a predefined format
	var returnDate=function(myDate){
		if(myDate){
			return formatDate(myDate, "yyyy-MM-dd'T'HH:mm:ss.SSSZ");
		}else{
			return null;
		}
	};

	/* Parsing function to construct request */

	// Get fields for the record to be sent
	var entities=data.changedEntities;
	// Set entity for readability
	var entity = entities[0]; 

	// Store the payload in a temporary object to screen null values 
	var tempRecordJson=generatePayload(entity);

	// Define and clear out the final json body to be sent
	var body={};

	// For each "element" in the temporary json...
	for(var key in tempRecordJson){
		// ... if the "element" is not null then add it to the final json
		if(tempRecordJson[key]!==null){body[key]=tempRecordJson[key];}
	}
	return body;
})()));

HTTP Response

After receiving the request, LivePerson returns a JSON payload that contains the supplemental data to include in the Medallia Experience Cloud survey record. OmniExporter uses JavaScript to parse the received JSON object, extract the fields, and map them to corresponding fields in the importer specification that updates the survey record.

HTTP Response Processing propertiesVale
Response Body ProcessingJavaScript
Result AssemblyJavaScript code to parse the JSON payload received from LivePerson. See below.
Auto Importer SpecCONTACT CENTER: LivePerson Append Data Import
Pre-processor SpecNone
Content TypeAPPLICATION_JSON

The JSON payload result that maps the LivePerson field values to their corresponding fields in importer specification, similar to this:

/* 
	Create a JSON object that can be read by the importer to update the existing
	survey record.
*/
write(JSON.stringify(function () {
	// Helper function to parse and format the chat transcript
	var getChatTranscript = function(messagesList) {
		// Filter out messages that don't pertain to the actual conversation (i.e. the survey)
		messagesList = messagesList.filter(function(message) {
    		return message.dialogId == conversationId;
    	});
		var outputList = [];
		for (i = 0; i < messagesList.length; i++) {
		  var message = messagesList[i];
		  var messageSender = message.sentBy.toUpperCase();
		  messageSender = messageSender == "CONSUMER" ? "CUSTOMER" : "AGENT";  
		  var messageText = message.messageData.msg ? message.messageData.msg.text : null;
		  if (messageText != null){
			messageText = messageText.replace(/\n|\r/g,' ');
		    outputList.push(messageSender + ": " + messageText);	  
		  }
		  
		}
		return outputList.join('\n');
	};
	
	// Convert the request sent into a JavaScript object
	var req = {
		"data"				:	data.changedEntities[0] /* Gather the fields from the exported survey record(s) */
	};
	
	// Convert the response received into a JavaScript object
	var res = {
		"status" 			:	JSON.parse(http.getResponseStatusCode()),
		"body"				:	JSON.parse(http.getResponseBodyUtf8())
	};
	
	/* In case of SUCCESS */
	if( res.status == "200" || res.status == "201" || res.status == "202" || res.status == "204") {		
		
		var recordsToProcess = [];
		var record = res.body.conversationHistoryRecords[0];
		var conversationId = req.data.e_liveperson_conversation_id_txt;
		
		/* Parse function to prevent missing fields from breaking the code. It takes one parameter:
			- field : label of the field to parse
		*/
		record.parse = function(section,field){
			if (section == "consumerParticipants") {
				return this[section][0][field] ? this[section][0][field] : null;
			}
			else 
				return this[section][field] ? this[section][field] : null;
			
		};
		
		var parsedRecord = {
				"medalliaId":				req.data.a_surveyid,
				"consumerId":				record.parse("consumerParticipants","participantId"),
				"firstName":				record.parse("consumerParticipants","firstName"),
				"lastName":					record.parse("consumerParticipants","lastName"),
				"latestAgentNickname":		record.parse("info","latestAgentNickname"),
				"email":					record.parse("info","email"),
				"phone":					record.parse("info","phone"),
				"country":					record.parse("info","country"),
				"timeZone":					record.parse("info","timeZone"),
				"brandId":					record.parse("info","brandId"),
				"conversationId":			record.parse("info","conversationId"),
				"startTime":				record.parse("info","startTime"),
				"endTime":					record.parse("info","endTime"),
				"duration":					record.parse("info","duration"),
				"closeReason":				record.parse("info","closeReason"),
				"source":					record.parse("info","source"),
				"device":					record.parse("info","device"),
				"latestSkillId":			record.parse("info","latestSkillId"),
				"latestSkillName":			record.parse("info","latestSkillName"),
				"latestAgentFullName":		record.parse("info","latestAgentFullName"),
				"latestAgentId":			record.parse("info","latestAgentId"),
				"latestAgentLoginName":		record.parse("info","latestAgentLoginName"),
				"language":					record.parse("info","language"),
				"agentGroupId":				record.parse("info","agentGroupId"),
				"agentGroupName":			record.parse("info","agentGroupName"),
				"chatTranscript":			getChatTranscript(record.messageRecords)
		};
		recordsToProcess.push(parsedRecord);
			
		return recordsToProcess;
		
	/* In case of FAILURE */	
	}else{
		
		return {
			"medalliaId"		:	req.data.a_surveyid, /* This exporter processes only one record at a time, so just look at first record in the record set */
			"success" 			:	"false"
		}; 
		
	}
	
}()));