I enjoy having the ability to speak to a room and auto-magically set a cooking timer, play NPR, or check the weather. I then thought it would be amusing to be able to start a workflow as well. If I can order a pizza with my voice, I should be able to start a workflow too!
With the Nintex Workflow Cloud, the ability to externally start a workflow (Nintex Workflow Cloud External Start ) opens up a world cloud of possibilities. With the right tools, anything can become a start event. In this blog post, I'll walk you through how I wired up Alexa to successfully start a Nintex Workflow with code samples and Alexa skills kit examples.
Alexa Skill Development
For demo purposes, I'll be calling into a leave approval workflow built in NWC. To build the Alexa skill that will help kick-off this workflow, we need a few important bits of information:
Skill Info
- Skill Type - What type of skill you're making.
- Select: Custom Interaction Model
- Name - Something of your choosing.
- In my case, "Time off"
- Invocation Name: Something of your choosing.
- In my case, "for time off"
Interaction Model
This defines the voice interface for the Alexa skill and it is built with the following sub-parts:
- Intent Schema - This defines the user intents, which define slots (variables) tied to each intent (function).
- Sample Utterances - This defines the phrases the user can speak and tells Alexa where to expect the given slots for your various intents.
Intent Schema Example
{
"intents":
{
"intent":"DayOff",
"slots":e
{
"name":"Day",
"type":"AMAZON.DATE"
}
]
},
{
"intent":"DaysOff",
"slots":>
{
"name":"DayStart",
"type":"AMAZON.DATE"
},
{
"name":"DayEnd",
"type":"AMAZON.DATE"
}
]
}
]
}
Sample Utterances Example
DayOff ask for time off {Day}
DayOff ask for {Day} off
DayOff I want {Day} off
DayOff I want a vacation {Day}
DayOff I want to take a vacation {Day}
DaysOff ask for time off from {DayStart} to {DayEnd}
DaysOff ask for time off starting {DayStart} until {DayEnd}
DaysOff ask for vacation from {DayStart} to {DayEnd}
DaysOff ask for vacation starting {DayStart} until {DayEnd}
DaysOff I want time off from {DayStart} to {DayEnd}
DaysOff I want time off starting {DayStart} until {DayEnd}
DaysOff I want to take vacation from {DayStart} to {DayEnd}
DaysOff I want to take vacation starting {DayStart} until {DayEnd}
DaysOff I want a vacation from {DayStart} to {DayEnd}
DaysOff I want a vacation starting {DayStart} until {DayEnd}
Configuration
- Service Endpoint Type: HTTPS (I haven't learned AWS Lambda yet)
- HTTPS URL: North America
- This is used to give Alexa interactions a better response time by directing users' requests to a particular geographical region. European designers may want to point to Europe here.
- In my sample, I'm pointing to an endpoint I created at ngrok since my demo service runs locally.
- Note: For test purposes, you may want to point to an HTTPS endpoint inspector such as Hookbin - Capture and Inspect HTTP Requests .
- HTTPS URL: North America
- Account Linking: No
SSL Certificate
- Certificate for NA Endpoint:
- Select: My development endpoint is a sub-domain of a domain that has a wildcard certificate from a certificate authority.
- For example, my endpoint is: https://<random-8char-long-hex-string>.ngrok.io/dayoff
Sample Request from Amazon
Once the above configuration is complete, you can go ahead and test what Amazon will send you. Here's a sample of the JSON body:
{
"version": "1.0",
"session": {
"new": true,
"sessionId": "SessionId.9adaf721-b8bb-482b-b35c-8d54c8bed912",
"application": {
"applicationId": "amzn1.ask.skill.<intentionally_hidden_uuid>"
},
"attributes": {},
"user": {
"userId": "amzn1.ask.account.<intentionally_hidden_token>"
}
},
"request": {
"type": "IntentRequest",
"requestId": "EdwRequestId.f4b67ca1-3420-4d68-89ff-6cc660784d74",
"timestamp": "2016-10-13T01:24:07Z",
"locale": "en-US",
"intent": {
"name": "DayOff",
"slots": {
"Day": {
"name": "Day",
"value": "2016-10-14"
}
}
}
}
}
Nintex Workflow Development
Workflow Example
Now that's the Alexa/Amazon-side of things complete. Let's have a look at our sample workflow below. Quick explanation of the Run-If action. I simply ignore the DayEnd variable if it is less than DayStart (it doesn't make sense to take end a vacation before it starts). When a date isn't defined, it is set to "0001-01-01 00:00:00". That conveniently will fail the Run-If check, allowing me to have both single day off and multiple days off scenarios covered by the same workflow that takes two (start & end) variables.
Start event Configuration
More importantly, let's have a look at the start variables that we've defined in the External Start event below. The DayEnd and DayStart variables are accepted as DateTime (ISO 8601, I believe) variables.
These appear in the external start's swagger API under the following path:
paths/<wf_id/instances>/post/parameters
Swagger API Example
{
"swagger": "2.0",
"info": {
"title": "LeaveApprovalTest-Alexa",
"description": ",
"version": "1.0.0"
},
"schemes":
"https"
],
"basePath": "/api/v1/workflow/published",
"produces":
"application/json"
],
"consumes":
"application/json"
],
"host": "<my-domain>.workflowcloud.com",
"paths": {
"/2da2e395-9e86-4f2d-bb2e-18e499acac82/instances": {
"post": {
"summary": "Starts the workflow",
"description": "Starts workflow: LeaveApprovalTest-Alexa",
"operationId": "wf2da2e395-9e86-4f2d-bb2e-18e499acac82",
"parameters": e
{
"name": "API Parameters",
"required": true,
"in": "body",
"schema": {
"type": "object",
"properties": {
"startData": {
"type": "object",
"properties": {
"se_day_end1": {
"title": "DayEnd",
"description": ",
"type": "string",
"format": "date-time"
},
"se_day_start1": {
"title": "DayStart",
"description": ",
"type": "string",
"format": "date-time"
}
}
},
"options": {
"type": "object",
"properties": {
"callbackUrl": {
"title": "callbackUrl",
"description": "A Url to return the results back (https urls only)",
"type": "string"
}
}
}
}
}
},
{
"name": "token",
"type": "string",
"in": "query",
"description": "A security token to start the workflow"
}
],
"responses": {
"202": {
"description": "Accepted",
"x-ntx-callback-schema": {
"type": "object",
"properties": {
"returnData": {
"type": "object",
"properties": {
"se_day_end1": {
"title": "DayEnd",
"description": ",
"type": "string",
"format": "date-time"
},
"se_day_start1": {
"title": "DayStart",
"description": ",
"type": "string",
"format": "date-time"
},
"c_duration_builder1": {
"title": "DurationBuilder",
"description": ",
"type": "string"
},
"c_placeholder_var1": {
"title": "PlaceholderVar",
"description": ",
"type": "string"
}
}
},
"workflow": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
}
}
}
}
}
},
"400": {
"description": "Bad Request"
},
"404": {
"description": "Not Found"
},
"410": {
"description": "Gone"
},
"429": {
"description": "Too Many Requests"