Anda di halaman 1dari 56

ServiceNow Platform

Integration
With
Amazon Alexa
v1.0

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
SUPPORTED VERSIONS 4

DISCLAIMER 4

OVERVIEW 5

HIGH LEVEL ARCHITECTURE 6


SKILL ENABLEMENT 6
SKILL INVOCATION 6
TERMINOLOGY - SKILLS, INTENTS, SLOTS, AND UTTERANCES 7

USE CASES 9
REQUIREMENTS 9
RESOURCES 9

SERVICENOW SETUP 10
OAUTH 10
SCRIPTED REST API 11

AMAZON SETUP 13
CREATE NEW SKILL 13
INTERACTION MODEL 14
OAUTH AND REST ENDPOINT CONFIGURATION 16
SSL SETUP 17

DEVICE AND SKILL SETUP 18


ENABLING THE ALEXA SKILL 18
LINKING ACCOUNT 18
ECHOSIM 18

REST SERVICE SCRIPTING 19


OVERVIEW 19
PHASE 1: HELLO WORLD! 20
PHASE 2: LIST INCIDENTS – NO INPUT 21
PHASE 3: LIST INCIDENTS – USER INPUT AND SESSION STATE 23

SECURITY, MODULARIZATION AND SCOPED APPLICATIONS 28


OVERVIEW 28
SOLUTION DESIGN 29
SECURITY 29
MODULARIZATION/SCOPED APPLICATIONS 29
SERVICENOW SETUP 31
SCOPED APPLICATION: ALEXA SKILLS KIT INTEGRATION 31
SCOPED APPLICATION: INCIDENT INTEGRATION 41
SCOPED APPLICATION: APPROVAL INTEGRATION 45
AMAZON SETUP 49
ServiceNow Platform Integration with Amazon Alexa - Whitepaper
© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
PREREQUISITES 49
LAMBDA FUNCTION – SERVICENOW INSTANCE ALEXA PROXY 49
ALEXA SKILL MODIFICATION 51
TESTING AND FINALIZATION 52

PUBLISHING YOUR SKILL 53

APPENDIX A - SECURITY 54
BEST PRACTICES 54
PIN 54

APPENDIX B - TROUBLESHOOTING 55

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
Supported Versions
This whitepaper copyrighted ©2017, supports the ServiceNow Platform for releases starting Istanbul and higher.

Disclaimer
ServiceNow is changing the way people work. With a service-orientation toward the activities, tasks and processes
that make up day-to-day work life, we help the modern enterprise operate faster and be more scalable than ever
before.

The updates in this document represent our integration specs and assumptions, only as of the date of this
whitepaper. We undertake no obligation, and do not intend to update the forward‐looking changes, to review or
confirm expectations on installations completed.

Further information on these and other factors that could affect our technical outcomes are not intended to be
included.

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
Overview
To integrate a ServiceNow application with Alexa you will need to setup the following.
1. ServiceNow instance
a. Authentication/Authorization – Using OAuth
b. REST endpoint to process Alexa requests – Using Scripted REST APIs
2. Amazon Services
a. Alexa Skill
b. Amazon Lambda Function – Used for proxying. More on this later.

The intent of this paper is to step through setup and creation of a skill in stages. The following
three scenarios are discussed:
1. Simple request/response based scenario.
2. Multi-step skill where state is remembered.
3. Refactoring to add:
a. Security – Authentication and authorization
b. Modularity – Script Includes
c. Domain separation - Scoped Applications

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
High Level Architecture

Skill Enablement

Once a skill is enabled and linked, Amazon manages the OAuth token automatically.

Skill Invocation

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
Terminology - Skills, Intents, Slots, and Utterances
- Each request is mapped to an intent provided by an Alexa Skill. A skill is invoked with a
keyword. For this whitepaper, we assume a skill that can be activated using the keyword
“snowy”. In this case the user would trigger that context/skill by saying “Alexa, ask
snowy …”
o You can effectively treat a skill as a mapping onto your service.
- Each skill has intents, which are the methods within your service. For example, you may
want to get a list of incidents or ask for details about a specific incident
o You can treat intents as mappings onto a specific method within your service.
- Each intent may have different ways of triggering it, depending on what a user says.
These are called utterances. For example, you could map someone saying “list
incidents” to an intent “list_incidents”. Similarly, the statement “find incidents” would
also map to the intent “list_incidents”.
o You can treat utterances as synonyms/multiple mappings that lead to an intent,
which implicitly maps to a method in our analogy.
- Each intent may have inputs/parameters called slots. These are inputs that affect the
logic flow when servicing the request.
o You can treat slots as inputs/parameters to the specific method in your service
o There are different types of slots:
▪ Built-In - There are many built-in slot types that can be used for common
slots such as: numbers, dates, times, cities, and more. See the Slot Type
Reference guide for more details.
▪ Custom – These are user defined slots
- For a complete example
Phrase Mappings
Alexa Input Alexa ask snowy to list critical open incidents created since last year

Amazon Conceptual Skill = snowy


Mapping Utterance = list {priority} {state} incidents created since {date}
Intent/Utterance Mapping = list_incidents
Slot Value
Priority Critical (Custom slot)

State Open (Custom slot)


Date 2017-01-01 (Built-In slot)
ServiceNow Conceptual public class SnowyService {
Mapping function list_incidents(jsonRequest) {}
}
JSONRequest Example {
"session": {
"new": true,
"sessionId": "SessionId.abc",
"application": {
"applicationId": "amzn1.ask.skill.e201c320-8eea-4a39-83ba-89c98e0a6773"
},
"attributes": {},
"user": {
"userId": "amzn1.ask.account.xyz"
}
},
"request": {
"type": "IntentRequest",
"requestId": "EdwRequestId.xxx",
"intent": {
"name": "incident_list",
"slots": {
"createdAfterDate": {

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
"name": "createdAfterDate",
"value": "2017"
},
"state": {
"name": "state",
"value": "open"
},
"priority": {
"name": "priority",
"value": "critical"
}
}
},
"locale": "en-US",
"timestamp": "2017-09-06T23:16:12Z"
},
"context": {
"AudioPlayer": {
"playerActivity": "IDLE"
},
"System": {
"application": {
"applicationId": "amzn1.ask.skill.asasas"
},
"user": {
"userId": "amzn1.ask.account.asad"
},
"device": {
"supportedInterfaces": {}
}
}
},
"version": "1.0"
}

- In short, for a developer:


o Skill = Service
o Intent = Method
o Slot = Parameter
o Utterance = Synonym for Intent with/without parameters = Method Overloading
- Each request through Alexa generates a JSON request to our Scripted REST API. The
schema for the JSON can be found here: Developer - Amazon - Alexa - JSON Interface
Reference for Custom Skills

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
Use Cases
Requirements
To walk through an integration, we will create a simple skill that allows a user to:
1. Request a list of incidents
a. Allow specifying the incident state as a filter.
b. Allow specifying the incident priority as a filter.
c. Allow specifying opened date as a filter.
2. Ask for details on a specific incident requesting specific fields.
3. Provide help in context.

We will then combine that information to build a modular and secure scoped application that
allows for secured read and write operations to the ServiceNow instance.

This paper provides one possible solution to the use cases presented. It is intended to provide a
foundation to help understand the various moving parts when building an integration with
Alexa. It is expected that the implementer will build on this knowledge to craft a solution
custom to their needs. The solution should consider:
- Functional and non-functional requirements
- Scalability/performance
- Security
- Working within the framework provided by the ServiceNow platform.

When designing your skill, interaction refer to Amazon’s Voice Design Guide.

Resources
- How to Interact with Alexa - Amazon Alexa Voice Design Guide (Video)
- Choose the Invocation Name for a Custom Skill
- Alexa for BusinessAlexa for Business
o Publishing Private SkillsPublishing Private Skills

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
ServiceNow Setup

OAuth
Resources:
- ServiceNow - Product Documentation - OAuth 2.0
- ServiceNow - Product Documentation - OAuth authorization code grant flow

Steps
1. Log into your ServiceNow instance, for example, https://<yourinstance>.service-
now.com/
2. Go to the System OAuth > Application Registry.

3. Make sure you are in the global scope and not in an application scope.
4. Click button
5. Select the Create an OAuth API endpoint for external client’s option.
6. Enter the following values (or substitute your own values):
a. Name = Amazon Al exa
b. Client Secret = al exa

7. Take note of the fields just entered as they are custom to your implementation and
will be needed during the Amazon setup phase:
a. Client ID
b. Client Secret
8. You will need to come back and enter the redirect URL once you have completed the
Amazon setup.

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
Scripted REST API
1. NOTE: This setup results in an unsecured REST API endpoint. Anyone connecting to
your endpoint will be able to see the data it returns without being authenticated for
the first few exercises. Authentication and authorization are added in later sections.
2. Log into your ServiceNow instance. for example, https://<yourinstance>.service-
now.com/
3. Go to System Web Services > Scripted Web Services > Scripted Rest APIs.

4. Make sure you are in the global application scope.


5. Click the button.
6. Enter the following values (or substitute your own values):
a. Name = Amazon Al exa - Snowy
b. API ID = ask_snowy

7. Click Submit.
8. Reopen the record you just added.
9. Take note of the following field as it will be custom to your implementation:
a. Base API Path
10. Under the Resources tab, click the button

11. Enter the following values (or substitute your own values):
a. Name = Al exa - Snowy
b. HTTP Method = POST
c. Relative Path = /
d. Requires Authentication = Unchecked (See NOTE in step 1)

12. Add the following code to the Script box:


(function process(/* RESTAPIRequest */request, /* RESTAPIResponse */response) {

var req = request.body.data.request;


var ses = request.body.data.session;

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
var alexaResponse = {};
alexaResponse.version = "1.0";
alexaResponse.sessionAttributes = {};
alexaResponse.response = {};
alexaResponse.response.shouldEndSession = false;
alexaResponse.response.outputSpeech = {};
alexaResponse.response.outputSpeech.type = "SSML";
alexaResponse.response.outputSpeech.ssml = "<speak>Hello World</speak>";

response.setStatus(200);
response.setContentType("application/json;charset=UTF-8");
var writer = response.getStreamWriter();
writer.writeString(global.JSON.stringify(alexaResponse));
// gs.info(global.JSON.stringify(alexaRequest.response));
return;
})(request, response);

13. Save the record


14. Test that your endpoint is working.
a. Connect to the instance and resource path in the record, for example,
https://<yourinstance>.service-now.com/api/snc/ask_snowy
b. Receive a message saying “GET method not supported for API.”

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
Amazon Setup
Create new skill
1. Login to Amazon Alexa Developer Console.
2. Click the Alexa tab and select the Get Started button on the Alexa Skills Kit card.
3. Click the Add New Skill button.
4. Enter the following values (or substitute your own values) and click Save.
a. Name = Snowy
b. Invocation Name = snowy
5. Click the Next button to move to the Interaction Model section.
NOTE: You should substitute your own name and invocation name values here.

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
Interaction Model
1. In the Interaction Model section, enter the following data in the fields.
Field Value/Instructions
Intent Schema {
"intents": [
{
"intent": "AMAZON.CancelIntent"
},
{
"intent": "AMAZON.HelpIntent"
},
{
"intent": "AMAZON.StopIntent"
},
{
"slots": [
{
"name": "approval_state",
"type": "APPROVAL_STATE"
},
{
"name": "index",
"type": "AMAZON.NUMBER"
}
],
"intent": "approval_edit_state"
},
{
"intent": "approval_search_manager_my_assigned"
},
{
"slots": [
{
"name": "field",
"type": "FIELD"
},
{
"name": "index",
"type": "AMAZON.NUMBER"
}
],
"intent": "incident_detail"
},
{
"slots": [
{
"name": "user_comment",
"type": "AMAZON.LITERAL"
},
{
"name": "index",
"type": "AMAZON.NUMBER"
}
],
"intent": "incident_edit_add_comment"
},
{
"slots": [
{
"name": "priority",
"type": "PRIORITY"
},
{
"name": "state",
"type": "STATE"
},
{
"name": "createdAfterDate",
"type": "AMAZON.DATE"
}
],
"intent": "incident_search"
},
{
"intent": "incident_search_fulfiller_my_assigned"
},
{
"intent": "incident_search_requestor_my_created"
}
]
}

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
Custom Slot Enter the values below.
Types NOTE: Each value must be on a separate line.

Click to add a new type.


Type Values
PRIORITY critical
high
moderate
low
planning
STATE new
in progress
on hold
resolved
closed
cancelled
FIELD short description
comments
caller
APPROVAL_STATE approve
deny
reject
Sample approval_edit_state {approval_state} change {index}
Utterances
approval_search_manager_my_assigned list my approvals

incident_detail get {field} for incident {index}

incident_edit_add_comment add comment to {index} my comment is {generic comment from


user|user_comment}
incident_search list incidents

incident_search list {priority} {state} incidents created after {createdAfterDate}

incident_search_fulfiller_my_assigned list my assigned incidents

incident_search_requestor_my_created list my created incidents

2. Click the Next button to move to the Configuration section.

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
OAuth and REST Endpoint Configuration
1. In the Configuration section, enter the following data in the fields.
Field Value/Instructions

Service Endpoint Type HTTPS


Default e.g. https://<yourinstance>.service-now.com/api/snc/ask_snowy

NOTE: Replace host name with your instance using the path from the ServiceNow Setup -
> Scripted API setup above.
Do you allow users to create an Yes
account or link to an existing
account with you?
Authorization URL e.g. https://<yourinstance>.service-now.com/oauth_auth.do

NOTE: Replace host name with your instance.


Client ID <your client id from application registry setup above>
e.g. 8002154a5805030044228ed927186374

This is copied from the setup of your ServiceNow instance above.


ServiceNow Setup – Oauth
Scope (Add Scope) useraccount
This actually needs to be the string useraccount
https://docs.servicenow.com/bundle/geneva-servicenow-
platform/page/administer/security/reference/r_OAuthAPIResponseParameters.html
Authorization Grant Type Auth Code Grant

Access Token URI e.g. https://<yourinstance>.service-now.com/oauth_token.do

NOTE: Replace with your instance.


Client Secret <your client secret from application registry setup above>
e.g. alexa

Copied from your setup above: ServiceNow Setup – Oauth.


Client Authentication Scheme Credentials in request body

2. You now need to take the redirect URLs from the Amazon Setup and update the
ServiceNow instance application registry setup in ServiceNow OAuth setup as described
above.

3. Click the “Next” button to move to the SSL Certificate section

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
SSL Setup
1. In the SSL Certificate” section, enter the following data in the fields.
Field Value/Instructions

Certificate for DEFAULT My development endpoint is a sub-domain of a domain that has a wildcard certificate
Endpoint from a certificate authority

2. Click the Next button to move to the Test section.


3. Make sure the “This skill is enabled for testing on your account” button is enabled. This
can be found in the section marked “Please complete the Interaction Model tab to start
testing this skill”

Basic setup is now complete. Once you have finished building the REST API endpoint you
will still need to deploy the skill to make it usable by others. This is covered in the Publishing
section.

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
Device and Skill Setup
Enabling the Alexa Skill
You will need to enable your Alexa skill before testing:
1. Log in to https://alexa.amazon.com/
2. Select Skills from left navigation bar.
3. Select Your Skills.
4. Select your skill, for example, Snowy.
5. Click Enable. If your skill is already enabled, then proceed to the next step.

Linking Account
You can wait to perform these steps until after you implement the security portion of the setup
below.
1. Navigate to your skill as described above.
2. Click Link Account to authenticate with the ServiceNow instance. You may need to try
this step a more than once.
You are now ready to test.

Echosim
If you do not have a device you can use https://echosim.io, a free, community-run project that
is an Echo in a webpage, for testing.

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
REST Service Scripting

Overview
We are now going to build up our skill using Amazon’s schema.
Developer - Amazon - Alexa - JSON Interface Reference for Custom Skills
We will address the use case stated in the steps above.

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
Phase 1: Hello World!
We have already set up the script to do our first test. Test your skill using the Service Simulator
in the Amazon Developer Console.
1. NOTE: This setup results in an unsecured REST API endpoint. Anyone connecting to
your endpoint will be able to see the data it returns without being authenticated for
the first few exercises. Authentication and authorization are added in later exercises.
2. Enter the utterance: “list incidents.”
3. Look for a response like the following:
{
"version": "1.0",
"response": {
"outputSpeech": {
"ssml": "<speak>Hello World</speak>",
"type": "SSML"
},
"speechletResponse": {
"outputSpeech": {
"ssml": "<speak>Hello World</speak>"
},
"shouldEndSession": false
}
},
"sessionAttributes": {}
}

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
Phase 2: List Incidents – No Input
1. NOTE: This setup results in an unsecured REST API endpoint. Anyone connecting to
your endpoint will be able to see the data it returns without being authenticated for
the first few exercises. Authentication and authorization are added in later section.
2. Modify the script
(function process(/* RESTAPIRequest */request, /* RESTAPIResponse */response) {
var alexaRequest = request.body.data.request;
var alexaSession = request.body.data.session;
var alexaRequestType = alexaRequest.type;
var alexaIntent = alexaRequest.intent.name;

var alexaResponse = {};


alexaResponse.version = "1.0";
alexaResponse.sessionAttributes = {};
alexaResponse.response = {};
// By default end the session
alexaResponse.response.shouldEndSession = true;
alexaResponse.response.outputSpeech = {};

if (alexaRequestType == 'LaunchRequest') {
alexaIntent = 'welcomeme';
}

alexaResponse.response.outputSpeech.type = "SSML";

if (alexaIntent == 'incident_search') {
var gr = new GlideRecord('incident');
gr.setLimit(5);
gr.query();
var out = "<speak>";
var i = 1;
var sysIds = [];
while (gr.next()) {
sysIds[i] = gr.getValue('sys_id');
out += '<say-as interpret-as="cardinal">' + i++ + '</say-as>. ';
out += gr.getValue("short_description");
out += ".";
}
out += "</speak>";
alexaResponse.sessionAttributes.incidentSysIds = sysIds;
alexaResponse.response.outputSpeech.ssml = out;
alexaResponse.response.shouldEndSession = false;
}

response.setStatus(200);
response.setContentType("application/json;charset=UTF-8");
var writer = response.getStreamWriter();
writer.writeString(global.JSON.stringify(alexaResponse));
// gs.info(global.JSON.stringify(alexaRequest.response));
return;
})(request, response);

3. Test your skill using the Service Simulator in the Amazon Developer Console.
a. Enter utterance: “list incidents.”
b. Look for a response like the following
{
"version": "1.0",
"response": {
"outputSpeech": {
"ssml": "<speak><say-as interpret-as=\"cardinal\">1</say-as>. Closed p1 - Critical incident Can't read
email.<say-as interpret-as=\"cardinal\">2</say-as>. On Hold p1 - Critical incident Unable to get to network file
shares.<say-as interpret-as=\"cardinal\">3</say-as>. In Progress p1 - Critical incident Wireless access is down in my
area.<say-as interpret-as=\"cardinal\">4</say-as>. Closed p1 - Critical incident Forgot email password.<say-as
interpret-as=\"cardinal\">5</say-as>. Closed p1 - Critical incident CPU load high for over 10 minutes.</speak>",
"type": "SSML"
},
"speechletResponse": {
"outputSpeech": {
"ssml": "<speak><say-as interpret-as=\"cardinal\">1</say-as>. Closed p1 - Critical incident Can't read
email.<say-as interpret-as=\"cardinal\">2</say-as>. On Hold p1 - Critical incident Unable to get to network file
shares.<say-as interpret-as=\"cardinal\">3</say-as>. In Progress p1 - Critical incident Wireless access is down in my
area.<say-as interpret-as=\"cardinal\">4</say-as>. Closed p1 - Critical incident Forgot email password.<say-as
interpret-as=\"cardinal\">5</say-as>. Closed p1 - Critical incident CPU load high for over 10 minutes.</speak>"
},
"shouldEndSession": false
}

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
},
"sessionAttributes": {
"incidentSysIds": [
null,
"9c573169c611228700193229fff72400",
"9d385017c611228701d22104cc95c371",
"e8caedcbc0a80164017df472f39eaed1",
"9d3c1197c611228701cd1d94bc32d76d",
"e8e875b0c0a80164009dc852b4d677d5"
],
"query": ""
}
}

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
Phase 3: List Incidents – User Input and Session State
1. NOTE: This setup results in an unsecured REST API endpoint. Anyone connecting to
your endpoint will be able to see the data it returns without being authenticated for
the first few exercises. Authentication and authorization are added in later section.
2. Modify the script.
(function process(/* RESTAPIRequest */request, /* RESTAPIResponse */response) {
/** ******************************************************* */
/* Common Functions */
/** ******************************************************* */
// Date source code sourced from:
// https://github.com/alexa/skill-sample-nodejs-calendar-reader/blob/master/src/index.js#L292
// Used to work out the dates given week numbers
var w2date = function(year, wn, dayNb) {
var day = 86400000;
var j10 = new Date(year, 0, 10, 12, 0, 0), j4 = new Date(year, 0, 4,
12, 0, 0), mon1 = j4.getTime() - j10.getDay() * day;
return new Date(mon1 + ((wn - 1) * 7 + dayNb) * day);
};

// Given a week number return the dates for both weekend days
var getWeekendData = function(res) {
if (res.length === 3) {
var saturdayIndex = 5;
var sundayIndex = 6;
var weekNumber = res[1].substring(1);

var weekStart = w2date(res[0], weekNumber, saturdayIndex);


var weekEnd = w2date(res[0], weekNumber, sundayIndex);

return Dates = {
startDate : weekStart,
endDate : weekEnd,
};
}
};

// Given a week number return the dates for both the start date and the end
// date
var getWeekData = function(res) {
if (res.length === 2) {
var mondayIndex = 0;
var sundayIndex = 6;
var weekNumber = res[1].substring(1);
var weekStart = w2date(res[0], weekNumber, mondayIndex);
var weekEnd = w2date(res[0], weekNumber, sundayIndex);
return Dates = {
startDate : weekStart,
endDate : weekEnd,
};
}
};

var getDateFromSlot = function(rawDate) {


// try to parse data
var date = new Date(Date.parse(rawDate));
var result;
// create an empty object to use later
var eventDate = {};

// if could not parse data must be one of the other formats


if (isNaN(date)) {
// to find out what type of date this is, we can split it and count
// how many parts we have see comments above.
var res = rawDate.split("-");
// if we have 2 bits that include a 'W' week number
if (res.length === 2 && res[1].indexOf('W') > -1) {
var dates = getWeekData(res);
eventDate["startDate"] = new Date(dates.startDate);
eventDate["endDate"] = new Date(dates.endDate);
// if we have 3 bits, we could either have a valid date (which
// would have parsed already) or a weekend
} else if (res.length === 3) {
var dates = getWeekendData(res);
eventDate["startDate"] = new Date(dates.startDate);
eventDate["endDate"] = new Date(dates.endDate);
// anything else would be out of range for this skill
} else if (res.length === 1) { // Year handling
eventDate["startDate"] = new Date(Date.parse(rawDate
+ "-01-01T00:00:00.000Z"));
eventDate["endDate"] = new Date(Date.parse(rawDate

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
+ +"-01-01T00:00:00.000Z"));
} else {
eventDate["error"] = dateOutOfRange;
}
// original slot value was parsed correctly
} else {
eventDate["startDate"] = new Date(date).setUTCHours(0, 0, 0, 0);
eventDate["endDate"] = new Date(date).setUTCHours(24, 0, 0, 0);
}
return eventDate;
};

/** ******************************************************* */
/* Mappings */
/** ******************************************************* */
var stateMappings = {};
stateMappings["new"] = 1;
stateMappings["in progress"] = 2;
stateMappings["on hold"] = 3;
stateMappings["resolved"] = 6;
stateMappings["closed"] = 7;
stateMappings["cancelled"] = 8;

var priorityMappings = {};


priorityMappings["critical"] = 1;
priorityMappings["high"] = 2;
priorityMappings["moderate"] = 3;
priorityMappings["low"] = 4;
priorityMappings["planning"] = 5;

var fieldMappings = {};


fieldMappings["short description"] = "short_description";
fieldMappings["comments"] = "comments";
fieldMappings["caller"] = "called_id.name";

/** ******************************************************* */
/* Customized Responses */
/** ******************************************************* */
var alexaRequest = request.body.data.request;
var alexaSession = request.body.data.session;
var alexaSessionAttributes = alexaSession.attributes;
var alexaRequestType = alexaRequest.type;
var alexaIntent = alexaRequest.intent.name;
var alexaIntentSlots = alexaRequest.intent.slots;
var alexaIntentContext = alexaSessionAttributes.intentContext;

var alexaResponse = {};


alexaResponse.version = "1.0";
alexaResponse.response = {};
// By default end the session
alexaResponse.response.shouldEndSession = true;
alexaResponse.response.outputSpeech = {};
alexaResponse.response.outputSpeech.type = "SSML";

if (alexaRequestType == 'LaunchRequest') {
alexaIntent = 'Help';
}

if (alexaIntent == 'AMAZON.StopIntent'
|| alexaIntent == 'AMAZON.CancelIntent') {
alexaResponse.sessionAttributes = {};
alexaResponse.response.outputSpeech.ssml = "<speak>Goodbye</speak>";
} else if (alexaIntent == 'AMAZON.HelpIntent' || alexaIntent == 'Help') {
var out = "<speak>";
out += "You can say, list incidents, to list incidents. You can list incidents specifying the
priority, state, and created date as filters. For example, list critical closed incidents created after last year.";
if (alexaIntentContext == 'incident_search') {
if (alexaSessionAttributes.sysIds.length) {
var indexes = alexaSessionAttributes.sysIds.length;
out += " You can get details about an incident. For example, get short
description for incident 1 based on your previous search.";
}
}
out += "</speak>";
// Keep session around so user can request details on data
alexaResponse.sessionAttributes = alexaSessionAttributes;
alexaResponse.response.outputSpeech.ssml = out;
} else if (alexaIntent == 'incident_detail') {
if (alexaSessionAttributes.table && alexaSessionAttributes.sysIds
&& alexaIntentSlots.index && alexaIntentSlots.index.value) {
var index = alexaIntentSlots.index.value;
var fieldName = fieldMappings[alexaIntentSlots.field.value];
var sysId = alexaSessionAttributes.sysIds.length > index ?
alexaSessionAttributes.sysIds[index]
: null;

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
if (sysId && fieldName) {
var out = "<speak>";
var gr = new GlideRecord(alexaSessionAttributes.table);
gr.addQuery("sys_id", '=', sysId);
gr.query();
if (gr.next()) {
var value = gr.getDisplayValue(fieldName);
out += alexaIntentSlots.field.value + " for incident "
+ gr.getValue("number") + ". "
+ (value ? value : "is empty");
}
out += "</speak>";
alexaResponse.response.outputSpeech.ssml = out;
}
}
// Keep session around so user can request details on data
alexaResponse.sessionAttributes = alexaSessionAttributes;
alexaResponse.response.shouldEndSession = false;
} else if (alexaIntent == 'incident_search') {
var limit = 5;
var gr = new GlideRecord('incident');
gr.setLimit(limit);

var query = "";


if (alexaIntentSlots.state
&& stateMappings[alexaIntentSlots.state.value]) {
gr.addQuery("incident_state", "=",
stateMappings[alexaIntentSlots.state.value]);
query += query.length == 0 ? "" : " and ";
query += " state is " + alexaIntentSlots.state.value;
}

if (alexaIntentSlots.priority
&& priorityMappings[alexaIntentSlots.priority.value]) {
gr.addQuery("priority", "=",
priorityMappings[alexaIntentSlots.priority.value]);
query += query.length == 0 ? "" : " and ";
query += " priority is " + alexaIntentSlots.priority.value;
}

var createdAfter = '';


if (alexaIntentSlots.createdAfterDate
&& alexaIntentSlots.createdAfterDate.value) {
var dateRange = getDateFromSlot(alexaIntentSlots.createdAfterDate.value);
if (dateRange && dateRange.startDate) {
createdAfter = dateRange.startDate.toISOString();
gr.addQuery("sys_created_on", ">=", createdAfter.substring(0,
10));
query += query.length == 0 ? "" : " and ";
query += " created after <say-as interpret-as=\"date\">"
+ createdAfter.substring(0, 10) + "</say-as>";
}
}

gr.query();
var out = "<speak>";
var i = 1;
var sysIds = [];

if (gr.hasNext()) {
out += "Results matching " + query + ".";
} else {
out += "No data found for " + query;
}

while (gr.next()) {
sysIds[i] = gr.getValue('sys_id');
out += '<say-as interpret-as="cardinal">' + i++ + '</say-as>. ';
out += gr.getDisplayValue("incident_state") + ' p';
out += gr.getDisplayValue("priority") + ' incident ';
out += gr.getValue("short_description");
out += ".";
}

out += "</speak>";

// Reset the session attributes


alexaResponse.sessionAttributes = {};
// Set the context so we can offer contextual help
alexaResponse.sessionAttributes.intentContext = 'incident_search';
// Store date needed to run query again or respond to detail requests
alexaResponse.sessionAttributes.createdAfter = createdAfter;
alexaResponse.sessionAttributes.query = gr.getEncodedQuery();
alexaResponse.sessionAttributes.table = gr.getTableName();
alexaResponse.sessionAttributes.sysIds = sysIds;

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
alexaResponse.response.outputSpeech.ssml = out;
// Keep session around so user can request details on data
alexaResponse.response.shouldEndSession = false;
}

response.setStatus(200);
response.setContentType("application/json;charset=UTF-8");
var writer = response.getStreamWriter();
writer.writeString(global.JSON.stringify(alexaResponse));
// gs.info(global.JSON.stringify(alexaRequest.response));
return;
})(request, response);

3. Test your skill using the Service Simulator in the Amazon Developer Console.
c. Enter utterance: “list critical closed incidents created after last year”
d. You should get back a response like the following
{
"version": "1.0",
"response": {
"outputSpeech": {
"ssml": "<speak><say-as interpret-as=\"cardinal\">1</say-as>. Closed p1 - Critical incident Reset my password.<say-as interpret-as=\"cardinal\">2</say-as>.
Closed p1 - Critical incident EMAIL is slow when an attachment is involved.<say-as interpret-as=\"cardinal\">3</say-as>. Closed p1 - Critical incident Missing my
home directory.<say-as interpret-as=\"cardinal\">4</say-as>. Closed p1 - Critical incident File Server is 80% full - Needs upgrade.<say-as interpret-
as=\"cardinal\">5</say-as>. Closed p1 - Critical incident Does not look like a backup occurred last night.</speak>",
"type": "SSML"
},
"speechletResponse": {
"outputSpeech": {
"ssml": "<speak><say-as interpret-as=\"cardinal\">1</say-as>. Closed p1 - Critical incident Reset my password.<say-as interpret-as=\"cardinal\">2</say-as>.
Closed p1 - Critical incident EMAIL is slow when an attachment is involved.<say-as interpret-as=\"cardinal\">3</say-as>. Closed p1 - Critical incident Missing my
home directory.<say-as interpret-as=\"cardinal\">4</say-as>. Closed p1 - Critical incident File Server is 80% full - Needs upgrade.<say-as interpret-
as=\"cardinal\">5</say-as>. Closed p1 - Critical incident Does not look like a backup occurred last night.</speak>"
},
"shouldEndSession": false
}
},
"sessionAttributes": {
"sysIds": [
null,
"46b66a40a9fe198101f243dfbc79033d",
"46cebb88a9fe198101aee93734f9768b",
"46e18c0fa9fe19810066a0083f76bd56",
"470af5afa9fe198101b324dd773ef379",
"470d51a0a9fe1981006c20c825e48933"
],
"query": "incident_state=7^priority=1.0^sys_created_on>=2016-01-01",
"createdAfter": "2016-01-01T00:00:00.000Z",
"table": "incident"
}
}

4. Test the session attributes:


a. Enter utterance: “get short description for incident 1.”
b. Look for a response like the following
{
"version": "1.0",
"response": {
"outputSpeech": {
"ssml": "<speak>short_description for incident INC0000009. Reset my password</speak>",
"type": "SSML"
},
"speechletResponse": {
"outputSpeech": {
"ssml": "<speak>short_description for incident INC0000009. Reset my password</speak>"
},
"shouldEndSession": false
}
},
"sessionAttributes": {
"sysIds": [
null,
"46b66a40a9fe198101f243dfbc79033d",
"46cebb88a9fe198101aee93734f9768b",
"46e18c0fa9fe19810066a0083f76bd56",

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
"470af5afa9fe198101b324dd773ef379",
"470d51a0a9fe1981006c20c825e48933"
],
"query": "incident_state=7^priority=1.0^sys_created_on>=2016-01-01",
"createdAfter": "2016-01-01T00:00:00.000Z",
"table": "incident"
}
}

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
Security, Modularization and Scoped Applications
Overview
Building upon the previous solution we will now:
- Secure the application
- Make the code modular

Security requirements include:


- The Alexa request contains the OAuth token inside the payload, whereas the
ServiceNow instance requires it in the “Authorization” header. We will need to generate
the correct header.
- There exists logging in the ServiceNow platform that will log out the HTTP request body.
We do not want to “leak” our access tokens out this way so we need to strip the access
tokens from the JSON payload

Other requirements include:


- Provide a generic framework and/or pattern for customers to add new intents
- Allow for creation of scoped applications to handle domain specific intents to expand
functionality over time
- Provide library of shared functions when working with Alexa requests

The additional use cases we need to handle are:


Persona Use Case

Requestor List incidents created by the requestor and get details about them.
Add Comments to the incident
Fulfiller List incidents assigned to the fulfiller
Add Comments to the incident
Manager List approvals waiting for manager.
Approve or Deny the request

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
Solution Design
Security
To meet the security requirements, we will create an AWS Lambda function to proxy our Alexa
request. This function will:
- Add in the relevant authorization headers
- Strip out the token data
before it is sent to the ServiceNow instance.

Modularization/Scoped Applications
To make our code modular we need a way of mapping incoming requests to handlers. We can
use many of the concepts from HTTP handling. Conceptually we have:
- HTTP Request = Alexa Request
- HTTP Response = Alexa Response
- HTTP Session = Alexa Session
- URL = intent

We need some persistent store to hold mappings of intents to handlers/processors. We can do


so by:
- introducing a mapping table
- building some handler at the REST API endpoint to route requests
- build the handlers using some coding/naming conventions.

Let’s assume every handler has the following interface:


public class <ApplicationScope>.<ClassName> {
/**
* Should return help text for all the supported intents
* @param ctx contextual access to helper functions, slot values and session attributes
* @param request the alexa request
* @param response the alexa response
* @return
*/
public String help(Context ctx, AlexaRequest request, AlexaResponse response);

/**
* Should process the request for the intent <intentname>
* @param ctx contextual access to helper functions, slot values and session attributes

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
* @param request the alexa request
* @param response the alexa response
* @return
*/
public void <intentname>(Context ctx, AlexaRequest request, AlexaResponse response);
}

This will suffice for the following cases:


Help – When a help request comes in all the classes listed in the handler registry are polled via
the help method for what functions they can perform and should return what phrases can be
uttered.
IntentRequest - When a request comes in a lookup is done in the handler registry. If a match is
found for the intent name, the script object/class associated with that is instantiated. If that
object then has a method with the same name as the intent, the intent is invoked, otherwise,
we default to help.

There are three inputs provided:


- Context - The context object is a wrapper class that can be used to
o Provide scoped access to session attributes so that data does not leak into other
handlers.
o Provide convenience methods for retrieving input data like slots, date
conversions
- Request – The request portion of the incoming Alexa request
- Response – An initialized response object

Code will be organized into separate packages:


- Library for common functions when working with the Alexa API
- Application specific code for handling requests targeted at a specific domain
o All application code will reference the library of common functions
o All application code will depend on the main module for Alexa integration

Specifically, with the ServiceNow platform we will map packages to scoped applications. As
such we will need the following scoped applications.

Amazon Alexa Skills Kit Integration


This will serve several functions:
- First as a utility library for other scoped applications to access common functions for
working with the Alexa API
- Second as a registry for mapping intents to scripts
- Third as the REST API endpoint for Alexa and to handle the routing of intents to their
Script Include handler code.

Incident Integration
This scoped application will handle the Fulfiller and Requestor use cases

Approval Integration
This scoped application will handle the Manager use case
ServiceNow Platform Integration with Amazon Alexa - Whitepaper
© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
ServiceNow Setup
Scoped Application: Alexa Skills Kit Integration
Scoped Application Creation
1. Go to “System Applications” -> “Applications”

2. Click Button
3. Select “Start From Scratch” and click the button

4. Enter values:
Field Value/Instructions

Name Alexa Skills Kit Integration


Scope x_snc_ask_api

5. Click button

6. Click “OK”.

7. Close the confirmation window.

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
Launch Studio for Scoped Application
1. Go to “System Applications” -> “Applications”
2. Click button on the application you are interested in e.g.

3. You should now be in the Studio Application

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
Data Model - Intent Handler Registry
1. Go to the studio for application “Alexa Skills Kit Integration” by following steps in Launch
Studio for Scoped Application.
2. Click Button
3. Select “Data Model” -> “Table”

4. Click Button
5. Edit following values:
a. Main section
Field Value/Instructions

Application Alexa Skills Kit Integration (Read Only)


Label Intent Handler Registry
Name x_snc_ask_api_intent_handler_registry
New Menu Name Alexa Skills Kit Integration

b. Add the two columns as shown as they are not created by default
Table Column name Type Reference Display

x_snc_ask_api_intent_handler_registry intent String TRUE

x_snc_ask_api_intent_handler_registry script Reference Script Include FALSE

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
6. Click button

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
Script Include - DateUtil
1. Go to the studio for application “Alexa Skills Kit Integration” by following steps in Launch
Studio for Scoped Application.
2. Click Button
3. Select “Server Development” -> “Script Include”

4. Click Button
5. Enter Following values and the script:
Field Value/Instructions

Application Alexa Skills Kit Integration (Read Only)


Name DateUtil
Accessible From All Application Scopes
Script
var DateUtil = Class.create();
DateUtil.prototype = {
initialize : function() {
},

type : 'DateUtil',

/** ******************************************************* */
/* Common Functions */
/** ******************************************************* */
// Date source code sourced from:
// https://github.com/alexa/skill-sample-nodejs-calendar-reader/blob/master/src/index.js#L292
// Used to work out the dates given week numbers
w2date : function(year, wn, dayNb) {
var day = 86400000;
var j10 = new Date(year, 0, 10, 12, 0, 0), j4 = new Date(year, 0, 4,
12, 0, 0), mon1 = j4.getTime() - j10.getDay() * day;
return new Date(mon1 + ((wn - 1) * 7 + dayNb) * day);
},

// Given a week number return the dates for both weekend days
getWeekendData : function(res) {
if (res.length === 3) {
var saturdayIndex = 5;
var sundayIndex = 6;
var weekNumber = res[1].substring(1);

var weekStart = w2date(res[0], weekNumber, saturdayIndex);


var weekEnd = w2date(res[0], weekNumber, sundayIndex);

return {
startDate : weekStart,
endDate : weekEnd,
};
}
},

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
// Given a week number return the dates for both the start date and the end
// date
getWeekData : function(res) {
if (res.length === 2) {
var mondayIndex = 0;
var sundayIndex = 6;
var weekNumber = res[1].substring(1);
var weekStart = w2date(res[0], weekNumber, mondayIndex);
var weekEnd = w2date(res[0], weekNumber, sundayIndex);
return {
startDate : weekStart,
endDate : weekEnd,
};
}
},

getDateFromSlot : function(rawDate) {
// try to parse data
var date = new Date(Date.parse(rawDate));
var result;
// create an empty object to use later
var eventDate = {};

// if could not parse data must be one of the other formats


if (isNaN(date)) {
// to find out what type of date this is, we can split it and count
// how many parts we have see comments above.
var res = rawDate.split("-");
// if we have 2 bits that include a 'W' week number
if (res.length === 2 && res[1].indexOf('W') > -1) {
var dates = getWeekData(res);
eventDate["startDate"] = new Date(dates.startDate);
eventDate["endDate"] = new Date(dates.endDate);
// if we have 3 bits, we could either have a valid date (which
// would have parsed already) or a weekend
} else if (res.length === 3) {
var dates = getWeekendData(res);
eventDate["startDate"] = new Date(dates.startDate);
eventDate["endDate"] = new Date(dates.endDate);
// anything else would be out of range for this skill
} else if (res.length === 1) { // Year handling
eventDate["startDate"] = new Date(Date.parse(rawDate
+ "-01-01T00:00:00.000Z"));
eventDate["endDate"] = new Date(Date.parse(rawDate
+ "-01-01T00:00:00.000Z"));
} else {
eventDate["error"] = dateOutOfRange;
}
// original slot value was parsed correctly
} else {
eventDate["startDate"] = new Date(date).setUTCHours(0, 0, 0, 0);
eventDate["endDate"] = new Date(date).setUTCHours(24, 0, 0, 0);
}
return eventDate;
}
};

6. Click Button

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
REST API – Alexa
1. Go to the studio for application “Alexa Skills Kit Integration” by following steps in Launch
Studio for Scoped Application.
2. Click Button
3. Select “Inbound Integrations” -> Scripted REST API”

4. Click Button
5. Enter Following values:
Field Value/Instructions

Application Alexa Skills Kit Integration (Read Only)


Name Amazon Alexa Skills Kit - REST API
API ID restapi

6. Click button.
7. Click the button under the “Resources” tab.
8. Enter Following values and the script:
Field Value/Instructions

Application Alexa Skills Kit Integration (Read Only)


Name Amazon Alexa Skill Router
HTTP Method POST
Relative Path /skill/route
Requires Authentication Checked (Located under security tab)
Resource Path /api/x_snc_ask_api/restapi/skill/route (Read Only) TAKE NOTE THIS WILL SHOW UP AFTER YOU
SUBMIT, YOU WILLL NEED TO ENTER THIS IN THE AWS LAMBDA FUNCTION LATER
Script
(function process(/* RESTAPIRequest */request, /* RESTAPIResponse */response) {
/********************************************************* */
/* Utilities and Wrapped Classes */
/********************************************************* */
var locateHandlers = function(cfg) {
var handlers = [];
var gr = new GlideRecord('x_snc_ask_api_intent_handler_registry');
// Add custom filters if any
if (cfg && cfg.filter) {
cfg.filter(gr);

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
}
gr.query();
var scriptIdMap = {};
while (gr.next()) {
scriptIdMap["_" + gr.getValue("script")] = true;
}
var scriptIds = Object.keys(scriptIdMap);
var scriptIndex = 0;
for (i = 0; i < scriptIds.length; i++) {
var scriptIncludeGr = new GlideRecord('sys_script_include');
var scriptSysId = scriptIds[i].replace('_', '');
scriptIncludeGr.get(scriptSysId);
var className = scriptIncludeGr.getValue("api_name");
if (className) {
var evaluator = new GlideScopedEvaluator();
var script = evaluator
.evaluateScript(scriptIncludeGr, 'script');
script['className'] = className;
if (script) {
handlers[scriptIndex++] = script;
}
}
}
return handlers;
};

var createContext = function(apiId, requestSession, request, response) {


var ctx = {
apiId : apiId,
logDebugMessage : function(msg) {
logDebugMessage(msg);
},
getIntentName : function() {
if (request && request.intent) {
return request.intent.name;
}
},
setSessionAttribute : function(key, value) {
if (response) {
if (!response.sessionAttributes) {
response.sessionAttributes = {};
}
if (!response.sessionAttributes[apiId]) {
response.sessionAttributes[apiId] = {};
}
response.sessionAttributes[apiId][key] = value;
}
},
getSessionAttribute : function(key) {
if (requestSession && requestSession.attributes
&& requestSession.attributes[apiId]) {
return requestSession.attributes[apiId][key];
}
return null;
},
getSlotValue : function(key, defaultValue) {
if (alexaRequest && alexaRequest.intent
&& alexaRequest.intent.slots) {
var slot = alexaRequest.intent.slots[key];
if (slot) {
return slot.value;
}
}
return defaultValue;
},
getSlotDateRangeValue : function(key) {
var dateSlotValue = this.getSlotValue(key);
if (dateSlotValue) {
var dateUtil = new x_snc_ask_api.DateUtil();
return dateUtil.getDateFromSlot(dateSlotValue);
}
return null;
}
};
return ctx;
};

var invokeHelp = function(alexaRequestSession, alexaRequest, alexaResponse) {


// List all handlers and get help
var cfg = {};
var handlers = locateHandlers(cfg);
var out = "<speak>";
for (i = 0; i < handlers.length; i++) {
var apiId = handlers[i].className;
var ctx = createContext(apiId, alexaRequestSession, alexaRequest,

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
alexaResponse);
logDebugMessage('Calling Help for Handler:' + apiId);
out += handlers[i].help(ctx, alexaRequest, alexaResponse);
}
out += "</speak>";
// Keep session around so user can request details on data
alexaResponse.sessionAttributes = alexaRequestSessionAttributes;
alexaResponse.response.outputSpeech.ssml = out;
};
/** ******************************************************* */
/* Debugging */
/** ******************************************************* */
var debugLogging = true;

var logDebugMessage = function(msg) {


if (debugLogging) {
gs.error(msg);
}
};

logDebugMessage(global.JSON.stringify(request.body.data));

/** ******************************************************* */
/* Initialization */
/** ******************************************************* */
// Request variable initialization
var alexaRequest = request.body.data.request;
var alexaRequestSession = request.body.data.session;
var alexaRequestSessionAttributes = alexaRequestSession.attributes;
var alexaRequestType = alexaRequest.type;
var alexaUser = request.body.data.session.user;
var alexaIntent = alexaRequest.intent.name;

// Default Response variable initialization


var alexaResponse = {};
alexaResponse.version = "1.0";
alexaResponse.response = {};
// By default end the session
alexaResponse.response.shouldEndSession = true;
alexaResponse.response.outputSpeech = {};
alexaResponse.response.outputSpeech.type = "SSML";

// Set to impersonated user


userDetails = gs.getUserName();

/** ******************************************************* */
/* Processing */
/** ******************************************************* */
if (alexaRequestType == 'LaunchRequest') {
alexaIntent = 'Help';
}
if (alexaIntent == 'AMAZON.StopIntent'
|| alexaIntent == 'AMAZON.CancelIntent') {
alexaResponse.sessionAttributes = {};
alexaResponse.response.outputSpeech.ssml = "<speak>Cancelled</speak>";
} else if (alexaIntent == 'AMAZON.HelpIntent' || alexaIntent == 'Help') {
invokeHelp(alexaRequestSession, alexaRequest, alexaResponse);
} else {
// Locate handler and run
var cfg = {
filter : function(gr) {
gr.addQuery("intent", "=", alexaIntent);
}
};
var handlers = locateHandlers(cfg);
var handled = false;
// Copy input session variables to ouput response
alexaResponse.sessionAttributes = alexaRequestSessionAttributes;
for (i = 0; i < handlers.length; i++) {
var apiId = handlers[i].className;
var ctx = createContext(apiId, alexaRequestSession, alexaRequest,
alexaResponse);
var functionToCall = handlers[i][alexaIntent];
if (functionToCall) {
logDebugMessage('Calling Process for Handler:' + apiId);
functionToCall(ctx, alexaRequest, alexaResponse);
alexaResponse.sessionAttributes.handledBy = apiId;
handled = true;
break;
}
}
if (!handled) {
invokeHelp(alexaRequestSession, alexaRequest, alexaResponse);
}
}

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
if (!alexaResponse.sessionAttributes) {
alexaResponse.sessionAttributes = {};
}
alexaResponse.sessionAttributes.userDetails = userDetails;

response.setStatus(200);
response.setContentType("application/json;charset=UTF-8");
var writer = response.getStreamWriter();
var responseJSON = global.JSON.stringify(alexaResponse);
logDebugMessage(responseJSON);
writer.writeString(responseJSON);
return;
})(request, response);

9. Click Button

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
Scoped Application: Incident Integration
Scoped Application Creation
1. Repeating the steps for Scoped Application Creation create a new one with values:
Field Value/Instructions

Name Alexa Skills Kit Integration - Incident


Scope x_snc_ask_incident

2. Navigate to the scoped application


3. From the “File” Menu Select “Settings”

4. Go to the “Dependency” tab and click the button. Add the “Alexa Skills Kit
Integration” application.

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
Script Include – Alexa Handler for Incidents
1. Following similar steps as Script Include - DateUtil, create a “Script Include” in the “Alexa
Skills Kit Integration - Incident” scoped application.
2. Enter the following values:
Field Value/Instructions

Application Alexa Skills Kit Integration – Incident (Read Only)


Name AlexaHandler_Incident
Accessible From All Application Scopes
Script
/**********************************************************/
/* Will search for incidents based on commonly mapped */
/* slots of STATE, PRIORITY and CREATED_DATE */
/** ******************************************************* */
function searchIncidents(context, alexaRequest, alexaResponse,
queryPreProcessor) {
/** ******************************************************* */
/* Mappings */
/** ******************************************************* */
var stateMappings = {};
stateMappings["new"] = 1;
stateMappings["in progress"] = 2;
stateMappings["on hold"] = 3;
stateMappings["resolved"] = 6;
stateMappings["closed"] = 7;
stateMappings["cancelled"] = 8;

var priorityMappings = {};


priorityMappings["critical"] = 1;
priorityMappings["high"] = 2;
priorityMappings["moderate"] = 3;
priorityMappings["low"] = 4;
priorityMappings["planning"] = 5;

// Process
var limit = 5;
var gr = new GlideRecordSecure('incident');
gr.setLimit(limit);

if (queryPreProcessor) {
queryPreProcessor.addFilters(gr);
}

var query = "";


var slotState = context.getSlotValue('state');
var mappingState = stateMappings[slotState];
if (mappingState) {
gr.addQuery("incident_state", "=", mappingState);
query += query.length == 0 ? "" : " and ";
query += " state is " + slotState;
}

var slotPriority = context.getSlotValue('priority');


var mappingPriority = priorityMappings[slotPriority];
if (mappingPriority) {
gr.addQuery("priority", "=", mappingPriority);
query += query.length == 0 ? "" : " and ";
query += " priority is " + slotPriority;
}

var dateRange = context.getSlotDateRangeValue('createdAfterDate');


if (dateRange && dateRange.startDate) {
var createdAfter = dateRange.startDate.toISOString();
gr.addQuery("sys_created_on", ">=", createdAfter.substring(0, 10));
query += query.length == 0 ? "" : " and ";
query += " created after <say-as interpret-as=\"date\">"
+ createdAfter.substring(0, 10) + "</say-as>";
}

gr.query();
var out = "<speak>";
var i = 0;
var sysIds = [];

if (gr.hasNext()) {
out += "Results matching " + query + ".";
} else {
out += "No data found for " + query;
}

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
while (gr.next()) {
sysIds[i] = gr.getValue('sys_id');
out += '<say-as interpret-as="cardinal">' + i + '</say-as>. ';
out += gr.getDisplayValue("incident_state") + ' p';
out += gr.getDisplayValue("priority") + ' incident ';
out += gr.getValue("short_description");
out += ".";
i++;
}

out += "</speak>";

// Store data needed to run query again or respond to detail


// requests
context.setSessionAttribute('query', gr.getEncodedQuery());
context.setSessionAttribute('table', gr.getTableName());
context.setSessionAttribute('sysIds', sysIds);
// Output ssml
alexaResponse.response.outputSpeech.ssml = out;
// Keep session around so user can request details on data
alexaResponse.response.shouldEndSession = false;
return;
}

/** ******************************************************* */
/* Will locate a record stored in session state reading */
/* value from the attributes sysIds and table, the index */
/* slot is used to identify the sys id */
/** ******************************************************* */
function locateRecord(context, alexaRequest, alexaResponse) {
var sysIds = context.getSessionAttribute('sysIds');
var table = context.getSessionAttribute('table');
var slotSysIdIndex = context.getSlotValue('index', -1);

context.logDebugMessage("sysIds=" + sysIds);
context.logDebugMessage("table=" + table);
context.logDebugMessage("slotSysIdIndex=" + slotSysIdIndex);

if (sysIds && table && slotSysIdIndex >= 0) {


var sysId = sysIds.length > slotSysIdIndex ? sysIds[slotSysIdIndex]
: null;
if (sysId) {
var gr = new GlideRecordSecure(table);
gr.addQuery("sys_id", '=', sysId);
gr.query();
if (gr.next()) {
return gr;
}
}
}
return null;
}
var AlexaHandler_Incident = Class.create();
AlexaHandler_Incident.prototype = {
type : 'AlexaHandler_Incident',
help : function(context, alexaRequest, alexaResponse) {
var table = context.getSessionAttribute('table');
var helpText = 'You can say, list incidents.\n'
helpText += 'You can list incidents specifying the priority, state, and created date as filters.
For example, list critical closed incidents created after last year.\n';
helpText += 'You can say list my assigned incidents.\n';
helpText += 'You can say list my created incidents.\n';
if ('incident' == table) {
var sysIds = context.getSessionAttribute('sysIds');
if (sysIds && sysIds.length > 0) {
helpText += ' You can get details about an incident. For example, get short
description for incident 1 based on your previous search.\n';
helpText += ' You can add comments to an incident. For example, add comment to
1 my comment is, hello world based on your previous search.\n';
}
}
return helpText;
},
incident_detail : function(context, alexaRequest, alexaResponse) {
var slotFieldName = context.getSlotValue('field');
/** ******************************************************* */
/* Mappings */
/** ******************************************************* */
var fieldMappings = {};
fieldMappings["short description"] = "short_description";
fieldMappings["comments"] = "comments";
fieldMappings["caller"] = "called_id.name";

// Process
var fieldName = fieldMappings[slotFieldName];

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
if (fieldName) {
var gr = locateRecord(context, alexaRequest, alexaResponse);
var out = "<speak>";
if (gr) {
var value = gr.getDisplayValue(fieldName);
out += slotFieldName + " for incident " + gr.getValue("number")
+ ". " + (value ? value : "is empty");
}
out += "</speak>";
alexaResponse.response.outputSpeech.ssml = out;
alexaResponse.response.shouldEndSession = false;
}
return;
},
incident_edit_add_comment : function(context, alexaRequest, alexaResponse) {

var comment = context.getSlotValue('user_comment');


var out = "<speak>";
if (comment) {
var gr = locateRecord(context, alexaRequest, alexaResponse);
gr.comments = comment;
gr.update();
out += "Added comment to incident " + gr.getValue("number") + ". ";
}
out += "</speak>";
alexaResponse.response.outputSpeech.ssml = out;
alexaResponse.response.shouldEndSession = false;
},
incident_search : function(context, alexaRequest, alexaResponse) {
var queryPreProcessor = null;
searchIncidents(context, alexaRequest, alexaResponse, queryPreProcessor);
},
incident_search_fulfiller_my_assigned : function(context, alexaRequest,
alexaResponse) {
var queryPreProcessor = {
addFilters : function(gr) {
gr.addQuery("assigned_to", "=", gs.getUserID());
}
};
searchIncidents(context, alexaRequest, alexaResponse, queryPreProcessor);
},
incident_search_requestor_my_created : function(context, alexaRequest,
alexaResponse) {
var queryPreProcessor = {
addFilters : function(gr) {
gr.addQuery("caller_id", "=", gs.getUserID());
}
};
searchIncidents(context, alexaRequest, alexaResponse, queryPreProcessor);
},
};

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
Scoped Application: Approval Integration
Scoped Application Creation
1. Repeating the steps for Scoped Application Creation create a new one with values:
Field Value/Instructions

Name Alexa Skills Kit Integration - Approval


Scope x_snc_ask_approval

2. Navigate to the scoped application


3. From the “File” Menu Select “Settings”

4. Go to the “Dependency” tab and click the button. Add the “Alexa Skills Kit
Integration” application.

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
Script Include – Alexa Handler for Approvals
1. Following similar steps as Script Include - DateUtil, create a “Script Include” in the “Alexa
Skills Kit Integration - Approval” scoped application
2. Enter following values:
Field Value/Instructions

Application Alexa Skills Kit Integration – Approval (Read Only)


Name AlexaHandler_Approval
Accessible From All Application Scopes
Script
/**********************************************************/
/* Will search for incidents based on commonly mapped */
/* slots of STATE, PRIORITY and CREATED_DATE */
/** ******************************************************* */
function searchApprovals(context, alexaRequest, alexaResponse,
queryPreProcessor) {
// Process
var limit = 5;
var gr = new GlideRecordSecure('sysapproval_approver');
gr.setLimit(limit);

if (queryPreProcessor) {
queryPreProcessor.addFilters(gr);
}

gr.query();
var out = "<speak>";
var i = 0;
var sysIds = [];

if (gr.hasNext()) {
out += "Results Found.";
} else {
out += "No data found.";
}

while (gr.next()) {
sysIds[i] = gr.getValue('sys_id');
out += '<say-as interpret-as="cardinal">' + i + '</say-as>. ';
out += gr.getDisplayValue("sysapproval.short_description");
out += ".";
i++;
}

out += "</speak>";

// Store data needed to run query again or respond to detail


// requests
context.setSessionAttribute('query', gr.getEncodedQuery());
context.setSessionAttribute('table', gr.getTableName());
context.setSessionAttribute('sysIds', sysIds);
// Output ssml
alexaResponse.response.outputSpeech.ssml = out;
// Keep session around so user can request details on data
alexaResponse.response.shouldEndSession = false;
return;
}

/** ******************************************************* */
/* Will locate a record stored in session state reading */
/* value from the attributes sysIds and table, the index */
/* slot is used to identify the sys id */
/** ******************************************************* */
function locateRecord(context, alexaRequest, alexaResponse) {
var sysIds = context.getSessionAttribute('sysIds');
var table = context.getSessionAttribute('table');
var slotSysIdIndex = context.getSlotValue('index', -1);

context.logDebugMessage("sysIds=" + sysIds);
context.logDebugMessage("table=" + table);
context.logDebugMessage("slotSysIdIndex=" + slotSysIdIndex);

if (sysIds && table && slotSysIdIndex >= 0) {


var sysId = sysIds.length > slotSysIdIndex ? sysIds[slotSysIdIndex]
: null;
if (sysId) {
var gr = new GlideRecordSecure(table);
gr.addQuery("sys_id", '=', sysId);
gr.query();
if (gr.next()) {

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
return gr;
}
}
}
return null;
}
var AlexaHandler_Approval = Class.create();
AlexaHandler_Approval.prototype = {
type : 'AlexaHandler_Approval',
help : function(context, alexaRequest, alexaResponse) {
var table = context.getSessionAttribute('table');
var helpText = 'You can say, list my approvals.\n';
if ('sysapproval_approver' == table) {
var sysIds = context.getSessionAttribute('sysIds');
if (sysIds && sysIds.length > 0) {
helpText += ' You can approve or deny a source. For example, approve change 1.\n';
}
}
return helpText;
},
approval_edit_state : function(context, alexaRequest, alexaResponse) {
var slotApprovalState = context.getSlotValue('approval_state');
/** ******************************************************* */
/* Mappings */
/** ******************************************************* */
var approvalStateMappings = {};
approvalStateMappings["approve"] = "approved";
approvalStateMappings["deny"] = "rejected";
approvalStateMappings["reject"] = "rejected";

// Process
var approvalState = approvalStateMappings[slotApprovalState];
if (approvalState) {
var gr = locateRecord(context, alexaRequest, alexaResponse);
gr.state = approvalState;
gr.update();
var out = "<speak>";
if (gr) {
out += approvalState + ' ' + gr.getDisplayValue("sysapproval.number");
}
out += "</speak>";
alexaResponse.response.outputSpeech.ssml = out;
alexaResponse.response.shouldEndSession = false;
}
return;
},
approval_search_manager_my_assigned : function(context, alexaRequest,
alexaResponse) {
var queryPreProcessor = {
addFilters : function(gr) {
gr.addQuery("approver", "=", gs.getUserID());
}
};
searchApprovals(context, alexaRequest, alexaResponse, queryPreProcessor);
},
};

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
Intent Mappings
1. Go to the “Alexa Skills Kit Integration” -> “Intent Handler Registries”

2. Add mappings for the following intents


Intent Script Include

approval_edit_state AlexaHandler_Approval
approval_search_manager_my_assigned AlexaHandler_Approval
incident_search_fulfiller_my_assigned AlexaHandler_Incident

incident_search AlexaHandler_Incident

incident_search_requestor_my_created AlexaHandler_Incident

incident_detail AlexaHandler_Incident

incident_edit_add_comment AlexaHandler_Incident

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
Amazon Setup
Prerequisites
If you have not linked your account, follow the step under Amazon Setup and link it.

Lambda Function – ServiceNow Instance Alexa Proxy


1. Login to the Amazon Lambda Function Creation Console
2. Make sure that your region matches one of the regions specified in Host a Custom Skill
as an AWS Lambda Function. There is a dropdown in the top right bar to select the
region.
3. Click “Create Function”
4. Click “Author from Scratch”
5. Enter following values:
Field Value/Instructions

Name ask_snc_alexa_proxy
Role Create new role from template(s)
Role Name ask_snc_alexa_proxy_user
Policy Templates Simple Microservice permissions

6. Click “Create Function”


7. In the “Configuration” tab enter the following. NOTE: You must replace the code in red
with the appropriate values from your configuration and ServiceNow host details from
above.
i. your.instance = de moni ghtl yecho. ser vi ce- now. com
ii. skill.router.path = / api / x_snc_ask_api /r est api/ skill/r out e
Field Value/Instructions

Code Entry Type Edit code inline


Runtime Node.js 6.10
Handler Index.handler
Script
var querystring = require('querystring');
var https = require('https');
var fs = require('fs');

function buildLinkAccountResponse() {
var alexaResponse = {
version : "1.0",
response : {
card : {
type : "LinkAccount"
},
outputSpeech : {
ssml : "<speak>Please link your account</speak>"
}
}
};
return alexaResponse;
}

exports.handler = function(event, context, callback) {


console.info('Received event', event);

// Grab the access token from the JSON payload


var accessToken = event.session.user.accessToken;

// Check if access token is present and if not ask for account linking
if (!accessToken) {
callback(null, buildLinkAccountResponse());
return;
}

// Null out the access token so we don't get leakage during logging on ServiceNow instance
// end
event.session.user.accessToken = "";
event.context.System.user.accessToken = "";

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
// Build the post string from the modified json payload
var post_data = JSON.stringify(event);

// An object of options to indicate where to post to


var post_options = {
host : '<your.instance>',
port : '443',
path : '<skill.router.path>',
method : 'POST',
headers : {
'Content-Type' : 'application/json',
'Content-Length' : post_data.length,
'Authorization' : 'Bearer ' + accessToken,
}
};

var post_request = https.request(post_options, function(res) {


var body = '';
res.setEncoding('utf8');
res.on('data', function(chunk) {
body += chunk;
});

res.on('end', function() {
var response = JSON.parse(body);
// Custom to ServiceNow REST enpoint
var error = response.error;
if (error) {
// Build link account card
response = buildLinkAccountResponse();
}
response.debug = {};
response.debug.attributes = {};
response.debug.attributes.sn_host = post_options.host;
response.debug.attributes.sn_api = post_options.path;
if (error) {
response.debug.attributes.sn_err = error;
}
console.info('OK: ' + JSON.stringify(response));
callback(null, response);
});

res.on('error', function(e) {
// On error send link acount card
var response = buildLinkAccountResponse();
response.debug = {};
response.debug.attributes = {};
response.debug.attributes.error = e;
console.log('ERROR: ' + JSON.stringify(response));
callback(null, response);
});
});

// post the data


post_request.write(post_data);
post_request.end();
};

8. In the “Triggers” tab


a. Click “Add trigger”
b. Click the empty dotted box
c. Select “Alexa Skills Kit”
d. Click “Submit”
9. From the top of the page take note of the ARN. For example:
10. Click “Save”
11. Test that the proxy works by
a. Use the “Test” tab in the Skill to generate the JSON request
b. Copy the JSON request to create Lambda test event
12. You can debug by using Amazon Cloud Watch

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
Alexa Skill Modification
1. Login to Amazon Alexa Developer Console
2. Navigate to your skill
3. Select the “Configuration” tab
4. Modify the following values:
Field Value/Instructions

Service Endpoint Type AWS Lambda ARN (Amazon Resource Name)


Default <your arn from above>
e.g.

5. Click “Save”

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
Testing and Finalization
1. Go to your skill in the Service Simulator in the Amazon Developer Console.
2. Test your mappings (using the test tab) out with the following phrases
Phrase Notes/Results

Help Help text is returned

List incidents List of incidents returned

List my created incidents List of incidents where the linked account holder is populated in the
caller_id is returned

List my assigned incidents List of incidents where the linked account holder is populated in the
assigned_to is returned

Add comment to 0 my comment is hello world For the cases above where an incident is returned the first incident is
updated with the comment hello world

List my approvals List of approvals where the linked account holder is populated in the
approver is returned
Deny change 0 For the cases above where an approval is returned the first approval is
updated with state deny

1. Once you are happy with your results go back through the code and
a. disable the logging
b. disable any unsecure REST endpoints from previous phases
2. Depending on your use cases you will likely be giving users access to sensitive data.
Please refer to Appendix A for best practices and guidelines to secure your skill further.
3. You can use the logging enabled in the various scripts and examine logs to see where
things may be going wrong. Refer to Appendix B for further help with debugging.

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
Publishing Your Skill
Alexa for Business - General documentation
Publishing Private Skills – Guide to publishing private skills

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
Appendix A - Security
Best Practices
When developing your application follow the Scripted REST APIs good practices. In particular,
use GlideRecordSecure within your scripted handlers as referenced in the Enforce and test
access controls section.

PIN
Alexa devices can be queried by anyone in their vicinity. In some cases, you may want to secure
retrieval or modification of sensitive information with a PIN or some other multi-factor
authentication method. The pin would be setup on the ServiceNow instance and requesting the
pin would be made part of the voice interaction.

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
Appendix B - Troubleshooting
- Some debug code is added to the context object it can be used to log messages to the
error stream. This can be turned on and off with a variable in the Scripted REST API.
- By default, all the permissions should be setup correctly if you follow the steps above.
However, if you see errors in the log like the one below
1. Illegal access to private < xxx > in scope < yyy > being called from scope < zzz >
o Navigate to cross scope application privileges

o Ensure they at minimum match the ones below.


Source Scope Target Scope Target Name Operation Status

Alexa Skills Kit Global ScriptableServiceResultBuilder.setStatus Execute API Allowed


Integration
Alexa Skills Kit Global ScriptableServiceResultStreamWriter.writeString Execute API Allowed
Integration
Alexa Skills Kit Global ScriptableServiceResultBuilder.setContentType Execute API Allowed
Integration
Alexa Skills Kit Global RESTAPIRequestBody Execute API Allowed
Integration
Alexa Skills Kit Global ScriptableServiceResultBuilder.getStreamWriter Execute API Allowed
Integration
Alexa Skills Kit Global RESTAPIRequest Execute API Allowed
Integration
Alexa Skills Kit Global sys_script_include Read Allowed
Integration
Alexa Skills Kit Global ScriptableGlideEvaluator.evaluateScript Execute API Allowed
Integration
Alexa Skills Kit Global incident Read Allowed
Integration -
Incident
Alexa Skills Kit Global GlideRecordSecure.getValue Execute API Allowed
Integration -
Incident
Alexa Skills Kit Global GlideRecord.update Execute API Allowed
Intergation –
Approval
Alexa Skills Kit Global GlideRecordSecure.getValue Execute API Allowed
Intergation –
Approval
Alexa Skills Kit Global sysapproval_approver Read Allowed
Intergation –
Approval
Alexa Skills Kit Global sysapproval_approver Write Allowed
Intergation –
Approval

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.
© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other
ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective
companies with which they are associated.

ServiceNow believes information in this publication is accurate as of its publication date. This publication
could include technical inaccuracies or typographical errors. The information is subject to change without
notice. Changes are periodically added to the information herein; these changes will be incorporated in new
additions of the publication. ServiceNow may make improvements and/or changes in the product(s) and/or
the program(s) described in this publication at any time. Reproduction of this publication without prior
written permission is forbidden. The information in this publication is provided “as is”. ServiceNow makes no
representations or warranties of any kind, with respect to the information in this publication, and specifically
disclaims implied warranties of merchantability or fitness for a purpose.

ServiceNow Platform Integration with Amazon Alexa - Whitepaper


© Copyright 2017 ServiceNow, Inc. All rights reserved. ServiceNow, the ServiceNow logo, and other ServiceNow marks are trademarks and /or registered trademarks of ServiceNow, Inc., in the United
States and/or other countries. Other company and product names may be trademarks of the respective companies with which they are associated.

Anda mungkin juga menyukai