Hello! 🎉

Hello and welcome to talksuite! Talksuite is our enterprise-grade bot building platform which is designed for us to focus on the security, scalability and privacy of your bot so you can focus on crafting the best experience for your users.

What we hope this documentation can help you with is the technical aspect of building a bot on this platform. We go over the basic concepts, the flow of a dialogue, how to integrate with other services and APIs, and how to create and manage your bot.

talksuite is dedicated to constantly improve and evolve and does release continuously so that you can access new features and fix problems as fast as possible. That being said, we’re dedicated to not breaking any features that you rely on. We make sure that talksuite is always backwards compatible and if we are planning on depreciating a feature, we will communicate it well in advance of doing so.

The talksuite studio allows an author to specify the rules for chatbot-style applications that run on a number of messaging apps including Skype and Slack, as well as our own apps which are available on iOS, Android, Mac and Windows. The rules for the application are expressed in JSON notation.

JSON notation allows structured data to be defined, with nested property name and value pairs. Throughout the document, JSON snippets are displayed in tabs on the right.

The talksuite engine runs the applications you have created.

The documentation has the following sections:

  • Introduction (this section). Explains the concepts and general features of talksuite

  • Triggers. The various ways in which interactions with users can be started

  • Nodes. The actions that can be performed in your application

  • Logic. Calculations available

  • Editing Dialogues. How to create dialogues using the studio dialogue editor

  • Office 365. Connect to Office 365 APIs

  • LUIS Entities. How talksuite can help with understanding natural language

  • System Language. How to translate fixed text messages.

  • Bot Config. How to configure a bot

  • Exercises. A few hands-on exercises to test what you’ve learned

  • Examples. Some example processes

  • Administration. Help for administrators of a talksuite organisation

  • Super Admins. Help for administrators of deployment

  • The Author’s Channel. Testing your bots using talksuite’s own channel

  • The People First app. Using talksuite to provide dialogues for the People First app

  • APIs and Schema. Technical documentation for developers

  • Solutions. Hints and tips and example solutions to the exercises

We’ll use colours throughout to highlight the text:

Hints and tips.

Useful information.

Something may not happen if you try and do this.

Something bad will happen if you do this.

Sometimes we will add items under construction to the document, so you can see in detail what is coming. We’ll mark these with an under construction sign 🚧

Updates

🎉 New Features

  • Snippets. Insert a template of a selected node type into your Dialogue

  • Automate the creation of a Microsoft Bot, removing the need for Azure set-up. You will be able to create a working bot in talksuite with just a few clicks

  • Build Object node. Construct lists and objects with a single node

  • PATCH method in an Action node

🚧 Under Construction

The following features will be available soon:

  • Brief and De-brief. Push information to your users at the start and end of a working day. This respects the users’ boundaries and gives them just the information they need, when they need it, without constant noise throughout the day

Signing in

In order to use talksuite you will need to be invited to an organisation. The invite link will take you to the talksuite welcome screen

Selecting the sign up link will take you to a sign-in screen where you can sign in or sign up to talksuite using the email address displayed on the welcome screen

If you have been invitied as an administrator, you will be taken to the talksuite Administration area where you can invite others to join. If you have been invited as an author, you will be taken to the talksuite studio, where you can build chat-bots

We’d advise using Chrome for the moment. Support for other browsers will be added later.

The sections below explain how to build chat-bots using the studio

Terminology

Here’s a quick list of some of the terms used in talksuite & in the rest of this documentation:

  • Channel. A channel is a messaging app supported by the Microsoft bot framework. Examples are Skype, Slack, Web chat and MHR’s own native iOS and Android apps, used for the people first and iTrent chatbots

  • Organisation. Your organisation. Each talksuite user is invited to join an organisation. In some cases, you may be invited to more than one organisation.

  • Bot. A bot is a configuration record that defines the authentication process and determines which channels can be used. It can also contain constant data. Users communicate with bots via a channel.

  • Dialogue. A dialogue is a configuration record that defines the workflow of an interaction with the user (e.g. when the user types “book a holiday”, ask for a start date and end date and book a holiday using these dates. Confirm to the user that the booking is successful).

  • Node. A node is an individual step within a dialogue (e.g. output a message)

  • Conversation. A conversation is the term for all interactions between a user and a bot. When a user leaves a conversation, all data associated with the dialogues is removed. A user may interact with several dialogues in a conversation.

  • Project. A project is a collection of dialogues. A project can be allocated to a bot, allowing you to control which users can use which dialogues.

Dialogue Structure

A dialogue has the following components:

  • Id gives the dialogue a unique name

  • Project places the dialogue in a project

  • Trigger defines how the dialogue will be started

  • Nodes are the individual steps of the dialogue process

  • Model in which variables are given their initial values

  • Entities in which variables populated from LUIS are defined

  • Priority defines how the dialogue runs in relation to other dialogues. See Dialogue Priority

The id, trigger and nodes must always be present.

In the example on the right, the dialogue is triggered by a LUIS intent “BookAHoliday”.

{
   "id": "example dialogue",
   "priority" : "interrupt",
   "trigger": {
      "type": "intent",
       "intent": "BookAHoliday"
    },
    "nodes": [
      {
        "type": "message",
        "message": "Okay, I'll book {date} for you. Remember your holiday allowance is {allowance}"
      }
     ],
     "model": {
        "allowance": "25"
      },
      "entities": {
         "date": "builtin.datetimeV2.date"
      }
}
  

Types of Data

Introduction

The following types of data can be used within talksuite:

  • Text (e.g. “Hello”)

  • Numbers (e.g. 23, -1, 3.14159)

  • Dates (e.g. 1st December 1965)

  • Times (e.g. 15:30)

  • Date and times (e.g. 1st December 1965 15:30:00)

  • True/False (true or false)

  • Lists Collections of values (e.g. [“red”, white”, “blue”])

  • Objects An object can have multiple properties. For example a person object could have a first name, a last name and a date of birth

You do not need to tell talksuite what type of data you are using. talksuite will look at the data and what you are trying to do with it and act accordingly

The Model

Variables can be initiated with constant values in the model section of a dialogue.

All values in the model must be in quotes, even if they are numbers or true/false values.

  • Numbers must be entered as text (but they will be treated as numbers)

  • Dates should be entered in the format 1 December 1965

  • Times should be entered as in the 24 clock (e.g.17:30)

  • Lists should be entered as comma-separated values enclosed in []

  • Object properties are separated from the object name by a /. E.g. person/firstname

There is no need for items in a list to be of the same type, in the list [“1”, “hello”, “1 December 1965”], the first item will be treated as a number in a numeric operation and the third item will be treated as a date in a date operation

See the “Model” snippet on the right hand menu for an example

Referencing variables

Variables names can be referenced in three ways:

  • When a property always has to be a variable name, just use the variable name. E.g. An output property of a node. - “output” : “myVariable”.

  • Variables can be embedded inside text by enclosing the variables in curly brackets. E.g a message property - “message” : “Hello {myNameVariable}”.

  • When parameters can contain text or a variable, enclose the variable in a var property. E.g. {“var” : “myVariable”}

Object properties are referenced by preceding the object name with the parent properties of the object. E.g. person/address/houseNumber

Items within lists are referenced by the numeric offset from the first item in the list (e.g. myList/0 for the first item, mylist/1 for the second and mylist/{n} for the nth item)

Variable names are case sensitive, so you must match the case of the variable, otherwise it will have no value.

Lists of objects

Lists of objects cannot be created in the model. Objects should be appended to a list using the addItem method See lists . See the List of Objects snippet in the right hand menu for an example or look at list logic

Conversation variables

If a variable is preceded by “conversation/” it will be stored in the database for the duration of the conversation. Conversation variables are available to all dialogues running in the conversation and not running in the background. Conversation variables are not available to other conversations. In other words, the data is private to the user.

If you are storing personal information, take into account the data protection implications.

Bot Constants

If a constant has been defined in your bot configuration record, it is available to all dialogues as a read-only value. To access it, precede the constant name with “Bot/”

Processing data

A variety of operations (e.g. addition, concatenation and sorting) can be performed on data using logic operations

[
    {
      "description" : "Example of the use of a number initiated in the model",
      "type": "operation",
      "output": "number",
      "operation": {
        "+": [
          {
            "var": "number"
          },
          {
            "var": "number"
          }
        ]
      }
    },
    {
    "description" : "Example of the use of a date-time initiated in the model",
      "type": "operation",
      "operation": {
        "method": [
          {
            "var": "date"
          },
          "addDays",
          [
            1
          ]
        ]
      },
      "output": "date"
    },
    {
     "description" : "Example of the use of an element in a list initiated in the model",
      "type": "message",
      "message": "date {date} number {number} list/0 {list/0} "
    }
],
"model": {
    "date": "1 December 1965 12:13":,
    "text": "hello",
    "number": "1.5",
    "list": [
      "1",
      "2",
      "3"
    ]
}
[
   {
     "type": "operation",
     "operation": {
       "method": [
         {
           "var": "people"
         },
         "addItem",
         [
           {
             "var": "janeSmith"
           }
         ]
       ]
     },
     "output": "people"
   },
   {
     "type": "operation",
     "operation": {
       "method": [
         {
           "var": "people"
         },
         "addItem",
         [
           {
             "var": "johnJones"
           }
         ]
       ]
     },
     "output": "people"
   },
   {
     "type": "message",
     "message": "I've built a list containing {people/0/firstname} {people/0/lastname} and {people/1/firstname} {people/1/lastname}"
   }
 ],
 "model": {
   "janeSmith/firstname": "Jane",
   "janeSmith/lastname": "Smith",
   "johnJones/firstname": "John",
   "johnJones/lastname": "Jones"
 }

Logic.

talksuite allows a range of calculations and logic to be performed

We’ve grouped the operations into the following areas:

  • Comparisons are used to compare two values. E.g. is one value the same as another? Is one number greater than another?

  • Logic allows comparisons to be combined with logical operations (and, or and not). E.g. is a number greater than 10 and less than 20?

  • Date calculations. A variety of date and time calculations are available, including getting the current date and time, timezones, date formatting, difference between dates and shifting dates by a period

  • Text operations. You can combine and split text items, search within text, get the length of a text item and change case

  • Arithmetic. You can add, subtract, divide, multiply, round and calculate a remainder from a division

  • Lists. You can build and modify a list, sort and filter lists and construct a list from a text item with a separator

  • XML. You can convert XML and escaped XML to a talksuite object

Built-in Variables

Here are the built-in variables available to you:

Regional settings

  • conversation.settings/timezone User’s time zone as an IANA location (e.g. Europe/London). Read/Write. Defaults to the bot time zone
  • conversation.settings/region User’s culture (language/country e.g. en-GB). Read/Write. Defaults to the bot region. This controls which Language store will be used

API HTTP Status Code

  • dialogue/lastApiStatusCode The HTTP status code of the last call to an API. Read only

Last Utterance

  • dialogue/triggerUtterance The user input that caused the dialogue to run. Read only

Verbose Mode

  • conversation.settings/verboseMode If set to true, debugging information will be displayed for each node executed. Read/Write

Authentication

  • user/AuthenticationToken Authentication token for a secure API. Read only. Can only be used in an action node header
  • user/AuthenticatedProviders A list of the authentication provider names (from bot config) that the user is currently signed into. Read only
  • user.settings/identifiers A list of unique identifiers for a user, used in external initiation of dialogues. Read/Write

Bot Configuration

  • bot.settings/botId The id of the current bot. Read only
  • bot.settings/botHost Where the bot and dialogues are hosted. Read only
  • bot.settings/organisationId The id of the organisation in which the bot is stored. Read only

Brief and Debrief Activation 🚧

  • conversation.settings/brief/active If set to false, the daily brief will not run in this conversation (i.e. for this bot user)

  • conversation.settings/debrief/active If set to false, the daily debrief will not run in this conversation

Node Flow

By default, once a node has run, the next node in the dialogue is run (e.g. node A, thenn node B, then node C)

You can alter the flow, so that node C is run after node A, set the id property of node C and the nextNode property of node A to the same value.

To do this:

  • Set an id property on node C (to, say “Node C”)

  • Set the nextNode property of node A to “Node C”

You can only move forward in a dialogue, never backwards.

If you want to stop executing a dialogue, set the nextNode property to “dialogue.stop”. If you are in a nested dialogue and don’t want the calling dialogue(s) to resume, use the Event node with an event of “endDialogue”.

If you want to skip a node, set the skip property of the node to true. The node will not run, but the nextNode property of the node will still be used to determine the next node.

If you want to run another dialogue, use the Dialogue node. When the called dialogue ends, the calling dialogue will resume.

If you want to loop through a list of records, use the Sequence Dialogue node.

If you want to loop until a condition is met, use the Repeat Dialogue node.

If you want to loop through a list of records and return data not contained within the record set, you’ll need to use the Repeat Dialogue node.

[
    {
      "type": "message",
      "message": "First line displayed",
      "nextNode": "second line"
    },
    {
      "type": "message",
      "message": "Not executed",
      "nextNode": "second line"
    },
    {
      "id": "second line",
      "type": "message",
      "message": "Second line displayed"
    },
    {
      "type": "message",
      "message": "Third line displayed",
      "nextNode": "dialogue.stop"
    },
    {
      "type": "message",
      "message": "Not executed"
    }
]
[
      {
        "type": "dialogue",
        "dialogueId": "example called dialogue",
        "description": "passOut and getBack are in this dialogue. receiveVar and outputVar are in the called dialogue",
        "inputs": {
          "receiveVar": {
            "var": "passOut"
          }
        },
        "outputs": {
          "getBack": {
            "var": "outputVar"
          }
        }  
      },
      {
        "type": "sequenceDialogue",
        "description": "people is a list of objects in this dialogue. person is populated in the called dialogue with each people record in turn",
        "dialogueId": "example called for each record",
        "inputItem": "person",
        "listName": "people"          
      },
      {
        "type": "repeatDialogue",
        "dialogueId": "example called repeatedly",
        "description" : "i is a variable in the calling dialogue, passed into the called dialogue as index. finished is a boolean in both dialogues",
        "inputs": {
          "index": {
            "var": "i"
          }
        },
        "outputs": {
          "i": {
            "var": "index"
          },
          "finished": {
            "var": "finished"
          }
        },
        "repeatUntil": {
          "===": [
            {
              "var": "finished"
            },
            "finished"
          ]
        }
      }       
]    
{
    "data": {
      "attributes": {
        "json": {
          "id": "example called dialogue",
          "trigger": {
            "type": "nestedDialogue"
          },
          "nodes": [
            {
              "type": "operation",
              "description" : "receiveVar is passed in and outputVar is returned",
              "operation": {
                "var": "receiveVar"
              },
              "output": "outputVar"
            }
          ]
        }
      }
    }
}
{
   "data": {
      "attributes": {
        "json": {
          "id": "example called for each record",
          "trigger": {
            "type": "nestedDialogue"
          },
          "nodes": [
            {
              "description" : "person is populated for each record in the list in the calling dialogue",
              "type": "message",
              "message": "{person/firstName}"
              
            }
          ]
        }
      }
    }
}
{
    "data": {
      "attributes": {
        "json": {
          "id": "example called repeatedly",
          "trigger": {
            "type": "nestedDialogue"
          },
          "nodes": [
            {
              "type": "operation",
              "output": "index",
              "operation": {
                "+": [
                  {
                    "var": "index"
                  },
                  1
                ]
              },
              "nextNodeIndex": 1
            },
            {
              "type": "choicePrompt",
              "message": "{index}: I'll keep asking you this until you select Stop",
              "retryMessage": "?",
              "listName": "menuButtons",
              "output": "menuChoice",
              "nextNodeIndex": 2
            },
            {
              "type": "decision",
              "rule": {
                "===": [
                  {
                    "var": "menuChoice"
                  },
                  {
                    "var": "menuButtons/0"
                  }
                ]
              },
              "passNodeIndex": 3,
              "failNodeIndex": null
            },
            {
              "type": "operation",
              "operation": {
                "var": "finishedText"
              },
              "output": "finished",
              "nextNodeIndex": null
            }
          ],
          "model": {
            "finishedText": "finished",
            "menuButtons": [
              "Stop",
              "Continue"
            ]
          }
        }
      }
    }
}

Language

To make your dialogues easily translatable into different languages, don’t embed natural language text in your dialogue. Place them in a language store.

You can have a separate language store for each country/language you support (e.g. en-GB for British English, en-US for US English). Each item in the store consists of a name and value. The name is used to reference the text in a dialogue. The value contains the text in the appropriate language/culture.

If you want language to vary in order to make your bot seem more human, you can assign an array of text items. An item will be randomly selected from the list each time it is referenced.

In the examples to the right, the definition of a store is shown along with its use in dialogue trigger node. The dialogue will trigger when any of “hello”, “hi” or “howdy” is entered. The dialogue will respond with a message chosen randomly from list list “bye”, “cherrio” etc.

Built-in messages

The systemText section allows you to define alternative texts for any fixed messages output by talksuite in the channel.

The fixed messages are:

  • Push Notification Text Applies to the people first and iTrent apps only. Sets the default push notification text for the language. This is the notifcation message displayed on the user’s phone when a message is received, the app is closed and there is no suitable text to display (e.g. when an image is displayed)

  • Dialogue Not Found Sets the default message output when a user’s input does not trigger a dialogue.

The image below shows the language records for British English (en-GB) and French (fr-FR).

{
  "systemTexts": {
    "dialogueNotFound": [
      "I'm sorry. I don't understand that"
    ]
    "notificationMessage": [
      "New message"
    ]
  },
  "texts": [
    {
      "name": "hello",
      "values": [
        "hello",
        "hi",
        "howdy"
      ]
    },
    {
      "name": "goodbye",
      "values": [
        "bye",
        "cheerio",
        "see ya",
        "goodbye",
        "laters"
      ]
    }
  ],
  "region": "en-GB"
}
{
  "trigger" : "message",
  "values" : [{"var" : "{language/hello}"],
}
"nodes" : [
  {
    "type": "message",
    "message" : "{language/goodbye}"          
  }
]

Projects

Use projects to break up your applications into manageable chunks, control what features of the application are available to a customer and to hold multiple versions of software.

Language records and dialogues can be placed inside a project. Dialogue ID and trigger validation will only be performed for dialogues in the project and dialogues without a project (i.e. global dialogues).

A project’s only attribute is a name (in addition to the generated key). Adding a projectId property with the project key to a language or dialogue record adds the item to the project.

In order for the dialogues in a project to run, the project must be added to the list of bot projects in the Bot config record.

Any dialogues which are not in a project are held in a global area.

A Project is shown below, expanded to show its language files and dialogues.

Introducing LUIS

Introduction

Microsoft’s Language Understanding Intelligent Service (LUIS) application can be trained to understand natural language. It determines the intention of the user from a sentence entered by the user. Each intention understood is referred to as an intent. You can trigger a dialogue when LUIS understands an intent using an intent trigger

For example, LUIS might have been trained to understand that the sentences “I want to book a holiday” and “Book me a holiday” all map to the intent bookHoliday.

A sentence entered by the user is referred to as an utterance. Each user utterance is evaluated against all the configured intents. A score for the utterance is assigned to each intent based on the strength of the match. The intent with the highest score is returned to the calling application. If the highest scoring intent is None, LUIS has come to the conclusion that the user’s intention is not amongst the configured intents.

LUIS can also extract information from the utterance. The types of information returned are referred to as entities. For example if location is an entity and LUIS has been trained to extract a location from the bookHoliday intent, the utterance “Book me a holiday in Cornwall” would return the intent of bookHoliday and an entity of location with a value of Cornwall. You can place entities directly into variables or you get them from the raw LUIS response. The LUIS date-time built-in entity type understands a wide range of date, time and duration phrases. However, it can return very many different types of data, depending on the utterance. In order to help the author make sense of all the possible responses, you can use the NLP Entities node

More information can be found at the LUIS home page

Bot Config

Bot configuration allows you to define the following:

  • Bot Registration and Localisation, language and timezone setting, plus the basic set-up to allow messages to move between the user’s device and talksuite

  • Authentiation Providers. A list of applications the bot can connect to along with the authentication parameters

  • Constants. Constant values available to dialogues

  • Natural Language. Determines which LUIS app to use for natural language understanding

  • Specify which Projects are active for a bot

  • There are also some other optional properties available

Patterns

talksuite can match pattterns in text. Pattern matching works in two ways. You can trigger a dialogue when a pattern is matched. You can also match a pattern in a dialogue using a pattern match operation

A pattern is defined using a regular expression. There are several dialects of regular expressions. We use the .NET version. Here are some simple examples:

  • hello matches text containing hello
  • (?i)hello matches text containing hello in any case
  • ^hello matches text starting with hello
  • hello$ matches text ending with hello
  • (hello|goodbye) matches text which contains either hello or goodbye
  • \\d matches text which contains a digit.

Where a backslash is required by the regular expression language, you need to use two backslashes to prevent JSON interpreting it as a special character

You can find more detail on the language and test your patterns at regexstorm.net

Dialogue Priority

Dialogue property to control the way in which a dialogue runs

Properties
priority

set to interrupt, supersede or background

By default a dialogue runs in the context of a conversation (so that it can interact with the user via messages and prompts etc.) and can only start once the current dialogue has finished.

Using the dialogue priority property, a dialogue can be made to run in three other ways:

  • An interrupt dialogue will suspend the running dialogue, run itself, then resume the original dialogue. Therefore, you could trigger a dialogue based on phrase, even if the existing dialogue is waiting for prompt input. This is useful for initiating dialogue cancellation or help dialogues.

  • A supersede dialogue will stop the running dialogue, run itself and not return to the original dialogue

  • A background dialogue, as the name suggests, runs in the background while the current dialogue is allowed to continue running. Background dialogues do not run in the context of a conversation, so cannot use card, message or prompt nodes or conversation variables.

If a dialogue does not need to run in the context of a conversation, setting it to background may improve performance, as it will not interfere with the conversation dialogues. Validation will be performed to ensure that the dialogue and any dialogues it calls do not include nodes or variables that need to be in a conversation.

{
  "id": "interrupt message",
  "priority": "interrupt",
  "trigger": {
    "type": "message",
    "values": [
      "there is an emergency"
    ]
  },
  "nodes": [
    {
      "type": "message",
      "message": "DON'T PANIC!"
    }
  ]
}

Brief and De-brief 🚧

talksuite supports daily brief and de-brief processes. These are designed to help your users start and end their day and to shield them from noise for the rest of the day, allowing them to get on with their work.

You decide what goes into the brief and de-brief and when they run. The processes run at the specified time in the user’s own time zone.

In order to set up a brief or debrief:

  • Configure a start time for each bot

  • Define the dialogues to run in the brief or debrief by defining Process triggers

  • Before a brief or debrief is run, the user must agree to start the process.

Trigger Types

Here is a summary of the different ways that a dialogue can be started

Type Trigger Mechanism
Message Exact match on a trigger phrase typed by the user
Pattern Match on text contained within a phrase typed by the user
Intent User input is matched against a LUIS intent
Attachment A file is uploaded by the user
Nested Dialogue A dialogue is called by another diaogue
No Trigger Match A user types something that does not trigger a dialogue by any of the above mechanisms
External Event A third party application requests a dialogue to run
Converation start A user uses the bot for the first time
Custom Event The (directline) client app requests a dialogue to run
Process Include a dialogue in a brief or debrief process

Message and pattern triggers will be checked for before talksuite goes to LUIS to match on an intent

Message

Starts a dialogue upon a successful keyword match

Properties
type

message

values

An array of strings or language file references to match the message against.

A trigger type of message will trigger a dialogue on a specific phrase or set of phrases. The examples to the right show:

  • A trigger on a single phrase (e.g. Hello)

  • A trigger on multiple phrases (e.g. Hello, Hi there)

  • A trigger on phrases from a language store (e.g “hello” or “hi there” in English, or “bonjour” or “salut” in French)

The match is against the whole input, so input of “hello bot” would not match the Hello trigger. Matching is not case sensitive, so HELLO will match Hello

If you want to match on input that contains a phrase (e.g anything starting with Hello), use a pattern trigger

Message and pattern trigger matching occurs before talksuite goes to LUIS to match intents.

There is also a node type of message. This sends a message to the user, whereas the message trigger enables the user to send a message to talksuite to start a dialogue

"trigger": {
  "type": "message",
  "values": [
    "Hello"          
  ]
}
"trigger": {
  "type": "message",
  "values": [
    "Hello",
    "Hi there"
    
  ]
}
"trigger": {
  "type": "message",
  "values": [
    { "var": "language/greetings" }
  ]
}

Intent

Starts a dialogue when LUIS matches input to an intent

Properties
type

intent

intent

The name of the LUIS intent you want this dialogue to trigger on.

output

Raw Luis response

A dialogue can be triggered when user input matches a LUIS intent. Firstly you must create a LUIS app and intent and link it to your bot by setting up a bot Natural Language section. Then, all text entered by a user outside of a dialogue is sent to LUIS. If the highest scoring intent is not “None” and you’ve defined a trigger for that intent, that dialogue is started.

LUIS can pick out key variables from a sentence (known as entities). These variables can be received by your dialogue. Create an “entities” section in your dialogue (at the same level as “model” and “nodes”). Create properties in that section. Each property name will be a variable in your dialogue. Each property value is the name of a LUIS entity.

Alternatively you can parse the raw LUIS response yourself. Add an output property to the trigger section of the dialogue. The value of the property will be a variable that will be populated with the LUIS response.

If you are using LUIS built-in date-time entities, you may find that LUIS returns a variety of different responses (e.g. date-times or date then time, a date range or two dates). We’ve written an NLP Entities node to help you extract just the data you need

"trigger": {
  "type": "intent",
  "intent": "sayHello"
}
"nodes" :[
    {
      "type" : "message",
      "message" : "hello {name}"
    }
]
"entities" : {
  "name" : "nameEntity"
}      
"trigger": {
  "type": "intent",
  "intent": "sayHello",
  "output": "response"
}
"nodes" :[
    {
      "type" : "message",
      "message" : "hello {response/entities/0/entity}"
    }
]            

Attachment

Starts a dialogue upon an attachment being received with a successful content type match.

Properties
type

attachment

contentTypes

An array of valid mime types that triggers this dialogue.

output

An object that maps the incoming properties of the attachment to local variables.

A dialogue can be triggered by the upload of a file of a particular type. In the example, the dialogue is triggered by the upload of a jpeg file.

// Snippet
"trigger": {
    "type": "attachment",
    "contentTypes": [ "image/jpeg" ],
    "output": "data"      
    }
}

Pattern

Starts a dialogue upon a successful pattern match

Properties
type

pattern

values

An array of strings or language file references. Each item should be a .NET regular expression. The dialogue is run if the user input matches the pattern

The snippet starts a dialogue matching on input that starts with “approve” in any case.

See Patterns for more information on patterns.

Pattern matching occurs before the phrase is sent to LUIS, so watch out for patterns that stop appropriate LUIS intents firing.

// Snippet
"trigger": {
  "type": "pattern",
  "values": [
    "^(?i)approve"
  ]
}

Nested Dialogues

A dialogue called from another dialogue

Properties
type

nestedDialogue

A dialogue of trigger type nestedDialogue can only be called from another dialogue. The following nodes can be used to call a nested dialogue

[
      {
        "type": "dialogue",
        "dialogueId": "example called dialogue",
        "description": "passOut and getBack are in this dialogue. receiveVar and outputVar are in the called dialogue",
        "inputs": {
          "receiveVar": {
            "var": "passOut"
          }
        },
        "outputs": {
          "getBack": {
            "var": "outputVar"
          }
        }  
      },
      {
        "type": "sequenceDialogue",
        "description": "people is a list of objects in this dialogue. person is populated in the called dialogue with each people record in turn",
        "dialogueId": "example called for each record",
        "inputItem": "person",
        "listName": "people"          
      },
      {
        "type": "repeatDialogue",
        "dialogueId": "example called repeatedly",
        "description" : "i is a variable in the calling dialogue, passed into the called dialogue as index. finished is a boolean in both dialogues",
        "inputs": {
          "index": {
            "var": "i"
          }
        },
        "outputs": {
          "i": {
            "var": "index"
          },
          "finished": {
            "var": "finished"
          }
        },
        "repeatUntil": {
          "===": [
            {
              "var": "finished"
            },
            "finished"
          ]
        }
      }       
]    
{
  "id": "example called dialogue",
  "trigger": {
    "type": "nestedDialogue"
   },
  "nodes": [
    {
     "type": "operation",
      "description" : "receiveVar is passed in and outputVar is returned",
       "operation": {
         "var": "receiveVar"
       },
      "output": "outputVar"
    }
  ]
}            
{
  "id": "example called for each record",
  "trigger": {
    "type": "nestedDialogue"
  },
  "nodes": [
    {
      "description" : "person is populated for each record in the list in the calling dialogue",
      "type": "message",
      "message": "{person/firstName}"
    }
  ]
}
{
          "id": "example called repeatedly",
          "trigger": {
            "type": "nestedDialogue"
          },
          "nodes": [
            {
              "type": "operation",
              "output": "index",
              "operation": {
                "+": [
                  {
                    "var": "index"
                  },
                  1
                ]
              }
            },
            {
              "type": "choicePrompt",
              "message": "{index}: I'll keep asking you this until you select Stop",
              "retryMessage": "?",
              "listName": "menuButtons",
              "output": "menuChoice"
            },
            {
              "type": "decision",
              "rule": {
                "===": [
                  {
                    "var": "menuChoice"
                  },
                  {
                    "var": "menuButtons/0"
                  }
                ]
              },
              
              "failNode": "dialog.stop"
            },
            {
              "type": "operation",
              "operation": {
                "var": "finishedText"
              },
              "output": "finished"
            }
          ],
          "model": {
            "finishedText": "finished",
            "menuButtons": [
              "Stop",
              "Continue"
            ]
          }
        }
      }
    }
}

No Trigger Match

Runs when no other triggers are matched (i.e the bot does not understand what has been typed)

Properties
type

event

event

noTriggerMatch

output

Raw Luis response

This event enables you to control what response the user gets when talksuite cannot match any other dialogue. The response could be a simple “I don’t understand” message or perhaps a menu of available options.

If there is no noTriggerMatch dialogue, a default “I’m sorry I didn’t understand that message (in English only) will be displayed

The built-in variable ‘dialogue/triggerUtterance’ can be used to find out what the user typed.

// Snippet
{
  "id": "Example no trigger match",
  "trigger": {
    "type": "event",
     "event": "noTriggerMatch"
  },
  "nodes": [
     {
       "type": "message",
       "message": "What does {dialogue/triggerUtterance} mean?"
     }
   ]
}            

External Event

A dialogue called from outside the system

Properties
type

customEvent

event

Event name

output

Variable populated with event data

In order to trigger dialogues from outside talksuite, you will need to define a webHookSecretKey in your bot.

The API to call is /organisations/org-id/bots/bot-id/dialogue. Where org-id is the id of you organisation and bot-id is the id of your bot. The request should be a POST

The body of the document should consist of the following properties:

  • name Event name. The dialogue with an event trigger with this name will be called.

  • address User identifier. The dialogue will be run in the context of a user with a user.setting/identifier property which matches the address. user.settings/identifier is a list, so a user can have different identifiers for different authentication providers

  • value A JSON block which is passed into the dialogue

External events can also be used by the People First app to communicate with talksuite. See People First events

{
  "name": "hmactest",
  "address": "john.smith",
  "value": {
          "message":  "Hello John"
  }
}    
{
      "type": "operation",
      "operation": {
        "method": [
          {
            "var": "User.Settings/Identifiers"
          },
          "addItem",
          [
            {
              "var": "myId"
            }
          ]
        ]
      },
      "output": "User.Settings/Identifiers"
},
{
  "id": "webhook-test",
  "trigger": {
    "type": "customEvent",
    "name": "hmactest",
    "output": "data"
  },
  "nodes": [
    {
      "type": "message",
      "message": "This is your message {data/message}"
    }
  ]        
}

Conversation Start

A dialogue called at the start of a conversation

Properties
type

event

event

conversationStart

An event dialogue with an event name of “conversationStart” will be run at the start of a conversation

You can use this to store infomration about a person that never changes (e.g. employee number)

{
  "trigger": "event",
  "event": "conversationStart"        
}    

Custom Event

A dialogue called on receipt of a custom event from the [People First app](#peoplefirstintro)

Properties
type

customEvent

name

Event name

output

Event data

The People First app will send an introduction custom event after the user has signed on. An event will also be sent when the device locale or time zone changes.

Other HR-specifc events will also be sent

{
  "type": "customEvent",
  "name": "introduction"        
}    
{
  "id": "UpdateRegionAndTimeZone",
  "trigger": {
    "type": "customEvent",
    "name": "updateLocaleAndTimeZone",
    "output": "RegionTimeZoneData"
  },
  "nodes": [
    {
      "type": "operation",
      "output": "language",
      "operation": {
        "substr": [
          {
            "var": "RegionTimeZoneData/locale"
          },
          0,
          2
        ]
      }
    },
    {
      "type": "operation",
      "output": "country",
      "operation": {
        "substr": [
          {
            "var": "RegionTimeZoneData/locale"
          },
          3,
          2
        ]
      }
    },
    {
      "type": "operation",
      "operation": {
        "cat": [
          {
            "var": "language"
          },
          "-",
          {
            "var": "country"
          }
        ]
      },
      "output": "botlocale"
    },
    {
      "type": "operation",
      "operation": {
        "var": "RegionTimeZoneData/timeZone"
      },
      "output": "conversation.settings/timeZone"
    },
    {
      "type": "operation",
      "operation": {
        "var": "botlocale"
      },
      "output": "conversation.settings/region"
    }
  ]
}  

Process 🚧

Forms part of a set of dialogues that will run for all users at a specified time each day

Properties
type

process

name

brief or debrief

precedence

The order in which the dialogue will display within the process. Options are start, urgent, standard, additional or finish

A brief of debrief can be configured to run at a set time every day by configuring a bot process

A brief or debrief consists of a set of dialogues which run with a pre-set precedence. The set of dialogues is defined by the process triggers. The precedence is defined as a property within the trigger

"trigger": {
  "type": "process",
  "name": "brief",          
  "precedence" : "start" 
}

Getting Started

This section contain examples of all the types of nodes. When you want to add a node to a dialogue, you don’t have to type it all in by hand. You can use Snippets to paste template JSON for the node.

A few common actions are listed below, along with the node types that might be helpful

I want to display a message

I want to capture typed user input

I want to display a card

I want to capture user input via buttons

I want to validate data

I want to perform a calculation

I want to set up data

I want to talk to APIs

I want to upload a file

I want to call another dialogue

I want to create a loop

I want to cancel a dialogue

I want to end a conversation

I want to send information to a directline client app

I want to extract data from a LUIS response

Message

Send a message to the user

Properties
type

message

message

The message sent to the user. Can be a mixture of fixed text, references to variables or a reference to a language file

customContent

Enables the display of Bespoke Cards

description

Text to help explain what the node is doing

id

A unique identifier. See Node Flow for details

nextNode

See Node Flow for details

skip

If set to true, the node will not be executed. See Node Flow for details

Use \n\r to split a message over multiple lines. Outputting a single message with multple lines will eliminate the delay the user experiences after each message is displayed.

Use the customContent property if you want to send custom JSON (for an MHR app custom card, for example) to the channel, rather than a simple text message. For more detail see MHR Cards

There is also a trigger type of message. This enables the user to start a dialogue by sending a message to talksuite, whereas the message node sends a message to the user

{
  "type": "message",
  "message": "Hello! I'm a bot!"
}

String Prompt

Ask the user for text input

Properties
type

stringPrompt

message

The message output to the user. A string or reference to a language file

retryMessage

Message to be output if validation fails.

maxRetries

Maximum number of retries allowed after validation failure. Default value is 3

continueOnFail

If set to true after the maximum failed attemps, the dialogue will continue with null output. If false, the dialogue will stop. Default is false

validation

Logical expression evaluating to true or false using JSON logic. This will normally be a comparison. The retryMessage will be displayed if the expression evaluates to false

customContent

Enables the display of Bespoke Cards

output

Variable name in which the user input will be stored. If not supplied the user will not be prompted for input

description

Text to help explain what the node is doing

id

A unique identifier. See Node Flow for details

nextNode

See Node Flow for details

skip

If set to true, the node will not be executed. See Node Flow for details

If the output variable is populated, the user will not be prompted. Therefore if you have two prompt nodes in the same dialogue with the same output variable, the second prompt will never be run.

The retryMessage property is mandatory, but is not used unless you have a validation rule in the node.

{
  "type": "stringPrompt",
  "message": "Enter your name",
  "retryMessage": "Enter your name",
  "output": "name"
}
{
  "type": "stringPrompt",
  "message": "Please enter your sort code (xx-xx-xx)",
  "retryMessage": "Please enter a string like xx-xx-xx",
  "output": "sortCode",
  "validation": {
    "method": [
       { "var": "sortCode" },
       "matchesPattern",
        [ "^\\d\\d-\\d\\d-\\d\\d$" ]
     ]
  }
}

Number Prompt

Ask the user for numeric input

Properties
type

numberPrompt

message

The message output to the user. A string or reference to a language file

retryMessage

Message to be output if validation fails. talksuite will validate that the input is a valid number. Additional validation can be performed using the validation property

maxRetries

Maximum number of retries allowed after validation failure. Default value is 3

continueOnFail

If set to true after the maximum failed attemps, the dialogue will continue with null output. If false, the dialogue will stop. Default is false

validation

Logical expression evaluating to true or false using JSON logic. This will normally be a comparison. The retryMessage will be displayed if the expression evaluates to false

customContent

Enables the display of Bespoke Cards

output

Variable name in which the user input will be stored.

description

Text to help explain what the node is doing

id

A unique identifier. See Node Flow for details

nextNode

See Node Flow for details

skip

If set to true, the node will not be executed. See Node Flow for details

If the output variable is populated, the user will not be prompted. Therefore if you have two prompt nodes in the same dialogue with the same output variable, the second prompt will never be run.

{
  "type": "numberPrompt",
  "message": "Please enter your age",
  "retryMessage": "That's not a valid number",
  "output": "age"
}
{
      "type": "numberPrompt",
      "message": "How old are you, if you don't mind me asking",
      "retryMessage": "That's not a valid age. Plese try again",
      "output": "age",
      "validation": {
        ">=": [
          {
            "var": "age"
          },
          0
        ]
}

Date Prompt

Ask the user for date input

Properties
type

datePrompt

message

The message output to the user. A string or reference to a language file

retryMessage

Message to be output if validation fails. talksuite will validate that the input in a valid date. Additional validation can be performed using the validation property

maxRetries

Maximum number of retries allowed after validation failure. Default value is 3

continueOnFail

If set to true after the maximum failed attemps, the dialogue will continue with null output. If false, the dialogue will stop. Default is false

validation

Logical expression evaluating to true or false using JSON logic. This will normally be a comparison. The retryMessage will be displayed if the expression evaluates to false

customContent

Enables the display of Bespoke Cards

output

Variable name in which the user input will be stored.

context

When date input is ambiguous (e.g. Friday or 1st June), if the context is set to “earliest” the earlier possible date is chosen (e.g. last Friday, last June). If the context is “latest” then the later date is chosen (e.g. next Friday, next June)

description

Text to help explain what the node is doing

id

A unique identifier. See Node Flow for details

nextNode

See Node Flow for details

skip

If set to true, the node will not be executed. See Node Flow for details

If the output variable is populated, the user will not be prompted. Therefore if you have two prompt nodes in the same dialogue with the same output variable, the second prompt will never be run.

{
  "type": "datePrompt",
  "message": "Enter the start date of your holiday",
  "retryMessage": "That's not a valid date",
  "output": "startDate"
}
[
    {
      "type": "operation",
      "operation": {
        "Date.currentDate": []
      },
      "output": "DateToday"
    },
    {
      "type": "datePrompt",
      "message": "Enter your overtime start date",
      "retryMessage": "Cannot be in the future", 
      "output": "startDate",
      "context" : "earliest",
      "validation": {
        "<=": [
          {
            "var": "startDate"
          },
          {
            "var": "DateToday"
          }
        ]
      }
    }
]

Date-time Prompt

Ask the user for date-time input

Properties
type

datePrompt

message

The message output to the user. A string or reference to a language file

retryMessage

Message to be output if validation fails. talksuite will validate that the input is a valid date-time. Additional validation can be performed using the validation property

maxRetries

Maximum number of retries allowed after validation failure. Default value is 3

continueOnFail

If set to true after the maximum failed attemps, the dialogue will continue with null output. If false, the dialogue will stop. Default is false

validation

Logical expression evaluating to true or false using JSON logic. This will normally be a comparison. The retryMessage will be displayed if the expression evaluates to false

customContent

Enables the display of Bespoke Cards

output

Variable name in which the user input will be stored.

context

When date input is ambiguous (e.g. Friday or 1st June), if the context is set to “earliest” the earlier possible date is chosen (e.g. last Friday, last June). If the context is “latest” then the later date is chosen (e.g. next Friday, next June)

description

Text to help explain what the node is doing

id

A unique identifier. See Node Flow for details

nextNode

See Node Flow for details

skip

If set to true, the node will not be executed. See Node Flow for details

If the output variable is populated, the user will not be prompted. Therefore if you have two prompt nodes in the same dialogue with the same output variable, the second prompt will never be run.

If you want to validate that the input is not in the past (or future), obtain the current date and time in the validation section of the prompt, rather than in a preceding node. This will refresh the current time each time the user inputs a value, so if the user leave the system for a while, the current date won’t get stale. See the “Validate not in past” snippet

{
  "type": "dateTimePrompt",
  "message": "Enter the start date and time of your holiday",
  "retryMessage": "That's not a valid date",
  "output": "startDate"
}
{
  "type": "dateTimePrompt",
  "message": "When do you want to book your appointment?",
  "retryMessage": "You cannot book an appointment in the past",
  "output": "appointment",
  "validation": {
    ">": [
      {
        "var": "appointment"
      },
      {
        "DateTime.currentDateTime": []
      }
    ]
  }
}

Time Prompt

Ask the user for time input

Properties
type

timePrompt

message

The message output to the user. A string or reference to a language file

retryMessage

Message to be output if validation fails. talksuite will validate that the input is a valid time. Additional validation can be performed by the validation property

maxRetries

Maximum number of retries allowed after validation failure. Default value is 3

continueOnFail

If set to true after the maximum failed attemps, the dialogue will continue with null output. If false, the dialogue will stop. Default is false

validation

Logical expression evaluating to true or false using JSON logic. This will normally be a comparison. The retryMessage will be displayed if the expression evaluates to false

customContent

Enables the display of Bespoke Cards

output

Variable name in which the user input will be stored.

description

Text to help explain what the node is doing

id

A unique identifier. See Node Flow for details

nextNode

See Node Flow for details

skip

If set to true, the node will not be executed. See Node Flow for details

If the output variable is populated, the user will not be prompted. Therefore if you have two prompt nodes in the same dialogue with the same output variable, the second prompt will never be run.

If you want to validate that the input is not in the past (or future), obtain the current date and time in the validation section of the prompt, rather than in a preceding node. This will refresh the current time each time the user inputs a value, so if the user leavse the system for a while, the current date won’t get stale. See the “Validate not in the past” snippet in the DateTime Prompt

{
  "type": "timePrompt",
  "message": "Enter the start time of your holiday",
  "retryMessage": "That's not a valid time",
  "output": "startTime"
}

Decision

Decides which of two paths to take through a dialogue

Properties
type

decision

rule

Logical expression evaluating to true or false using JSON logic. This will normally be a comparison

passNode

id of the node to run if the rule evaluates to true

failNode

id of the node to run if the rule evaluates to false

You don’t need to specify a pass node and a fail node. Often you may just have one and fall through to the next node for the alternative path.

[
    {
      "type": "confirmationPrompt",
      "message": "Do you prefer Pepsi or Coke",
      "retryMessage": "Please select one of the Pepsi or Coke buttons",
      "positiveMessage": "Pepsi",
      "negativeMessage": "Coke",
      "output": "likesPepsi"
    },
    {
      "type": "decision",
      "rule": {
        "===": [
          {
            "var": "likesPepsi"
          },
          true
        ]
      },
      "passNode": "Pepsi",
      "failNode": "Coke"
    },
    {
      "id": "Pepsi",
      "type": "message",
      "message": "I prefer Pepsi too",
      "nextNode": "Dialogue.stop"
    },
    {
      "id": "Coke",
      "type": "message",
      "message": "I'm more of a Pepsi person myself"      
    }
]
{
      "type": "decision",
      "description": "Check if  : (animal is Dog and noise is Woof) or (animal is Cat and noise is Meow)",
      "rule": {
        "or": [
          {
            "and": [
              {
                "===": [
                  {
                    "var": "animal"
                  },
                  "Dog"
                ]
              },
              {
                "===": [
                  {
                    "var": "noise"
                  },
                  "Woof"
                ]
              }
            ]
          },
          {
            "and": [
              {
                "===": [
                  {
                    "var": "animal"
                  },
                  "Cat"
                ]
              },
              {
                "===": [
                  {
                    "var": "noise"
                  },
                  "Meow"
                ]
              }
            ]
          }
        ]
      },
      "passNode": "correct",
      "failNode": "wrong"
}

Confirmation Prompt

Displays a card with two buttons, one of which returns a true response and one a false response

Properties
type

confirmationPrompt

message

The message displayed on the card. A string or reference to a language file

retryMessage

Message to be displayed if the user response is not a button press (or one of the button labels typed)

maxRetries

Maximum number of retries allowed after validation failure. Default value is 3

continueOnFail

If set to true after the maximum failed attemps, the dialogue will continue with null output. If false, the dialogue will stop. Default is false

positiveMessage

Label of the button, which if pressed will return true

negativeMessage

Label of the button, which if pressed will return false

output

Name of the variable in which the true or false response is stored

description

Text to help explain what the node is doing

id

A unique identifier. See Node Flow for details

nextNode

See Node Flow for details

skip

If set to true, the node will not be executed. See Node Flow for details

If you want more than two buttons, use a Choice Prompt

{
       "type": "confirmationPrompt",
       "message": "Do you prefer Pepsi or Coke",
       "retryMessage": "Please enter Pepsi or Coke",
       "positiveMessage": "Pepsi",
       "negativeMessage": "Coke",
       "output": "likesPepsi"
 },
 {
       "type": "message",
       "message": "Output is {likesPepsi}"
       
 }

Choice Prompt

Displays a card with a list of buttons. The label of the selected button is returned.

Properties
type

choicePrompt

message

The message displayed on the card. A string or reference to a language file

retryMessage

Message to be displayed if the user response is not a button press (or one of the button labels typed)

maxRetries

Maximum number of retries allowed after validation failure. Default value is 3

continueOnFail

If set to true after the maximum failed attemps, the dialogue will continue with null output. If false, the dialogue will stop. Default is false

listName

List containing the button labels

displayName

If the list is a list of objects, this is the property within the object that contains the button label

output

Name of the variable in which the selected label is stored

description

Text to help explain what the node is doing

id

A unique identifier. See Node Flow for details

nextNode

See Node Flow for details

skip

If set to true, the node will not be executed. See Node Flow for details

{
      "type": "choicePrompt",
      "message": "What is your favourite drink",
      "retryMessage": "Please selected from the list of drinks",
      "listName": "drinks",
      "output": "selectedDrink"
},
{
      "type": "message",
      "message": "Output is {selectedDrink}"
      
}
{
      "type": "choicePrompt",
      "message": "What is your favourite drink",
      "retryMessage": "Please selected from the list of drinks",
      "listName": "drinks",
      "displayName" :"name",
      "output": "selectedDrink"
},
{
      "type": "message",
      "message": "Output is {selectedDrink}"
      
}

Action

Interact with APIs

Properties
type

action

service

Properties containing information to be sent to the API

service/url

URL of the API

service/method

GET to read data, POST to create data, PUT or PATCH to update data and DELETE to delete data

service/headers

Header names and values

service/body

Body data to be sent for POSTs, PUTs and PATCHs

output

Properties containing information returned by the API

output/body

Response body from the API

output/header

Response headers from the API

authorised

If set to true, the Authorization header need not be specified, as it will be generated by talksuite

description

Text to help explain what the node is doing

id

A unique identifier. See Node Flow for details

nextNode

See Node Flow for details

skip

If set to true, the node will not be executed. See Node Flow for details

Use the built-in variable dialogue.lastApiStatusCode to get the HTTP response code from the API call.

PUTs and DELETEs in people first requires an If-Match concurrency header to be supplied. This can be obtained from the ETag response header from a GET on the record to be updated.

It’s a good idea to specify the common base of URLs as a bot constant.

If you are accessing APIs which require authentication, the built-in variable User/Authentication will be populated with a bearer token once the user has signed into the application. The application will be determined from the domain specified in the bot configuration record, so there is no need to specify which token you need.

When a dialogue is triggered, talksuite will analyse the action nodes in the dialogue and look at the urls being used. If the url matches one of the service domains of the secondary authentication providers in the bot config record, it will ask the user to authenticate.

If you want to bypass automatic secondary authentication, hold the url in two parts and concatentate it in the dialogue. You can now catch the 401 yourself.

When accessing an API that uses XML, if you need to use attributes, prefix the attribute name with the @ character.

{
      "type": "action",
      "service": {
        "url": "https://www.gov.uk/bank-holidays.json",
        "method": "GET",
        "headers": {
          "Accept": "application/json"
        }
      },
      "outputs": {
        "body": {
          "holidays": "/england-and-wales/events"
        }
      }
}
{
      "type": "action",
      "service": {
        "body": {
          "friendsfamily": "{friendsfamily}"
        },
        "method": "POST",
        "url": "{Bot/baseUrl}/hrm/people/{personId}/friendsfamily?annotate=t",
        "headers": {
          "Accept": "application/json",
          "TenantCode": "{Bot/TenantCode}",
          "EnvironmentCode": "{Bot/EnvironmentCode}",
          "Authorization": "Bearer {User/AuthenticationToken}"
        }
}  
{
      "type": "action",
      "service": {
        "url": "{Bot/baseUrl}/profile",
        "method": "GET",
        "headers": {
          "Accept": "application/json",
          "TenantCode": "{Bot/TenantCode}",
          "EnvironmentCode": "{Bot/EnvironmentCode}",
          "Authorization": "Bearer {User/AuthenticationToken}"
        }
      },
      "outputs": {
        "body": {
          "timeZoneId": "/data/profile/timeZoneId",
          "regionId": "/data/profile/regionId"
        }
      }
}  
{
      "type": "action",
      "service": {
        "url": "{Bot/baseUrl}/hrm/friendsfamily/{selectedContact/emergencyContactId}?annotate=t&personId={personId}",
        "method": "GET",
        "headers": {
          "Accept": "application/json",
          "TenantCode": "{Bot/TenantCode}",
          "EnvironmentCode": "{Bot/EnvironmentCode}",
          "Authorization": "Bearer {User/AuthenticationToken}"
        }
      },
      "outputs": {              
        "header": {
          "etagResponse": "ETag"
        }
      }
    },
    {
      "type": "decision",
      "rule": {
        "===": [
          {
            "var": "Dialogue/lastApiStatusCode"
          },
          200
        ]
      },
      
      "failNodeIndex": "Error"
    },
    {
      "type": "action",
      "service": {
        "body": {
          "friendsfamily": "{contact}"
        },
        "method": "PUT",
        "url": "{Bot/baseUrl}/hrm//friendsfamily/{selectedContact/emergencyContactId}?annotate=t",
        "headers": {
          "Accept": "application/json",
          "TenantCode": "{Bot/TenantCode}",
          "EnvironmentCode": "{Bot/EnvironmentCode}",
          "Authorization": "Bearer {User/AuthenticationToken}",
          "If-Match": "{etagResponse}"
        }
      }
}
{
      "type": "action",
      "service": {
        "url": "{Bot/baseUrl}/hrm/friendsfamily/{selectedContact/emergencyContactId}?annotate=t&personId={personId}",
        "method": "GET",
        "headers": {
          "Accept": "application/json",
          "TenantCode": "{Bot/TenantCode}",
          "EnvironmentCode": "{Bot/EnvironmentCode}",
          "Authorization": "Bearer {User/AuthenticationToken}"
        }
      },
      "outputs": {
        "body": {
          "contact": "/data/friendsfamily"
        },
        "header": {
          "etagResponse": "ETag"
        }
      }
    },
    {
      "type": "decision",
      "rule": {
        "===": [
          {
            "var": "Dialogue/lastApiStatusCode"
          },
          200
        ]
      },
      "failNode": "Error"
},
{
      "type": "action",
      "service": {
        "method": "DELETE",
        "url": "{Bot/baseUrl}/hrm//friendsfamily/{selectedContact/emergencyContactId}?annotate=t",
        "headers": {
          "Accept": "application/json",
          "TenantCode": "{Bot/TenantCode}",
          "EnvironmentCode": "{Bot/EnvironmentCode}",
          "Authorization": "Bearer {User/AuthenticationToken}",
          "If-Match": "{etagResponse}"
        }
      }
}      
   
{
  "type": "action",
   "service": {
     "url": "{bot/trentApiBaseUrl}/wrd_rest/run/PerAbsHols.xml?abs_id=new",
      "authorised": true,
      "headers": {
         "Accept": "text/xml",
         "Content-Type": "text/xml"
        },
      "body": {
         "?xml": {
           "@version": "1.0",
           "@encoding": "UTF-8"
          },
          "itrent": {
             "@xmlns:xl": "http://www.w3.org/1999/xlink",
              "absence": {
                 "@status": "new",
                 "@crc": "",
                 "@id": "",
                 "abs_d": "{dayOffDateString}",
                 "hol_period": "FULL",
                 "abs_type_id": "022550005C",
                 "dl_avail_jobs": "T",
                 "update_i": "T",
                 "notes": null
                 }
           }
         },
      "method": "POST"
     },
     "outputs": {
       "body": {
       "response": ""
    }
  }
}

Operation

Perform a calculation

Properties
type

operation

operation

Variable name for an assignment or a JSON logic method or date function

output

Name of the variable in which the result is stored

description

Text to help explain what the node is doing

id

A unique identifier. See Node Flow for details

nextNode

See Node Flow for details

skip

If set to true, the node will not be executed. See Node Flow for details

Operation nodes are used to copy the value of one variable to another and to perform JSON logic operations and execute date functions.

The operation can’t be a constant value. It needs to be a function or a variable. If you want to assign a constant to a variable, define the constant in the model, then you can assign the model variable to another variable

{
      "description" : "Assign the value of x to y",
      "type": "operation",
      "operation": {
          "var" :"x"
      },
      "output": "y"
}
{
      "description" : "Concatenate hello, a space and world into the text variable",
      "type": "operation",
      "operation": {
        "cat": [
          "hello",
          " ",
          "world"
        ]
      },
      "output": "text"
}
{
      "description" : "Store the number of characters in the variable text in lengthOfText",
      "type": "operation",
      "operation": {
        "method": [
          {
            "var": "text"
          },
          "length"
        ]
      },
      "output": "lengthOfText"
}
{
      "description" : "Get the current date",
      "type": "operation",
      "operation": {
        "Date.currentDate": []
      },
      "output": "DateToday"
}

Build Object

Build a data object

Properties
type

buildObject

object

Properties and values defining the structure of the object

output

Name of the variable in which the result is stored

description

Text to help explain what the node is doing

id

A unique identifier. See Node Flow for details

nextNode

See Node Flow for details

skip

If set to true, the node will not be executed. See Node Flow for details

The object can be built using constant property values or individual variables for each property. Examples are shown on the right

A build object node removes the need for multiple property assignment and addItem operation nodes

You can only assign individual values to an object in a buildObject node

If you want to insert list variables or other object variables into an object, use an operation node

{
  "type" : "buildObject",
  "object" : {        
    "customer": {
      "firstName": "Tony",
      "lastName": "Hancock",
      "favourites" : [
        "Books",
        "Albums",
        "Games"
      ]
    },
    "orders": [
      {
        "productType": "Book",
        "productDetails": {
          "title": "1984",
          "author": "George Orwell"
        },
        "price": 7.99
      },
      {
        "productType": "Album",
        "productDetails": {
          "title": "Aqualung",
          "artist": "Jethro Tull"
        },
        "price": 15
      }
    ],
    "address": {
      "houseNumber": 23,
      "streetName": "Railway Cuttings",
      "town": "East Cheam",
      "billingAddress": true
    }
  },
  "output" : "orders"
}
{
  "type" : "buildObject",
  "object" : {        
    "customer": {
      "firstName": "{firstName}",
      "lastName": "{lastName}",
      "favourites" : [
        "{fav1}",
        "{fav2}",
        "{fav3}"
      ]
    },
    "orders": [
      {
        "productType": "{prodType1}",
        "productDetails": {
          "title": "{prodTitle1}",
          "author": "{prodauthor1}"
        },
        "price": "{price1}"
      },
      {
        "productType": "{prodType21}",
        "productDetails": {
          "title": "{prodTitle2}",
          "author": "{prodauthor2}"
        },
        "price": "{price2}"
      }
    ],
    "address": {
      "houseNumber": "{houseNum}",
      "streetName": "{street}",
      "town": "{town}",
      "billingAddress": "{billing}"
    }
  },
  "output" : "orders"
}

Dialogue

Run another dialogue

Properties
type

dialogue

dialogueId

Name of the dialogue to call.

inputs

Define the transfer of data from local variables to variables in the called dialogue. See the snippets for details

outputs

Define the transfer of data from variables in the called dialogue back to local variables in the called dialogue. See the snippets for details

description

Text to help explain what the node is doing

id

A unique identifier. See Node Flow for details

nextNode

See Node Flow for details

skip

If set to true, the node will not be executed. See Node Flow for details

The snippets show one input variable and one output variable, but multiple variables can be specified for input and output.

If the calling dialogue is in a project, the called dialogue cannot be in another project.

See Node Flow for other ways of controlling the flow of dialogues

The dialogueId is validated, so you’ll have to create the called dialogue before referencing it in a node

{
  "type": "dialogue",
  "dialogueId": "example called dialogue",
  "description": "passOut and getBack are in this dialogue. receiveVar and outputVar are in the called dialogue",
  "inputs": {
    "receiveVar": {
      "var": "passOut"
    }
  },
  "outputs": {
    "getBack": {
      "var": "outputVar"
    }
  }  
}
{
  "data": {
      "attributes": {
      "json": {
          "id": "example called dialogue",
          "trigger": {
          "type": "nestedDialogue"
          },
          "nodes": [
          {
              "type": "operation",
              "description": "receiveVar is passed in and outputVar is returned",
              "operation": {
              "var": "receiveVar"
              },
              "output": "outputVar"
          }
          ]
      }
      }
  }
}

Sequence Dialogue

Run another dialogue repeatedly for each item in a list

Properties
type

sequenceDialogue

dialogueId

Name of the dialogue to call.

inputItem

The name of the list variable

listName

The name of the variable in the called dialogue that will be populated for each record in the list, one record at a time, with one call for each record

inputs

Additional parameters to be passed into the called dialogue. Each parameter consists of a mapping from a local variable to a variable in the called dialogue. See the advance snippets for details

description

Text to help explain what the node is doing

id

A unique identifier. See Node Flow for details

nextNode

See Node Flow for details

skip

If set to true, the node will not be executed. See Node Flow for details

If the calling dialogue is in a project, the called dialogue cannot be in another project.

See Node Flow for other ways of controlling the flow of dialogues

The dialogueId is validated, so you’ll have to create the called dialogue before referencing it in a node

There is no output parameters section in this node. You’ll have to use a repeatDialogue node if you want to pass information between different iterations of the loop or output to anything other than the listname array.

{
      "type": "sequenceDialogue",
      "dialogueId": "displayPerson",
      "inputItem": "person",
      "listName": "people"
}
{
  "data": {
      "attributes": {
      "json": {
          "id": "displayPerson",
          "trigger": {
          "type": "nestedDialogue"
          },
          "nodes": [
          {
              "description": "Display the knownAs property for each item in the list",
              "type": "message",
              "message": "{person/knownAs}"
          }
          ]
      }
      }
  }
}      
{
      "description": "people is a list of objects, local1 and local2 are variables in this dialogue",
      "type": "sequenceDialogue",
      "dialogueId": "Example called for each record",
      "inputItem": "person",
      "listName": "people",
      "inputs": {
        "input1": {
          "var": "local1"
        },
        "input2": {
          "var": "local2"
        }
      }
 }
{
  "data": {
    "attributes": {
      "json": {
        "id": "example called for each record",
        "trigger": {
          "type": "nestedDialogue"
        },
        "nodes": [
          {
            "description": "person will be populated with each record in the calling list in turn. input1 and input2 are passed from the calling dialogue and will have the same value for each call",
            "type": "message",
            "message": "{person/firstName} input1={input1}, input2={input2}"
          }
        ]
      }
    }    
}

Repeat Dialogue

Run another dialogue repeatedly until a condition is met

Properties
type

repeatDialogue

dialogueId

Name of the dialogue to call.

inputs

Variables passed to the called dialogue

outputs

Variables received from the called dialogue

repeatUntil

A Boolean expression in JSON logic. The nested dialogue will be called until this expression evaluates to true

description

Text to help explain what the node is doing

id

A unique identifier. See Node Flow for details

nextNode

See Node Flow for details

skip

If set to true, the node will not be executed. See Node Flow for details

If the calling dialogue is in a project, the called dialogue cannot be in another project.

See Node Flow for other ways of controlling the flow of dialogues

If you are using a numeric variable, incremented in the called dialogue to control the loop, if you don’t initialise the variable to “0” in the model, the variable will always be null, as null + 1 = null. This will put you into an infinite loop

After 1000 iterations of a loop, the talksuite will assume you are in an infinite loop and stop.

The dialogueId is validated, so you’ll have to create the called dialogue before referencing it in a node

When you are debugging, to get out of an infinite loops, put a string prompt in the called node and write an interrupt dialogue that cancels the current dialogue. You can remove the string prompt when the loop logic is tested.

{
  "type": "repeatDialogue",
  "dialogueId": "example called repeatedly",
  "description" : "i is a variable in the calling dialogue, passed into the called dialogue as index. finished is a boolean in both dialogues",
  "inputs": {
    "index": {
      "var": "i"
    }
  },
  "outputs": {
    "i": {
      "var": "index"
    },
    "finished": {
      "var": "finished"
    }
  },
  "repeatUntil": {
    "===": [
      {
        "var": "finished"
      },
      "finished"
    ]
  }
} 
{
    "data": {
      "attributes": {
        "json": {
          "id": "example called repeatedly",
          "trigger": {
            "type": "nestedDialogue"
          },
          "nodes": [
            {
              "type": "operation",
              "output": "index",
              "operation": {
                "+": [
                  {
                    "var": "index"
                  },
                  1
                ]
              },
              "nextNodeIndex": 1
            },
            {
              "type": "choicePrompt",
              "message": "{index}: I'll keep asking you this until you select Stop",
              "retryMessage": "?",
              "listName": "menuButtons",
              "output": "menuChoice",
              "nextNodeIndex": 2
            },
            {
              "type": "decision",
              "rule": {
                "===": [
                  {
                    "var": "menuChoice"
                  },
                  {
                    "var": "menuButtons/0"
                  }
                ]
              },
              "passNodeIndex": 3,
              "failNodeIndex": null
            },
            {
              "type": "operation",
              "operation": {
                "var": "finishedText"
              },
              "output": "finished",
              "nextNodeIndex": null
            }
          ],
          "model": {
            "finishedText": "finished",
            "menuButtons": [
              "Stop",
              "Continue"
            ]
          }
        }
      }
    }
}      

Download Action

GET binary data from an API

Properties
type

action

service

Properties containing information to be sent to the API

service/url

URL of the API

service/headers

Header names and values

output

Name of the object to store the returned data

description

Text to help explain what the node is doing

id

A unique identifier. See Node Flow for details

nextNode

See Node Flow for details

skip

If set to true, the node will not be executed. See Node Flow for details

The output variable has a number of properties. If the output variable name is image, use image/url to access the data and image/contentType to get the content type.

If you are accessing APIs which require authentication, the built-in variable User/Authentication will be populated with a bearer token once the user has signed in in to the application. The application will be determined from the domain specified in the bot configuration record, so there is no need to specify which token you need.

{
      "type": "downloadAction",
      "service": {
        "url": "{Bot/BaseUrl}/hrm/people/{person/personId}/photo",
        "headers": {
          "TenantCode": "{Bot/TenantCode}",
          "EnvironmentCode": "{Bot/EnvironmentCode}",
          "Authorization": "Bearer {User/AuthenticationToken}",
          "Accept": "*/*"
        }
      },
      "outputs": {
        "content": "image"
}

Attachment Prompt

Ask the user to upload a file

Properties
type

attachmentPrompt

message

The message output to the user. A string or reference to a language file

retryMessage

Message to be output if validation fails

contentTypes

Array of valid file types (e.g. image/jpeg)

customContent

See Custom Content for reference

output

Variable name of the object in which the uploaded file information will be stored. The image is stored in the url property of the object

description

Text to help explain what the node is doing

id

A unique identifier. See Node Flow for details

nextNode

See Node Flow for details

skip

If set to true, the node will not be executed. See Node Flow for details

{
    "type": "attachmentPrompt",      
    "message": "Upload an image",
    "retryMessage": "Invalid file type - please supply a jpg, jpeg, png or gif file type",
    "contentTypes": [ "image/jpeg", "image/png", "image/gif" ],
    "output": "attachment"
}

Card

Display a card with text, buttons and an image

Properties
type

card

content

Properties describing the content of the card

content/title

Card title

content/subtitle

Subtitle

content/text

Body text of the card

content/image

Image to display on the card. Use the /url property of the image object

content/isThumbnail

If set to true the image is displayed as a thumbnail. Defaults to false

content/tapOptions

Properties controlling actions when the card is tapped

content/tapOptions/displayName

Text to be output when the card is tapped

content/tapOptions/value

Value to be returned when the card is tapped

content/tapOptions/output

Variable in which the returned tap value is stored.

buttonOptions

Properties controlling the buttons on the card

buttonOptions/listName

List of button labels

buttonOptions/displayName

If button list consists of objects, this is the object property to use as the label

buttonOptions/output

Variable which will be populated with the button pressed

content/retryMessage

Message output if the user types anything other than the label of a button

description

Text to help explain what the node is doing

id

A unique identifier. See Node Flow for details

nextNode

See Node Flow for details

skip

If set to true, the node will not be executed. See Node Flow for details

If an output property is specified, the node will wait for user input (or a button press or tap). It is possible to have a card with buttons and no output property. In this case the node will not wait for input.

You can write a dialogue triggered on the button label, which will allow the user to press a button or start another dialogue.

{
      "type": "card",
      "content": {
        "title": "Button test",
        "subtitle": "{subTitle}",
        "text": "{text}",
        "image": "{image/Url}",
        "buttonOptions": {
          "listName": "buttonSet",
          "output": "selectedButton"
        },
        "retryMessage": "Please select one of the buttons"
      }
},
{
      "type": "card",
      "content": {
        "title": "Button test",
        "subtitle": "{subTitle}",
        "text": "{text}",
        "image": "{image/Url}"              
      }
}
{
      "type": "card",
      "content": {
        "title": "Tap test",
        "isThumbnail": true,
        "tapOptions": {
          "displayName": "tap",
          "output": "tapped",
          "value": "tap value"
        },
        "subtitle": "{subTitle}",
        "text": "{text}",
        "image": "{image/Url}"
      }
}

Card Collection

Display a scrolling list (carousel) of cards

Properties
type

cardCollection

listName

Name of a list of objects that contains the data for the cards

contentItem

Name to be used within the node to access each object in the list in turn. E.g. if the listName is people, then the content item would be person.

pageSize

Maximum number of cards to display

content

Properties describing the content of the card

content/title

Card title

content/subtitle

Subtitle

content/text

Body text of the card

content/image

Image to display on the card. Use the /url property of the image object

content/isThumbnail

If set to true the image is displayed as a thumbnail. Defaults to false

content/tapOptions

Properties controlling actions when the card is tapped

content/tapOptions/displayName

Text to be output when the card is tapped

content/tapOptions/value

Value to be returned when the card is tapped

content/tapOptions/output

Variable in which the returned tap value is stored.

buttonOptions

Properties controlling the buttons on the card

buttonOptions/listname

List of button labels

buttonOptions/displayName

If button list consists of objects, this is the object property to use as the label

buttonOptions/output

Variable which will be populated with details of the button pressed. If the finishMessage property is present, this can be a list of buttons

content/retryMessage

Message output if the user types anything other than the label of a button

content/finishMessage

A label for a button to be displayed below the carousel. Use this property if you want to allow multiple button selections. The node will only progress when this button is pressed.

content/messageText

Message output above the carousel.

content/output

Object from the listName object list for which the button was pressed. If finishMessage is specified, this can be a list of objects for which a button has been pressed. If not output property is specified, then the node will end without waiting for input.

description

Text to help explain what the node is doing

id

A unique identifier. See Node Flow for details

nextNode

See Node Flow for details

skip

If set to true, the node will not be executed. See Node Flow for details

If an output property is specified, the node will wait for user input (or a button press or tap). It is possible to have a card with buttons and no output property. In this case the node will not wait for input. You can write a dialogue triggered on the button label, which will allow the user to press a button or start another dialogue.

{
      "type": "cardCollection",
      "listName": "people",
      "contentItem": "person",
      "pageSize": 3,
      "content": {
        "title": "{person/lastName}",
        "subtitle": "{person/firstName}",
        "image": "{person/photo}",
        "buttonOptions": {
          "listName": "buttons",
          "output": "selectedOption"
        },
        "retryMessage": "Please press one of the buttons"
      },
      "output": "selectedPerson"
}
{
      "type": "cardCollection",
      "listName": "people",
      "contentItem": "person",
      "pageSize": 3,
      "content": {
        "title": "{person/lastName}",
        "subtitle": "{person/firstName}",
        "image": "{person/photo}",
        "buttonOptions": {
          "listName": "buttons",
          "output": "selectedButtons"
        },
        "retryMessage": "Please press one of the buttons"
      },
      "messageText": "You can press a button for several people. Press done when you've finished",
      "finishMessage": "Done",
      "output": "selectedPeople"
}

Event

Change the state of an app

Properties
type

event

event

Set to endDialogue to end the dialogue, leaveConversation to leave the conversation or resettDialogue to re-start the dialogue

description

Text to help explain what the node is doing

id

A unique identifier. See Node Flow for details

nextNode

See Node Flow for details

skip

If set to true, the node will not be executed. See Node Flow for details

You may want to set the priority of dialogues which end dialogues or conversations to interrupt, allowing the user to end a dialogue or conversation while inside a dialogue

{
  "id": "cancel",
   "trigger": {
      "type": "message",
      "values": [
         "cancel"
       ]
    },
    "nodes": [
      {
        "type": "event",
         "event": "endDialogue"
       }
    ],
    "priority": "interrupt"        
}
{
  "data": {
      "attributes": {
      "json": {
          "id": "exit",
          "trigger": {
          "type": "message",
          "values": [
              "exit"
          ]
          },
          "nodes": [
          {
              "type": "event",
              "event": "leaveConversation"
          }
          ],
          "priority": "interrupt"
      }
      }
}
{
     "type": "event",
     "event": "resetDialogue"
}      

Custom Card Collection

Display a scrolling list (carousel) of bespoke cards

Properties
type

customCardCollection

listName

Name of a list of objects that contains the data for the cards

contentItem

Name to be used within the node to access each object in the list in turn. E.g. if the listName is people, then the content item would be person.

pageSize

Maximum number of cards to display

customContent

Enables the display of Bespoke Cards

messageText

Message to display above the carousel

output

Indicates which button on which card has been pressed. Uses the value property of the button section within the custom content

outputOperation.

Allow the button output to be transformed

outputOperation/operation

A JSON logic operation allowing button output to be transformed

outputOperation/output

Variable populated with results of the output operation transformation

validation

A JSON logic operation. If this does not evaluate to true the user input is treated as invalid

retryMessage

A message displayed when validation fails

selected

The object in listName associated with the card for which the button is pressed

finishMessage

The label of a button that will be displayed below the carousel. If specified the user can selected buttons from multiple cards. Only when the finishMessage button is pressed will the node move on to the next node. The output and selected properties will now be lists, containing details of all the cards on which buttons were pressed and each of the button presses

description

Text to help explain what the node is doing

id

A unique identifier. See Node Flow for details

nextNode

See Node Flow for details

skip

If set to true, the node will not be executed. See Node Flow for details

This node requires that Bespoke Cards be available in the channel. This is only the case for the talksuite Authors’ channel or the People First app.

Custom Event

Send an event to the People First app

Properties
type

customEvent

name

The name of the event. Must be a name accepted by the client app

data

Data object to send to the app

description

Text to help explain what the node is doing

id

A unique identifier. See Node Flow for details

nextNode

See Node Flow for details

skip

If set to true, the node will not be executed. See Node Flow for details

This node can only be used when the bot is connected to the People First app. See People First Outbound Events for details

{
  "type": "customEvent",
  "name": "clockInStatus",
  "data": {"var" : "status"}
}

NLP Entities

Natural language processor entity analysis. Allows you to specify what information you want from a LUIS response.

Properties
type

nlpEntities

input

The JSON response from the output property of the intent dialogue

requiredEntities

A list of entities types in the repsonse that you want to pick out. You don’t have to use the full LUIS date-time built-in entity name, so you can specify “builtin.datetimeV2.date” or just “date”

rules

Rules to remove ambiguity

rules/dateContext

Set to latest if ambiguous dates should resolve to a future date or earliest if they should resolve to a past date

rules/likelyStartTime

Set to the hour of the day which defines a 12 hour period that ambiguous times will default to. For example a value of “7” will define a period of 7am to 7pm, which means that 10 o’clock will be resolved to 10am.

outputs

Processed outputs from the LUIS response

outputs/matchedEntities

A list of entity objects matched against the requirements. Each object has a type property (the LUIS entity type), a value property (the resolved LUIS value) and in the case of half day period, a period property which can be AM or PM.

description

Text to help explain what the node is doing

id

A unique identifier. See Node Flow for details

nextNode

See Node Flow for details

skip

If set to true, the node will not be executed. See Node Flow for details

See Luis Entities for details of how the nlpEntities node matches the raw luis response to your list of required entities

{
   "type": "nlpEntities",
   "input": "luisResult",    
    "requiredEntities": "startDateAndEndDate",
    "rules" : {
       "dateContext": "latest"
     },
     "outputs": {        
        "matchedEntities": "results"
     }
}

Property Order

Your dialogues will be easier for others to read if the properties are in a consistent and logical order. We’d recommend the following ordering:

Property Example
id Only if the node is jumped to by another node
description You don’t need a description for every node, just when some explanation is needed
skip Only put this is if the value is true
type Node type
Output to the user before any logic is performed e.g message or title, subtitle etc. for cards
Logic e.g. validation for prompts, repeatUntil for loops
Output to the user after logic e.g. retryMessage
Variables returned from the node e.g. output
Next node logic e.g. nextNode or passNode and failNode for decisions

Comparisons

The following operations can be used to compare two values. They all return true or false. They are useful in decision nodes, the validation section of prompts and within a conditional assignment operation (if)

Equal to
  • ”==” : [#1,#2]. Returns true if #1 equals #2, even if one is a number and the other equivalent text (e.g. 1 == “1” is true)
Equivalent to
  • ”===” : [#1,#2]. Returns true if #1 equals #2, and both are text or both are numbers. (e.g. 1 == 1 is true, 1 === “1” is false)
Greater Than
  • ”>” : [#1,#2] . Returns true if #1 is greater than #2
Greater Than or equal to
  • ”>=” : [#1,#2]. Returns true if #1 is greater than or equal to #2
Less than
  • ”<” : [#1,#2]. Returns true if #1 is less than #2
Less than or equal to
  • ”<=” : [#1,#2]. Returns true if #1 is less than or equal to #2
Not equal to
  • ”!==” : [#1,#2]. Returns true if #1 does not equal #2, where numbers and text are regarded as different
Not equivalent to
  • ”!=” : [#1,#2]. Returns true if #1 does not equal #2, where numbers and text are regarded as the same
Matches a pattern
  • “method” : [ #1,”matchesPattern” [#2]]. Returns true if #1 matches the .NET regular expression in #2

The \ character in a regular expression needs to be replaced with \\ to prevent JSON treating it as a special character

Avoid using language references directly in comparisons. Assign the language reference to a variable in the model and use the variable in the comparison

{
 "type": "decision",
  "description": "Check likesPepsi is true",    
 "rule": {
   "===": [
     {
       "var": "likesPepsi"
     },
     true
   ]
 },
 "passNode": "Pepsi",
 "failNode": "Coke"
},
{
  "type": "decision",
  "description": "Check count is zero where count could be a number or text",
  "rule": {
    "==": [
      {
        "var": "count"
      },
      "10"
    ]
  },
  "passNode": "ten",
  "failNode": "not ten"
},
{
      "type": "datePrompt",
      "description": "Check that endDate is after start date",
      "message": "When do you want your holiday to end?",
      "retryMessage": "Enter a valid date which must be after the start date",
      "output": "endDate",
      "validation": {
          ">": [
            {
              "var": "endDate"
            },
            {
              "var": "startDate"
            }
          ] 
      }
}
{
      "type": "datePrompt",
      "description": "Check that endDate is after or equal to startDate",
      "message": "When do you want your holiday to end?",
      "retryMessage": "Enter a valid date which can't be before the start date",
      "output": "endDate",
      "validation": {
          ">=": [
            {
              "var": "endDate"
            },
            {
              "var": "startDate"
            }
          ] 
      }
}
{
      "type": "datePrompt",
      "description": "Check that startDate is before endDate",
      "message": "When do you want your holiday to start?",
      "retryMessage": "Enter a valid date which must be before the end date",
      "output": "startDate",
      "validation": {
          "<": [
            {
              "var": "startDate"
            },
            {
              "var": "enedDate"
            }
          ] 
      }
}
{
      "type": "datePrompt",
      "description": "Check that startDate is not after endDate",
      "message": "When do you want your holiday to start?",
      "retryMessage": "Enter a valid date which cannot be after the end date",
      "output": "startDate",
      "validation": {
          "<=": [
            {
              "var": "startDate"
            },
            {
              "var": "enedDate"
            }
          ] 
      }
}
{
 "type": "decision",
  "description": "Check likesPepsi is not true",    
 "rule": {
   "!==": [
     {
       "var": "likesPepsi"
     },
     true
   ]
 },
 "passNode": "Coke",
 "failNode": "Pepsi"
},
{
  "type": "decision",
  "description": "Check count is not zero where count could be a number or text",
  "rule": {
    "!=": [
      {
        "var": "count"
      },
      "10"
    ]
  },
  "passNode": "not ten",
  "failNode": "ten"
},
{
  "type": "stringPrompt",
  "message": "Please enter your sort code (nn-nn-nn)",
  "retryMessage": "Please enter a string like nn-nn-nn where n is a digit",
  "output": "sortCode",
  "validation": {
    "method": [
         { "var": "sortCode" },
         "matchesPattern",
          [ "^\\d\\d-\\d\\d-\\d\\d$" ]
     ]
   }
}      

Logical expressions

In the following examples #1, #2 etc can be replaced by a variable using the “var” property or a constant value. See Using Variables

The following operations can be used to evaluate logical expressions. Use them in decisions and prompt validation. These operations can be nested to evaluate expressions like “(a = b or c =d) and (e =f or g = h)”. They can also be combined with comparions to evaluate expressions like (a > 10) or (b > 20)

And

“and” : [#1,#2]. Returns true if #1 and #2 are true

Or
  • “or” : [#1,#2]. Returns true if #1 or #2 is true
Not
  • ”!” : [#1]. Returns true if #1 is false and false if #1 is true
Conditional assignment
  • “if” : [#c1][#v1] … [#cN][#vN][#d]. Returns #v1 if #c1 is true. Returns #v2 if #c2 is true etc. Returns #d if all of #c1 to #cN are false.
{
  "type": "decision",
  "description": "Check for a blue circle",
  "rule": {
    "and": [
      {
        "===": [
          {
            "var": "colour"
          },
          "Blue"
        ]
      },
      {
        "===": [
          {
            "var": "shape"
          },
          "Circle"
        ]
      }
    ]
  },
  "passNode": "Blue Circle",
  "failNode": "something else"
} 
{
  "type": "decision",
  "description": "Check if colour is Blue or Red",
  "rule": {
    "or": [
      {
        "===": [
          {
            "var": "colour"
          },
          "Blue"
        ]
      },
      {
        "===": [
          {
            "var": "colour"
          },
          "Red"
        ]
      }
    ]
  },
  "passNode": "Blue or Red",
  "failNode": "something else" 
}    
{
  "type": "decision",
  "description": "Check if name is null or undefined",
  "rule": {
    "!": [
          {
            "var": "name"
          }
    ]
  },
  "passNode": "name not set",
  "failNode": "name is set"
}
{
   "type": "operation",
   "description": "Output zero if number is 0 , one if number is 1 and something elese otherwise",
   "output": "output",
   "operation": {
     "if": [
       {
         "===": [
           {
             "var": "number"
           },
           0
         ]
       },
        "zero",
       {
         "===": [
           {
             "var": "number"
           },
           1
         ]
       },
       "one",
       "something else"
     ]
   }
}

Dates and Times

In the following examples #1, #2 etc can be replaced by a variable using the “var” property or a constant value. See Using Variables

Getting the current date and time

The operations:

  • “Date.currentDate” : []
  • “DateTime.currentDateTime” : []
  • “Time.currentTime” : []

can be used to get the current date/time in the user’s own time zone

The current year can be obtained using Date.currentYear : [] or DateTime. currentYear : []. Similarly for months, days, hours minutes and seconds.

Formatting

The default format for a date is 1 December 1965. The default format for a time is 17:30 if the region is en-GB and 5:30 PM for en-US.

Custom formatting is available using:

  • “Date.format” : [#1,#2]
  • “DateTime.format” : [#1,#2]

Where #1 is a date variable and #2 is a format string that can consist of the following components:

  • “d” The day of the month, from 1 through 31.
  • “dd” The day of the month, from 01 through 31.
  • “ddd” The abbreviated name of the day of the week.
  • “dddd” The full name of the day of the week.
  • “do” The day of the month from as an ordinal - 1st through to 31st
  • “M” The month, from 1 through 12.
  • “MM” The month, from 01 through 12.
  • “MMM” The abbreviated name of the month.
  • “MMMM” The full name of the month.
  • “yy” The year, from 00 to 99.
  • “yyyy” The year as a four-digit number.
  • “hh” Double digit hour of 12 hour clock (01 to 12)
  • “HH” Double digit hour of 24 hour clock (00 to 23)
  • “mm” Double digit minute (00 to 59)
  • “ss” Double digit second (00 to 59)
  • “tt” AM or PM

Durations between dates

  • “Date.difference” : [#1,#2] calculates the duration between #1 and #2 expressed in a combination of years, months and days, allowing output such as “John is 26 years, 2 months and 5 days old. The DateTime.difference variant also calculates the difference in hours, minutes and seconds. The output variable has sub-properties of Year, Months etc. populated. E.g. duration/Years.

  • “Date.totalDifference” : [#1,#2] operation allows you express a duration between two dates in either years, months or days (e.g. 1 year or 12 months or 365 days). DateTime.totaldifference also can calculate durations in hours, minutes or seconds.

Shifting a date by a period

It is possible to add or substract a number of years, months, weeks, days, hours to a date or date-time and a number of hours, minutes or seconds to a date-time.

  • “method” : [ #1,”addYears”,[#2]]. Returns a date or date-time #2 years later than #1. Similarly for months, weeks and days

  • “method” : [ #1,”subtractYears”,[#2]]. Returns a date or date-time #2 years earlier than #1. Similarly for months, weeks and days

  • “method” : [ #1,”addHours”,[#2]]. Returns a date-time #2 hours later than #1. Similarly for minutes and seconds

  • “method” : [ #1,”subtractHours”,[#2]]. Returns a date-time #2 hours earlier than #1. Similarly for minutes and seconds

Timezones

Input dates are treated as local dates. The user’s time zone can be obtained from conversation.settings/timeZone

  • “DateTime.toUTC” : [#1,#2] Returns the UTC date/time equivalent to local date/time #1 in time zone #2 (as an IANA location e.g. Europe/London)

  • “DateTime.toUTC” : [#1,#2,#3] Returns the UTC date/time equivalent to local date #1 and local time #2 in time zone #3

  • “Date.toUTC” : [#1,#2] Returns the UTC date equivalent to local date #1 in time zone #2

  • “Date.fromUTC” : [#1,#2] Returns the local date equivalent to UTC date #1 in time zone #2

  • “DateTime.fromUTC” : [#1,#2] Returns the UTC date equivalent to UTC date #1 in time zone #2

[
    {
      "type": "operation",
      "operation": {
        "Date.currentDate": []
      },
      "output": "DateToday"
    },
    {
      "type": "operation",
      "operation": {
        "DateTime.currentDateTime": []
      },
      "output": "DateTimeNow"
    },
    {
      "type": "operation",
      "operation": {
        "Time.currentTime": []
      },
      "output": "TimeNow"
    },
    {
      "type": "message",
      "message": "Today's date is {DateToday}"
    },
    {
      "type": "message",
      "message": "Today's date and time is {DateTimeNow}"
    },
    {
      "type": "message",
      "message": "Current time is {TimeNow}"
    }
]
[
    {
      "type": "operation",
      "operation": {
        "DateTime.currentDateTime": []
      },
      "output": "DateTimeNow"            
    },
    {
      "type": "operation",
      "operation": {
        "DateTime.format": [
          {
            "var": "DateTimeNow"
          },
          "dddd do MMMM yyyy HH:mm:ss"
        ]
      },
      "output": "24HourFormat"
    },
    {
      "type": "operation",
      "operation": {
        "DateTime.format": [
          {
            "var": "DateTimeNow"
          },
          "dddd do MMMM yyyy hh:mm:ss tt"
        ]
      },
      "output": "12HourFormat"
    },
    {
      "type": "message",
      "message": "Today's date and time in 24 hour format is {24HourFormat}"
    },
    {
      "type": "message",
      "message": "Today's date and time in 12 hour format is {12HourFormat}"
    }
]
[
    {
      "type": "dateTimePrompt",
      "message": "Enter a start date",
      "retryMessage": "That’s not a valid date",
      "output": "startDate"
    },
    {
      "type": "dateTimePrompt",
      "message": "Enter an end date",
      "retryMessage": "That’s not a valid date",
      "output": "endDate"
    },
    {
      "type": "operation",
      "operation": {
        "Date.difference": [
          {
            "var": "startDate"
          },
          {
            "var": "endDate"
          }
        ]
      },
      "output": "age"
    },
    {
      "type": "message",
      "message": "Difference between dates {startDate} and {endDate} is {age/Years} years, {age/Months} Months and {age/Days} days"
    },
    {
      "type": "operation",
      "operation": {
        "Date.totalDifference": [
          {
            "var": "startDate"
          },
          {
            "var": "endDate"
          },
          "Days"
        ]
      },
      "output": "durationInDays"
    },
    {
      "type": "message",
      "message": "Total Difference between dates {startDate} and {endDate} is {durationInDays} days"
    }
]
[
    {
      "type": "operation",
      "operation": {
        "DateTime.currentDateTime": []
      },
      "output": "DateTimeNow"
    },
    {
      "type": "operation",
      "operation": {
        "DateTime.format": [
          {
            "var": "DateTimeNow"
          },
          "dddd do MMMM yyyy HH:mm:ss"
        ]
      },
      "output": "localTime"
    },
    {
      "type": "message",
      "message": "The time in your current timezone of {conversation.settings/timeZone} is {localTime}",
      "nextNodeIndex": 3
    },
    {
      "type": "operation",
      "operation": {
        "DateTime.toUTC": [
          {
            "var": "DateTimeNow"
          },
          {
            "var": "conversation.settings/timeZone"
          }
        ]
      },
      "output": "UTCTime"
    },
    {
      "type": "message",
      "message": "UTC time is {UTCTime}",
      "nextNodeIndex": 5
    },
    {
      "type": "operation",
      "operation": {
        "DateTime.fromUTC": [
          {
            "var": "UTCtime"
          },
          {
            "var": "conversation.settings/timeZone"
          }
        ]
      },
      "output": "localTime"
    },
    {
      "type": "message",
      "message": "Converting back to local time gives {localTime}"
    }
]
{ 
      "type": "operation",
      "description": "Add one day to startDate",
      "operation": {
        "method": [
          {
            "var": "startDate"
          },
          "addDays",
          [
            1
          ]
        ]
      },
      "output": "nextDay"
 },

Text

In the following examples #1, #2 etc can be replaced by a variable using the “var” property or a constant value. See Using Variables

The following operations can be used to manipulate variables containing Text

Add text items together
  • “cat” : [#1,#2, .,. #N]. Concatenates #1 to #N

If you reference a variable x in a cat method using the notation {x} and use the output from the cat in another dialogue, the value of x will be blank. Use {“var” : “x”} instead. The reason for this is that the notation {x} does not copy the value of x but makes a link to it. As x changes the output value changes. When you leave the dialogue the link to x is broken.

Get the number of characters in text
  • “method” : [ #1,”length”]. Returns the number of characters in #1
Find the position of text within a variable
  • “method” : [ #1,”indexOf”, [#2,#3]]. Returns the position of the first instance of #2 in the portion of #1 starting from character position #3. Returns -1 if not found. Matches are not case sensitive.
Get a portion of a text variable
  • “substr” : “[#1,#2,#3]. Returns #3 characters from #1 starting at character position #2. If #3 is omitted all characters from position #2 will be returned. If #3 is omitted and #2 is negative, then the last #2 characters (ignoring the minus) of #1 are returned
Convert the case of text
  • “String.toUpper” : [#1]. Converts #1 to upper case. Also toLower and toTitleCase
{
      "type": "operation",
      "operation": {
        "cat": [
           {
            "var": "firstWord"
          },
          " ",
           {
            "var": "secondWord"
          },
        ]
      },
      "output": "sentence"
}
{
      "type": "operation",
      "operation": {
        "method": [
          {
            "var": "text"
          },
          "length"
        ]
      },
      "output": "lengthOfText"
}
{
      "type": "operation",
      "operation": {
        "method": [
          {
            "var": "text"
          },
          "indexOf",
          [
            "w"
          ]
        ]
      },
      "output": "firstW"
}
{
      "type": "operation",
      "operation": {
        "substr": [
          {
            "var": "text"
          },
          2,
          3
        ]
      },
      "output": "3from2"      
}
{
      "type": "operation",
      "operation": {
        "String.toUpper": [
          {
            "var": "text"
          }
        ]
      },
      "output": "upper"
}          

Arithmetic

In the following examples #1, #2 etc can be replaced by a variable using the “var” property or a constant value. See Using Variables

The following arithmetic operations can be performed

Addition
  • ”+” : [#1,#2]. Returns the sum of #1 and #2
Subtraction
  • ”-“ : [#1,#2]. Returns #1 minus #2
Multiplication
  • ”*” : [#1,#2]. Returns #1 multiplied by #2
Division
  • ”/*” : [#1,#2]. Returns #1 divided by #2
Rounding
  • “method” : [ #1,”round”, [#2]. Returns #1 rounded to #2 decimal places
Remainder of
  • ”%*” : [#1,#2]. Returns the remainder of #1 divided by #2 (e.g. 12 % 10 is 2)
{
      "type": "operation",
      "output": "four",
      "operation": {
        "+": [
          {
            "var": "two"
          },
          2
        ]
      }
}
{
      "type": "operation",
      "output": "two",
      "operation": {
        "-": [
          {
            "var": "four"
          },
          2
        ]
      }
}
{
      "type": "operation",
      "output": "eight",
      "operation": {
        "*": [
          {
            "var": "four"
          },
          2
        ]
      }
}
{
      "type": "operation",
      "output": "twoAndTwoThirds",
      "operation": {
        "/": [
          {
            "var": "eight"
          },
          3
        ]
      }
}
{
      "type": "operation",
      "operation": {
        "method": [
          {
            "var": "twoAndTwoThirds"
          },
          "round",
          [
            2
          ]
        ]
      },
      "output": "twoPoint66"
}
{
      "type": "operation",
      "output": "two",
      "operation": {
        "%": [
          {
            "var": "eight"
          },
          3
        ]
      }
}      

Lists

Lists can be simple lists of values (e.g. “Cat, “Dog”, “Horse”) or be lists of objects.

You access an item of a list using a numeric index starting from 0 (e.g. myList/2 is the third item in the list).

You can use a variable index when accessing a variable as part of a text string (e.g. “The item is {mylist{n})”)

You cannot use variable indexes when accessing variables using the “var” property. You will have to use the getItem method to extract an item from a list.

In the following examples #1, #2 etc can be replaced by a variable using the “var” property or a constant value. See Using Variables

The following operations can be used to build and manipulate lists:

Add an item to the end of a list
  • “method” : [ #1,”addItem”, [#2]]. Returns list #1 with #2 appended to it
Get an item from a list
  • “method” : [ #1,”getItem”, [#2]]. Returns the element of #1 at position #2 (zero being the first element)
Update a list item
  • “method” : [ #1,”updateItem”,[#2,#3]]. Returns list #1 with the element at position #2 replaced with #3
Remove an items from a list
  • “method” : [ #1,”removeItem”, [#2]]. Returns list #1 with the element at position #2 removed
Get the number of items in a list
  • “method” : [ #1,”getCount”]. Returns the number of elements in list #1
Create a list from a text item with a separator
  • “method” : [ #1,”split”, [#2]. Returns a list created from text #1 using #2 as a separator (e.g. #1 = “a,b,c”, “#2 = “,”, list contains 3 items “a”,”b” and “c”)
Sort a list
  • “method” : [ #1,”sort”, [#2,#3]]. Returns list #1 sorted by property name #2. #3 controls the sort direction (“ascending” or “descending”). Additional property/direction parameter pairs can be specified. If the list is a simple list with no properties replace [#2,#3] with []
Filter a list
  • “method” : [ #1,”filter”, [#2,#3]]. Returns list #1, excluding any elements where the property #2 does not equal the value #3. #2 and #3 can be replaced with a logical expression. Elements where the expression evaluates to false are excluded from the results. In the logical expression, references to properties should be preceded by the property “current” :
{
      "type": "operation",
      "operation": {
        "method": [
          {
            "var": "myList"
          },
          "addItem",
          [
            {
              "var": "text"
            }
          ]
        ]
      },
      "output": "myList"
}
{
      "type": "operation",
      "operation": {
        "method": [
          {
            "var": "myList"
          },
          "getItem",
          [
            {
              "var": "index"
            }
          ]
        ]
      },
      "output": "myItem"
}          
{
      "type": "operation",
      "operation": {
        "method": [
          {
            "var": "myList"
          },
          "updateItem",
          [
            {
              "var": "index"
            },
            {
              "var": "myItem"
            }
          ]
        ]
      },
      "output": "myList"
}          
{
      "type": "operation",
      "operation": {
        "method": [
          {
            "var": "myList"
          },
          "removeItem",
          [                  
            {
              "var": "index"
            }
          ]
        ]
      },
      "output": "myList"
}          
{
      "type": "operation",
      "operation": {
        "method": [
          {
            "var": "myList"
          },
          "getCount"                
        ]
      },
      "output": "count"
}          
{
      "type": "operation",
      "operation": {
        "method": [
          {
            "var": "csvText"
          },
          "split",
          [
            ","
          ]
        ]
      },
      "output": "myList"
}          
{
  "type": "operation",
  "operation": {
    "method": [
      {
        "var": "values"
      },
      "sort",
      [
        "value",
        "ascending"
      ]
    ]
  },
  "output": "values"
}
{
  "type": "operation",
  "operation": {
    "method": [
      {
        "var": "chosenArea"
      },
      "filter",
      [
        {
          ">=": [
            {
              "current": "date"
            },
            {
              "var": "startDate"
            }
          ]
        }
      ]
    ]
  }
  ,
  "output": "filteredDates"
}     

XML

The following operations can be used to convert XML to a talksuite object (which is stored as JSON):

Convert XML to an object
  • “String.xmlToJson” : [ #1]. Returns an object with properties and data defined as XML in #1
Convert escaped XML to an object
  • “String.escapedXmlToJson” : [ #1]. Returns an object with properties and data defined as escaped XML in #1

XML attributes are converted to a property of the element preceded by @

If an element has attributes or child element plus a value, then the value is converted to a property with the name #text, otherwise just use the property name

In the examples on the right Example XML contains unescaped XML and Object contains the talksuite object it is converted to, expressed as JSON

Escaped XML

Escaped XML replaces special characters with codes.

Here are the codes:

{
      "type": "operation",
      "operation": {
        "String.xmlToJson": [
           {
            "var": "xml"
          }
        ]
      },
      "output": "object"
}
{
      "type": "operation",
      "operation": {
        "String.escapedXmlToJson": [
           {
            "var": "escapedXml"
          }
        ]
      },
      "output": "object"
}
<people>
  <person gender="female">
    001
    <firstname>Anna</firstname>
    <lastname>Smith</lastname>
  </person><person gender="male">
    002
    <firstname>John</firstname>
     <lastname>Jones</lastname>
  </person>
</people>   
{
  "people": {
    "person": [
      {
        "@gender": "female",
        "#text": "001",
        "firstname": "Anna",
        "lastname": "Smith"
      },
      {
        "@gender": "male",
        "#text": "002",
        "firstname": "John",
        "lastname": "Jones"
      }
    ]
  }
}

Using Variables

There are three ways of accessing variables in talksuite. This section explains when to use each of them

Mixed text and variables

There are a number of properties that can contain a mixture of text and variables. In these cases the variables names are enclosed in curly brakets, like this

“Hello {name}”

This syntax can be used in the following places:

Property Nodes
message message, stringprompt, numberPrompt, datePrompt,dateTimePrompt, timePrompt,choicePrompt, confirmationPrompt, attachmentPrompt
retryMessage stringPrompt, numberPrompt, datePrompt,dateTimePrompt, timePrompt,choicePrompt, confirmationPrompt, attachmentPrompt
service/url action, downloadAction
service/body action
content/title card, cardcollection
content/subtitle card, cardcollection
content/text card, cardcollection
content/image card, cardcollection
customContent customCardCollection, stringPrompt, messagePrompt

Variables using the var property or constants

Parameters to logical operations can be variables or constants (but not a mix of both). Logical operations (eg. <=, and, +) are used in operation nodes and the validation properties of prompts

Variables are references as :

{ “var” : “myVar” }

Constants are entered as numbers e.g. 123, text, e.g. “123” or booleans e.g. true

talskuite will allow you to use the {} notation in JSON logic parameters. Avoid this and use the “var” notations

Variables name only

The content of output, listname and display name properties always just contain the variable name

List Indexes

You access an item of a list using a fixed numeric index (e.g. myList/2) but you cannot access a list item using an index in a variable (e.g. mylist/{index} will not work). Use getItem or updateItem operations to update lists using variable indexes

{
  "type": "message",
  "message": "Hello {name}"           
}
{
  "type": "operation",
  "output": "two",
   "operation": {
      "-": [
        {
          "var": "four"
        },
        2
      ]
   }
}
{
  "type": "operation",
   "operation": {
     "Date.currentDate": []
    },
    "output": "DateToday"
}

Creating a dialogue

To create a new dialogue, select the icon from the global area or a project

Then select the Add dialogue from the pop-up menu

Template JSON for a new dialogue is displayed. You must provide in the id, trigger and at least one node. You can then use the Create menu option to create the dialogue.

If there are errors in the dialogue, they will be displayed above the JSON.

If the dialogue has been successfully created, the menu will change to provide the following options:

  • Move. Move the dialogue to another project
  • Update. Update the dialogue
  • Delete. Delete the dialogue

An introduction to JSON

JSON is a notation for storing structured data. It’s similar to, but simpler than, XML. An example is shown to the right.

All JSON files start and end with curly brackets. The data is made up of property names (always in quotes) and property values. The property names and values are separated by colons. The simplest possible JSON file is a single property and value. E.g.

{ "firstName" : "Tony" }

Properties are separated by commmas. E.g.

{ "firstName" : "Tony" },
{ "lastNameName" : "Hancock" },

Property values can contain text (as above) which is in quotes, or numbers (see price below), which are not in quotes, or true/false values (see billingAddress below)

{ "price" : 7.99 }

{ "billingAddress" : true }

As well as a single value, properties can contain other properties. Properties can be nested within properties to several levels (see town within address).

Properties can contain lists of values. Lists are enclosed within square brackets (see favourites).

Lists can be made up of collections of properties (see orders)

{
  "customer": {
    "firstName": "Tony",
    "lastName": "Hancock",
    "favourites" : [
      "Books",
      "Albums",
      "Games"
    ]
  },
  "orders": [
    {
      "productType": "Book",
      "productDetails": {
        "title": "1984",
        "author": "George Orwell"
      },
      "price": 7.99
    },
    {
      "productType": "Album",
      "productDetails": {
        "title": "Aqualung",
        "artist": "Jethro Tull"
      },
      "price": 15
    }
  ],
  "address": {
    "houseNumber": 23,
    "streetName": "Railway Cuttings",
    "town": "East Cheam",
    "billingAddress": true
  }
}              

Editor features

Press F1 to get a full list of editor features. Here a few of the most useful:

  • Find text (F3)
  • Format document (on the right click menu)
  • Change all occurences (on the right click menu)

Help with syntax

If your dialogue is not valid JSON, the first text after the error will be underlined in red.

If your dialogue is valid JSON, but does not match the dialogue schema, the invalid text will be underlined in green.

If you hover over the green underlined text, a schema error will be displayed.

If you hover over the red underlined text, a json error will be displayed

Snippets

Don’t type JSON in from scratch! Use snippets to insert template JSON

Trigger snippets

In the trigger section of a dialogue, type “trigger” to see a list of the trigger types available

Select the required trigger type and the JSON for that type will be inserted

Node snippets

There is at least one snippet for each node type. Typing in the node area will display a list of matching snippets

Selecting the snippet will insert a sample node of that type with default properties

Default property values are highlighted. You can tab between the highlighted values to edit them.

Individual properties within a node can be selected by pressing space and selecting the property name

Combining Node and Logic Snippets

When a property of a node can contain logic, the property value is left as blank placeholder in the snippet. You need to insert the appropriate logic snippet into the blank property value.

The following properties are left as placeholders:

  • rule in a decision node snippet

  • operation in an operation node snippet

  • validation in prompt node snippets

  • both logic parameters in “and” and “or” logic snippets

Here is an example of inserting the current date into an operation node:

First, start typing the name of the node to insert (operation)

Then select the snippet from the menu to insert the node text

Position the cursor inside the empty operation property and start typing the name of the logical operation you want to use (current date)

Select the required operation from the list

Built-in Variable Snippets

You can insert built-in variable snippets. The example below shows the insertion of the conversation timezone variable into a cat operation.

Select the timezone variable

Insert it into the node

Syntax Overview

The diagram below shows some of the snippets available. It shows the trigger types, node types and the operation, rule and comparion properties that can be inserted inside nodes. It also shows custom content available to the authors’ channel and a list of built-in variables

Office 365 APIs

Miscrosoft provides APIs to access Office 365 services such as: SharePoint, OneDrive, Outlook/Exchange, Microsoft Teams, OneNote, Planner, and Excel

There is no special support for Office 365 APIs in talksuite. They are treated like any other external API. However some detail on how to use them is inculded here as they can be used to provide data storage that can be accessed by all bot users

The APIS are referred to as MS Graph

Accessing data in Excel

If you want to hold data that can be accessed by multiple users, you can host the data in an area that will be accessible to your bot users, such as sharepoint. Data can be stored in Excel files and accessed via (MS graph) APIs.

Follow these steps to gain access to your data

  1. Make sure all the users that need to access the data to have access to the sharepoint site.

  2. Set up an Authentication Provider in your bot config. You will need to give talksuite appropriate access to Office 365 files using the scope property (e.g. “Files.Read”)

  3. Find the site ID of the shairpoint site. You can get the ID from the following API call

    https://graph.microsoft.com/v1.0/sites?search=my-site-name

    Get the id property from the result of this call and put it in a bot constant

  4. Create a data file and write a dialogue to access it. See the example snippet on the right

Bot users will be asked to sign-in to Office 365 when they trigger the dialogue

The example reads the following Excel file.

and displays the data

{
  "id": "shared data",
  "trigger": {
    "type": "message",
    "values": [
      "shared data"
    ]
  },
  "nodes": [
    {
      "type": "action",
      "service": {
        "method": "GET",
        "authorised": true,
        "url": "https://graph.microsoft.com/v1.0/sites/{Bot/SharepointSite}/drive/root:/Book.xlsx:/workbook/worksheets('Sheet1')/usedRange",
        "headers": {
          "Accept": "application/json"
        }
      },
      "outputs": {
        "body": {
          "matrix": "text"
        }
      }
    },
    {
      "type": "message",
      "message": "Salaries are:\n\r{matrix/1/0} - {matrix/1/1}\n\r{matrix/2/0} - {matrix/2/1}\n\r{matrix/3/0} - {matrix/3/1}"
    },
    {
      "type": "action",
      "service": {
        "method": "GET",
        "authorised": true,
        "url": "https://graph.microsoft.com/v1.0/sites/{Bot/SharepointSite}/drive/root:/Book.xlsx:/workbook/worksheets('Sheet1')/charts/salaries/image",
        "headers": {
          "Accept": "application/json"
        }
      },
      "outputs": {
        "body": {
          "chart": "value"
        }
      }
    },
    {
      "type": "card",
      "content": {
        "title": "Salaries",
        "image": "data:image/png;base64,{chart}"
      }
    }
  ]
}           

Date built-in

The NLP Entities node can be used to extract the date built-in entity (builtin.datetimeV2.date) from a LUIS response. talksuite also adds a period property to the results, so phrases such as morning or afternoon generate an AM or PM period value

The code example on the right assumes a LUIS entitiy which will match an utterance starting “I want to book a holiday…”. If the date is Monday 12th November 2018, the utterances below will produce the following output in the matchedEntities property

Utterance First date First period Second date Second period
i want to book a holiday for tuesday 2018-11-13 blank blank blank
i want to book a holiday for tuesday afternoon 2018-11-13 PM blank blank
i want to book a holiday from tuesday afternoon to wednesday morning 2018-11-13 PM 2018-11-14 AM
i want to book a holiday starting from the afternoon of next monday through to next wednesday morning 2018-11-19 PM 2018-11-21 AM

The code example (NLP Node) uses the default ambiguity rules, which chooses a future date when the utterance could be past of future (so on a Tuesday, “wednesday” will be interpreted as tomorrow, rather than the previous Wednesday). See the Ambiguity rules for details

The LUIS response for the third utterance in the table above is shown on the right

{
        "id": "NLP Node",
        "trigger": {
          "type": "intent",
          "intent": "BookHoliday",
          "output": "luisJson"
         },
         "nodes": [
         {
           "type": "nlpEntities",
           "input": "luisJson",
            "requiredEntities": "twodates",
            "outputs": {
              "matchedEntities": "results"
            }
         },
         {
           "type": "message",
           "message": "Book holiday intent"
         },
         {
            "type": "sequenceDialogue",
            "dialogueId": "NLP Output",
            "inputItem": "result",
            "listName": "results"
         }
       ],
       "model": {
         "twodates": [
            "date",
            "date"			
         ]
      }
}
{
  "id": "NLP Output",
  "trigger": {
    "type": "nestedDialogue"
  },
  "nodes": [
    {
      "type": "message",
      "message": "Type: [{result/type}] Value: [{result/value}] Period: [{result/period}] Mod: [{result/mode}]"
    }
  ]          
}
{
  "query": "i want to book a holiday from tuesday afternoon to wednesday morning",
  "topScoringIntent": {
    "intent": "BookHoliday",
    "score": 0.9775836
  },
  "entities": [
    {
      "entity": "tuesday afternoon",
      "type": "builtin.datetimeV2.datetimerange",
      "startIndex": 30,
      "endIndex": 46,
      "resolution": {
        "values": [
          {
            "timex": "XXXX-WXX-2TAF",
            "type": "datetimerange",
            "start": "2018-11-27 12:00:00",
            "end": "2018-11-27 16:00:00"
          },
          {
            "timex": "XXXX-WXX-2TAF",
            "type": "datetimerange",
            "start": "2018-12-04 12:00:00",
            "end": "2018-12-04 16:00:00"
          }
        ]
      }
    },
    {
      "entity": "wednesday morning",
      "type": "builtin.datetimeV2.datetimerange",
      "startIndex": 51,
      "endIndex": 67,
      "resolution": {
        "values": [
          {
            "timex": "XXXX-WXX-3TMO",
            "type": "datetimerange",
            "start": "2018-11-28 08:00:00",
            "end": "2018-11-28 12:00:00"
          },
          {
            "timex": "XXXX-WXX-3TMO",
            "type": "datetimerange",
            "start": "2018-12-05 08:00:00",
            "end": "2018-12-05 12:00:00"
          }
        ]
      }
    }
  ],
  "sentimentAnalysis": {
    "label": "negative",
    "score": 0.2434367
  }
}

Duration built-in

The NLP Entities node can be used to extract the duration built-in entity (builtin.datetimeV2.duration) from a LUIS response. The value returns the duration expressed as a number of seconds. In some cases the node can infer a pair of dates from a date and a duration. See the third example in the table below

The code example on the right assumes a LUIS entitiy which will match an utterance starting “I want to book a holiday…”. The required entities are date, duration, date and duration so talksuite will match up to two dates and two durations in the LUIS repsonse and will ignore anything else. If the date is Monday 12th November 2018, the utterances below will produce the following output in the matchedEntities property

Utterance First date First duration Second date Second duration
i want to book a holiday taking 5 hours on sunday through to 3 hours on monday 2018-11-18 18000 2018-11-19 10800
i want to book a holiday for 3 days starting on wednesday 2018-11-21 blank 2018-11-23 blank

The LUIS response for the first utterance in the table above is shown on the right

{
        "id": "NLP Node",
        "trigger": {
          "type": "intent",
          "intent": "BookHoliday",
          "output": "luisJson"
         },
         "nodes": [
         {
           "type": "nlpEntities",
           "input": "luisJson",
            "requiredEntities": "datesAndDurations",
            "outputs": {
              "matchedEntities": "results"
            }
         },
         {
           "type": "message",
           "message": "Book holiday intent"
         },
         {
            "type": "sequenceDialogue",
            "dialogueId": "NLP Output",
            "inputItem": "result",
            "listName": "results"
         }
       ],
       "model": {
         "datesAndDurations": [
            "date",
            "duration"
            "date",
            "duration"			
         ]
      }
}
{
  "id": "NLP Output",
  "trigger": {
    "type": "nestedDialogue"
  },
  "nodes": [
    {
      "type": "message",
      "message": "Type: [{result/type}] Value: [{result/value}] Period: [{result/period}] Mod: [{result/mode}]"
    }
  ]          
}
{
  "query": "i want to book a holiday taking 5 hours on sunday through to 3 hours on monday",
  "topScoringIntent": {
    "intent": "BookHoliday",
    "score": 0.980723858
  },
  "entities": [
    {
      "entity": "5 hours",
      "type": "builtin.datetimeV2.duration",
      "startIndex": 32,
      "endIndex": 38,
      "resolution": {
        "values": [
          {
            "timex": "PT5H",
            "type": "duration",
            "value": "18000"
          }
        ]
      }
    },
    {
      "entity": "sunday",
      "type": "builtin.datetimeV2.date",
      "startIndex": 43,
      "endIndex": 48,
      "resolution": {
        "values": [
          {
            "timex": "XXXX-WXX-7",
            "type": "date",
            "value": "2018-11-25"
          },
          {
            "timex": "XXXX-WXX-7",
            "type": "date",
            "value": "2018-12-02"
          }
        ]
      }
    },
    {
      "entity": "3 hours",
      "type": "builtin.datetimeV2.duration",
      "startIndex": 61,
      "endIndex": 67,
      "resolution": {
        "values": [
          {
            "timex": "PT3H",
            "type": "duration",
            "value": "10800"
          }
        ]
      }
    },
    {
      "entity": "monday",
      "type": "builtin.datetimeV2.date",
      "startIndex": 72,
      "endIndex": 77,
      "resolution": {
        "values": [
          {
            "timex": "XXXX-WXX-1",
            "type": "date",
            "value": "2018-11-26"
          },
          {
            "timex": "XXXX-WXX-1",
            "type": "date",
            "value": "2018-12-03"
          }
        ]
      }
    },
    {
      "entity": "5",
      "type": "builtin.number",
      "startIndex": 32,
      "endIndex": 32,
      "resolution": {
        "subtype": "integer",
        "value": "5"
      }
    },
    {
      "entity": "3",
      "type": "builtin.number",
      "startIndex": 61,
      "endIndex": 61,
      "resolution": {
        "subtype": "integer",
        "value": "3"
      }
    }
  ],
  "sentimentAnalysis": {
    "label": "negative",
    "score": 0.210899085
  }
}     

Date-times built-in

The NLP Entities node can be used to extract the datetime built-in entity (builtin.datetimeV2.datetime) from a LUIS response. The value returns a combined date and time.

The code example on the right assumes a LUIS entitiy which will match an utterance starting “I want to book a holiday…”. The required entities are two datetimes, so talksuite will match up to two date-times and will ignore anything else. If the date is Monday 12th November 2018, the utterances below will produce the following output in the matchedEntities property

Utterance First date-time Second date-time
i want to book a holiday from 5 oclock on sunday to 3oclock on monday 2018-11-18 17:00:00 2018-11-19 15:00:00
i want to book a holiday starting at 9am on 20th November 2018-11-20 09:00:00 blank

The code example uses the default ambiguity rules, which chooses a future date when the utterance could be past of future (so on a Tuesday, “wednesday” will be interpreted as tomorrow, rather than the previous Wednesday. If the time is ambiguous, it will prefer a time in the range 7am to 7pm (so 5 o’clock will default to 5pm). See the Ambiguity rules for details

The LUIS response for the first utterance in the table above is shown on the right

{
        "id": "NLP Node",
        "trigger": {
          "type": "intent",
          "intent": "BookHoliday",
          "output": "luisJson"
         },
         "nodes": [
         {
           "type": "nlpEntities",
           "input": "luisJson",
            "requiredEntities": "dateTimes",
            "outputs": {
              "matchedEntities": "results"
            }
         },
         {
           "type": "message",
           "message": "Book holiday intent"
         },
         
            "type": "sequenceDialogue",
            "dialogueId": "NLP Output",
            "inputItem": "result",
            "listName": "results"
         }
       ],
       "model": {
         "dateTimes": [
            "datetime",                 
            "datetime"			
         ]
      }
}
{
  "id": "NLP Output",
  "trigger": {
    "type": "nestedDialogue"
  },
  "nodes": [
    {
      "type": "message",
      "message": "Type: [{result/type}] Value: [{result/value}] Period: [{result/period}] Mod: [{result/mode}]"
    }
  ]          
}
{
  "query": "i want to book a holiday from 5 oclock on sunday to 3oclock on monday",
  "topScoringIntent": {
    "intent": "BookHoliday",
    "score": 0.886165559
  },
  "entities": [
    {
      "entity": "from 5 oclock on sunday to 3oclock on monday",
      "type": "builtin.datetimeV2.datetimerange",
      "startIndex": 25,
      "endIndex": 68,
      "resolution": {
        "values": [
          {
            "timex": "(XXXX-WXX-7T05,XXXX-WXX-1T03,PT22H)",
            "type": "datetimerange",
            "start": "2018-11-25 05:00:00",
            "end": "2018-11-26 03:00:00"
          },
          {
            "timex": "(XXXX-WXX-7T05,XXXX-WXX-1T03,PT22H)",
            "type": "datetimerange",
            "start": "2018-12-02 05:00:00",
            "end": "2018-12-03 03:00:00"
          },
          {
            "timex": "(XXXX-WXX-7T17,XXXX-WXX-1T15,PT22H)",
            "type": "datetimerange",
            "start": "2018-11-25 17:00:00",
            "end": "2018-11-26 15:00:00"
          },
          {
            "timex": "(XXXX-WXX-7T17,XXXX-WXX-1T15,PT22H)",
            "type": "datetimerange",
            "start": "2018-12-02 17:00:00",
            "end": "2018-12-03 15:00:00"
          }
        ]
      }
    },
    {
      "entity": "5",
      "type": "builtin.number",
      "startIndex": 30,
      "endIndex": 30,
      "resolution": {
        "subtype": "integer",
        "value": "5"
      }
    }
  ],
  "sentimentAnalysis": {
    "label": "negative",
    "score": 0.266002953
  }
}  

Time built-in

The NLP Entities node can be used to extract the time built-in entity (builtin.datetimeV2.time) from a LUIS response. The value returns a time.

The code example on the right assumes a LUIS entitiy which will match an utterance starting “I want to book a holiday…”. The required entitiy is a time, so talksuite will match a single time entity and will ignore anything else. The utterances below will produce the following output in the matchedEntities property

Utterance Time
i want to book a holiday at 10 o’clock 10:00:00
i want to book a holiday at 10pm 22:00:00

The code example uses the default ambiguity rules, which chooses times in the range 7am to 7pm. If the time is ambiguous, it will prefer a time in the range 7am to 7pm (so 5 o’clock will default to 5pm). See the Ambiguity rules for details

The LUIS response for the first utterance in the table above is shown on the right

{
        "id": "NLP Node",
        "trigger": {
          "type": "intent",
          "intent": "BookHoliday",
          "output": "luisJson"
         },
         "nodes": [
         {
           "type": "nlpEntities",
           "input": "luisJson",
            "requiredEntities": "time",
            "outputs": {
              "matchedEntities": "results"
            }
         },
         {
           "type": "message",
           "message": "Book holiday intent"
         },
         
            "type": "sequenceDialogue",
            "dialogueId": "NLP Output",
            "inputItem": "result",
            "listName": "results"
         }
       ],
       "model": {
         "timet": [
            "time"                  
         ]
      }
}
{
  "id": "NLP Output",
  "trigger": {
    "type": "nestedDialogue"
  },
  "nodes": [
    {
      "type": "message",
      "message": "Type: [{result/type}] Value: [{result/value}] Period: [{result/period}] Mod: [{result/mode}]"
    }
  ]          
}
{
  "query": "i want to book a holiday at 10 o’clock",
  "topScoringIntent": {
    "intent": "BookHoliday",
    "score": 0.8900969
  },
  "entities": [
    {
      "entity": "10 o’clock",
      "type": "builtin.datetimeV2.time",
      "startIndex": 28,
      "endIndex": 37,
      "resolution": {
        "values": [
          {
            "timex": "T10",
            "type": "time",
            "value": "10:00:00"
          },
          {
            "timex": "T22",
            "type": "time",
            "value": "22:00:00"
          }
        ]
      }
    },
    {
      "entity": "10",
      "type": "builtin.number",
      "startIndex": 28,
      "endIndex": 29,
      "resolution": {
        "subtype": "integer",
        "value": "10"
      }
    }
  ],
  "sentimentAnalysis": {
    "label": "negative",
    "score": 0.138628185
  }
}      

Resolving ambiguity

The NLP Entities node can be used to extract entity data from a LUIS response (including the date time build-ins - builtin.datetimeV2). These built-in entities return a choice of values when the utterance is ambiguous. For example “Monday” can mean the Monday coming or the Monday just gone. “10 o’clock” can mean 10am or 10pm. The node allows you to specify which of the choices you prefer.

The rules property of the node can have the following sub-properties:

  • dateContext. If set to “latest”, the later of a choice of ambiguous dates is chosen. If set to “earliest”, the earlier of the choices is chosen. “latest” is appropriate for forward looking applications such as holiday booking or meeting planning. “earliest” is appropriate for backward looking applications such as expenses or overtime booking. The default is “latest”

  • likelyStartTime. This allows a preferred 12 hour period for ambiguous times to be chosen. The value is an hour to start the period at. For example if the application is office-based then a value of “7” would produce a preferred period of 7am to 7pm. “10 o’clock” would be interpreted as 10 am. If the application was (say) pizza delivery, then a time context of “12” would set a preferred period of noon to midnight, so “10 o’clock” would be 10pm. The default is “7”

{
        "id": "NLP Node",
        "trigger": {
          "type": "intent",
          "intent": "BookHoliday",
          "output": "luisJson"
         },
         "nodes": [
         {
           "type": "nlpEntities",
           "input": "luisJson",
            "requiredEntities": "dateTimes",
            "rules" : {
              "dateContext" : "latest",
              "likelyStartTime" : "7"
            }
            "outputs": {
              "matchedEntities": "results"
            }
         },
         {
           "type": "message",
           "message": "Book holiday intent"
         },
         
            "type": "sequenceDialogue",
            "dialogueId": "NLP Output",
            "inputItem": "result",
            "listName": "results"
         }
       ],
       "model": {
         "dateTimes": [
            "datetime",                 
            "datetime"			
         ]
      }
}
{
  "id": "NLP Output",
  "trigger": {
    "type": "nestedDialogue"
  },
  "nodes": [
    {
      "type": "message",
      "message": "Type: [{result/type}] Value: [{result/value}] Period: [{result/period}] Mod: [{result/mode}]"
    }
  ]          
}

Other entities

The NLP Entities node can be used to extract entities that are not LUIS built-ins

The code example on the right assumes a LUIS entitiy which will match an utterance starting “I want a pint of lager”. The required entities are drink (which matches to lager) and size (which matches to pint). The results will be

Utterance drink size
i want a pint of lager lager pint

The LUIS response for the utterance in the table above is shown on the right

{
        "id": "NLP Node",
        "trigger": {
          "type": "intent",
          "intent": "Drink order",
          "output": "luisJson"
         },
         "nodes": [
         {
           "type": "nlpEntities",
           "input": "luisJson",
            "requiredEntities": "drink",
            "outputs": {
              "matchedEntities": "results"
            }
         },
         {
           "type": "message",
           "message": "Drink order intent"
         },
         
            "type": "sequenceDialogue",
            "dialogueId": "NLP Output",
            "inputItem": "result",
            "listName": "results"
         }
       ],
       "model": {
         "dateTimes": [
            "drink",                 
            "size"			
         ]
      }
}
{
  "id": "NLP Output",
  "trigger": {
    "type": "nestedDialogue"
  },
  "nodes": [
    {
      "type": "message",
      "message": "Type: [{result/type}] Value: [{result/value}] Period: [{result/period}] Mod: [{result/mode}]"
    }
  ]          
}
{
  "query": "i want a pint of lager",
  "topScoringIntent": {
    "intent": "Drink order",
    "score": 0.9613707
  },
  "entities": [
    {
      "entity": "lager",
      "type": "drink",
      "startIndex": 17,
      "endIndex": 21,
      "score": 0.9462079
    },
    {
      "entity": "pint",
      "type": "size",
      "startIndex": 9,
      "endIndex": 12,
      "score": 0.929178238
    }
  ],
  "sentimentAnalysis": {
    "label": "negative",
    "score": 0.0957952142
  }
}

Grouping entities

The NLP Entities node can be used to extract groups of entities. If an item in the requiredEntities list is a comma-separated list of entities, enclosed by brackets, these entities will be grouped. talksuite will look for instances of “ and” in between recognised entities and treat this as a separator. When an “and” separator is found, the matching process will move onto the next group.

The code example on the right assumes a LUIS entitiy which will match an utterance starting “I want to book a holiday”. There are two groups of required entities, both containing a date and duration

Utterance group 1 - date group 1 - duration group 2 - date group 2 - duration
I want to book a holiday for monday and for 3 hours on tuesday monday blank tuesday 3 hours
I want to book a holiday for 3 hours on monday and for 4 hours on tuesday monday 3 hours tuesday 4 hours
I want to book a holiday for monday for 3 hours and tuesday monday 3 hours tuesday blank
{
        "id": "NLP Node",
        "trigger": {
          "type": "intent",
          "intent": "Drink order",
          "output": "luisJson"
         },
         "nodes": [
         {
           "type": "nlpEntities",
           "input": "luisJson",
            "requiredEntities": "datesAndDurations",
            "outputs": {
              "matchedEntities": "results"
            }
         },
         {
           "type": "message",
           "message": "Holiday booking intent"
         },
         
            "type": "sequenceDialogue",
            "dialogueId": "NLP Output",
            "inputItem": "result",
            "listName": "results"
         }
       ],
       "model": {
         "datesAndDurations": [
            "(date,duration)",                 
            "(date,duration)"			
         ]
      }
}
{
  "id": "NLP Output",
  "trigger": {
    "type": "nestedDialogue"
  },
  "nodes": [
    {
      "type": "message",
      "message": "Type: [{result/type}] Value: [{result/value}] Period: [{result/period}] Mod: [{result/mode}]"
    }
  ]          
}

Notification text

Default text of a push notification

Properties
notificationMessage

Default push notification text when there is no suitable text in the message (e.g an image)

The default value (“New Message”) may be changed for each language

{
  "systemTexts": {
    "notificationMessage": [
      "New message"
    ]
  }        
  "region": "en-GB"
}

Dialogue not found

Default response when no dialogue can be found to be triggered

Properties
dialogueNotFound

If no dialogue can be triggered on user input, this message is displayed.

The default value (“I’m sorry I don’t understand that”) may be changed for each language

If you want to do more than just output a message when the user input is not understood, you can write a No Trigger Match dialogue

{
  "systemTexts": {
    "dialogueNotFound": [
      "I'm sorry. I don't understand that"
    ]
  }        
  "region": "en-GB"
}

Creating a Bot

When you create a bot using the Add Bot link at the top of the left hand menu, you will be asked to give the bot a name

The bot must be registered with several Microsoft services before it can be used. This may take a few moments, so a progress indicator shows each step of the process

Once the registation is complete, press the View JSON button to see the configuration

Registration and Localisation

Bot configuration mandatory fields defining the registration of the bot with microsoft and timezone and language defaults

Properties
msAppId

App Id for the application registered with the Microsoft Application Registration Portal. This will be automatically populated when you create your bot

msAppSecret

Password for the application registered with the Microsoft Application Registration Portal. This will be automatically populated when you create your bot

region

Region for the bot. Defaults to en-GB

timeZone

Time zone for the bot. Defaults to Europe/London

name

Name of the bot. Must be unique within the organisation

The bot has been configured to use the Skype channel, you can test your bot as follows:

  • Create a conversation start dialogue in the global area of your organisation, which outputs a message

  • Invite the bot as a Skype contact using the following link https://join.skype.com/bot/appId where appId is the msAppId property from your bot.

  • Add the bot as a Skype contact. You should now wait a few minutes for the contact to be activated

  • Send a message to the contact. The message in the conversation start dialogue should be displayed.

{
  "data": {
    "attributes": {   
      "msAppId": "place you app id here",
      "msAppSecret": "place your app secret here",
      "region": "en-GB",
      "timeZone": "Europe/London",
      "name": "Basic bot"
    }
  }
}      

Talksuite Desktop App

Configure the bot to work with the talksuite desktop app and other apps that use the directline channel

Properties
directLineSecret

Secret required to work with the directline API. This is required to connect to the talksuite desktop app or the iOS and Android people first and iTrent native apps. This will be automatically populated when you create your bot

discoveryUrl

Read only property containing the discovery URL for for the talksuite desktop app and MHR’s people first mobile apps.

Authentication Providers

Details of authentication providers supported by the bot

Properties
type

OpenIdConnect for people first or MSGraph or Slack

authParameters

List of application-specific parameters

azureADResource

Azure active directory

clientId

OpenId Connect Client Id

clientSecret

OpenId Connect Client secret

isPrimary

If true, authentication will always be required. If false, authentication will only be required when an attempt is made to access an API. There can be only one primary

name

Application name shown when the user signs in

scope

Application-specific parameters to control the access level requested

serviceDomains

List of application domains. Used to determine which application is being access from the url in the API call

tokenIssuer

Token issuing end point for OpenIdConnect

authoriszationUrl

End point to get an initial authorisation code for oauth2

tokenUrl

Token issuing end point for oauth2

maxAuthPrompts

Number of attempts the user has to authenticate, before the authentication process is stopped

A bot can support multiple authenticaion providers. Each provider is configured in a record in the authentication providers list. OpenIdConnect and oauth2 (for Slack) authentication policies are supported. Bespoke support is also provided for MHR’s iTrent product.

If a provider is marked as primary, then authentication will be performed before any dialogues can be run. Only one provider can be primary. Authentication for secondary providers will occur at the start of a dialogue that performs an API call for that provider. Authentication is not mandatory, so you can create an unauthenticated (i.e. public) bot.

{
  "data": {
    "attributes": { 
      "authenticationProviders": [   
        {
          "authorizationUrl": "place you oauth2 authorisation URL here",
          "authParameters": [
            {
              "name": "place your provider-specific parameter names here",
              "value": "place your provider-specific parameter values here"
            }
          ],
          "type": "OpenIdConnect",
          "clientId": "place your client ID here",
          "clientSecret": "place your client secret here",
          "isPrimary": false,
          "maxAuthPrompts": 3,
          "name": "Name of the appliction being connected to",
          "serviceDomains": [
            "place the base url of the providers APIs here"
          ],
          "tokenIssuer": "place you OpenIdConnect token issuer here"
        },        
       ],     
      "msAppId": "place you app id here",
      "msAppSecret": "place your app secret here",
      "region": "en-GB",
      "timeZone": "Europe/London",
      "name": "Authenticated bot"
    }
  }
}      

Constants

Use bot constants to hold bot-specific values to be used by dialogues

Properties
name

Name of a constant

value

Value of a constant

publish

Include in the discovery API. Defaults to false

Bot constants are used to hold values that are common accross multiple dialogues, but may differ between bots. For example if a bot is being used by several different clients, the environment name and tehcnical support details may differ for each client. They can also be used to make authentication information from the bot available to the dialogues. For example the service domain could be made available and used as a base URL for API calls

The discovery API is used by the people first Android and iOS apps. Setting a constant to published will include the value of the constant in the returned data for the API. This API is public, so do not publish and constants with sensitive or secret data

{
  "data": {
    "attributes": {  
      "constants": [				
        {
          "name": "environmentName",
          "value": "Production"
        },
        {
          "name" : "technicalSupport",
          "value" : "Please ring the IT team on 555-123-456"
        }
      ],      
      "msAppId": "place you app id here",
      "msAppSecret": "place your app secret here",
      "region": "en-GB",
      "timeZone": "Europe/London",
      "name": "Basic bot"
    }
  }
}      
{
  "type": "message",
  "message" : "An error has occurred. {Bot/technicalSupport}"
}            

Natural Language

Speicfy which LUIS model to use

Properties
appID

LUIS application ID. in LUIS, select the MANAGE menu option. The application ID is displayed at the top of the screen

appKey

LUIS key. From the MANAGE menu select the Keys and Endpoint soption from the Application Setting menu on the left. The key can be copied to the clipboard from the list of resouces at the bottom of the screen

intentThreshold

Each matched intent is given a score between 1 (very good match) and 0 (no match). This value prevents intents with a score below the threshold from firing

staging

If you have a staging version of the model as well as production, set this property to true to use the staging model.

These properties control which LUIS application utterances will be sent to

{
  "data": {
    "attributes": {   
     "naturalLanguageProcessors": [
       {
         "appId": "a321fdd9-bc3f-4dd8-9b17-b6694cfa15ce",
          "appKey": "4e9d9d05953e483ebf20ee882f9276f9",
          "intentThreshold": 0.7,
          "staging": false
       }
      ],              
      "msAppId": "place you app id here",
      "msAppSecret": "place your app secret here",
      "region": "en-GB",
      "timeZone": "Europe/London",
      "name": "Bot with NLP"
    }
  }
}      

Other properties

Other optional bot config properties

Properties
channels

Whitelist of Microsoft Bot Framework channels that can be used by the bot. If none are specified, all channels configured in Azure can be used

authenticationTimout

Number of seconds of inactivity after which the user must re-authenticate

webHookSecretKey

AWS-style key. If this is specifed for the bot, AWS headers generated from this key must be used to initiate dialogues externally. The app-level AWS key cannot be used if a key is supplied at bot level

disableTypingActivity

If set to true, the channel typing activity indicator will be disabled when the bot is processing

dialogueTimeout

Number of seconds of inactivity after which a running dialogue is cancelled

deploymentState

Read-only property recording the state of the bot deployment. Should be manual for a successfully created bot

isDeleting

Read-only property recording the start of the deletion of a bot. Should be false

The following items can be placed in the channels whitelist property: facebook skype msteams telegram kik email slack groupme sms emulator directline webchat console cortana

{
  "data": {
    "attributes": {  
      "authenticationTimeout": 0,
      "dialogueTimeout" : 3600,
      "channels": ["skype", "slack"],			
      "disableTypingActivity": false,
      "deploymentState" : "manual",
      "isDeleting" : false
      "webHookSecretKey": "password123"             
      "msAppId": "place you app id here",
      "msAppSecret": "place your app secret here",
      "region": "en-GB",
      "timeZone": "Europe/London",
      "name": "Basic bot"
    }
  }
}      

Projects

Specifies the projects that are active within a bot

Properties
projectId

A list of the Ids of the projects that you want to be avaialble to the bot. Only dialogues in projects in this list can fire in the bot (in addition to global dialogues)

{
  "data": {
    "attributes": {   
      "msAppId": "place you app id here",
      "msAppSecret": "place your app secret here",
      "region": "en-GB",
      "timeZone": "Europe/London",
      "name": "Bot with projects",
      "projectIds": [
         "Enter you project Id here",
         "Enter another project Id here"
     ],
   }
  }
}      

Brief and Debrief 🚧

Specifies when the Brief and Debrief will run each day

Properties
processes

A list of all the processes that can be run for the bot (currently brief and de-brief only)

processes/name

The name of the process (must be brief or debrief)

processes/recurrence

Parameters that define when the process will run

processes/hours

Hour of the day when the process will start (0-23). Syntactically this is a list, but only one value will be accepted.

processes/minutes

Minutes within the hour when the process will start. Syntactically this is a list, but only one value will be accepted.

{
  "data": {
    "attributes": {   
      "msAppId": "place you app id here",
      "msAppSecret": "place your app secret here",
      "region": "en-GB",
      "timeZone": "Europe/London",
      "name": "Bot with brief start time",
      "processes": [
         {
           "name": "brief",
            "recurrence": {
              "hours": [
                9
              ],
              "minutes": [
                 30
              ]
            }
         }
      ]
    }
  }
}      

Calculate Overtime

Write a dialogue to perform the following steps, when the user types “Claim overtime”

  1. Ask the user to enter their standard hourly rate of pay

  2. Ask the user to enter the date on which the overtime was earned

  3. Ask the user to enter the number of hours overtime worked on that day

  4. Calculate the overtime for the day at the standard rate for a week day, time and a half for a Saturday and double time for a Sunday

  5. Display the hours worked, date and total overtime for the day

  6. Ask the user if they want to add another day’s overtime or submit their Claim

  7. If the user selects to add another day, repeat the process from step 2

  8. If the user selects to submit their claim, display the total overtime pay for all days entered and stop the dialogue

For hints and tips and a solution go to the Overtime solution

Photo Album

Write 3 dialogues to add a photo to an album, view the photo album and remove a photo from the album

  • The add photo dialogue asks the user to upload a photo, then asks for a caption for the photo. The photos are stored in the user’s conversation

  • The photo album dialogue shows the photo album as a set (carousel) of cards, with the caption shown for each card

  • The remove album dialogue asks for a caption, then removes the photo with that caption

For hints and tips and a solution go to the Photo Album solution

Flight Calculator

Write a dialogue to calculate the arrival time in local time for a flight. The steps are:

  1. Select the departure airport (choose from Los Angeles, New York, London or Paris)

  2. Enter the departure time in the departure time zone

  3. Select the arrival airport (from the same list of locations)

  4. Calculate the arrival time in the timezone of the arrival airport

  5. Display the details of the flight, including the local arrival time

For hints and tips and a solution go to the Flight calculator solution

UK Public Holidays Example Dialogue

Example dialogues that use a public UK government API to display UK public holidays

{
  "id": "UK Public Holidays",
  "description": "Gets bank holiday for England and Wales, Scotland and Northern Ireland for a date range ",
  "trigger": {
    "type": "message",
    "values": [
      "uk public holidays",
      "bank holidays"           
    ]
  },
  "nodes": [
    {
      "description": "Display a menu of the areas of the UK that have separate public holidays",
      "type": "choicePrompt",
      "message": "Please select the part of the UK that you want to see the Bank Holidays for",
      "retryMessage": "Please select one of the options below",
      "listName": "areas",
      "output": "area"
    },
    {
      "description": "Prompt for a start date",
      "type": "datePrompt",
      "message": "I can show bank holidays for a year. Enter the start date of the year",
      "retryMessage": "Please enter a valid date",
      "output": "startDate"
    },
    {
      "description": "Format the date as, for example, 1st December 2018",
      "type": "operation",
      "operation": {
        "Date.format": [
          {
            "var": "startDate"
          },
          "do MMMM yyyy"
        ]
      },
      "output": "formattedStartDate"
    },
    {
      "description": "The end date of the range of holidays to display is one year after the start date",
      "type": "operation",
      "operation": {
        "method": [
          {
            "var": "startDate"
          },
          "addYears",
          [
            1
          ]
        ]
      },
      "output": "endDate"
    },
    {
      "type": "message",
      "message": "Looking up Bank Holidays for the year starting on {formattedStartDate} for {area}"
    },
    {
      "description": "Read all holiday records from the UK government API",
      "type": "action",
      "service": {
        "url": "https://www.gov.uk/bank-holidays.json",
        "method": "GET",
        "headers": {
          "Accept": "application/json"
        }
      },
      "outputs": {
        "body": {
          "englandAndWales": "/england-and-wales/events",
          "scotland": "/scotland/events",
          "northerIreland": "/northern-ireland/events"
        }
      }
    },
    {
      "description": "A separate variable has been populated for each UK area. Populate the chosenArea variable with data from the selected area",
      "type": "operation",
      "output": "chosenArea",
      "operation": {
        "if": [
          {
            "===": [
              {
                "var": "area"
              },
              "England and Wales"
            ]
          },
          {
            "var": "englandAndWales"
          },
          {
            "===": [
              {
                "var": "area"
              },
              "Scotland"
            ]
          },
          {
            "var": "scotland"
          },
          {
            "===": [
              {
                "var": "area"
              },
              "Northern Ireland"
            ]
          },
          {
            "var": "northernIreland"
          },
          {
            "var": "englandAndWales"
          }
        ]
      }
    },
    {
      "description": "Discard any dates outside the required range",
      "type": "operation",
      "operation": {
        "method": [
          {
            "var": "chosenArea"
          },
          "filter",
          [
            {
              "and": [
                {
                  ">=": [
                    {
                      "current": "date"
                    },
                    {
                      "var": "startDate"
                    }
                  ]
                },
                {
                  "<": [
                    {
                      "current": "date"
                    },
                    {
                      "var": "endDate"
                    }
                  ]
                }
              ]
            }
          ]
        ]
      },
      "output": "filteredDates"
    },
    {
      "description": "Count the number of records in the range",
      "type": "operation",
      "operation": {
        "method": [
          {
            "var": "filteredDates"
          },
          "getCount"
        ]
      },
      "output": "numberOfItems"
    },
    {
      "description": "If there is no data in the range, skip to a no data message",
      "type": "decision",
      "rule": {
        "===": [
          {
            "var": "numberOfItems"
          },
          0
        ]
      },
      "passNode": "no data"
    },
    {
      "description": "For each date in the range, build the text to output",
      "type": "repeatDialogue",
      "dialogueId": "Format UK Public Holidays",
      "inputs": {
        "index": {
          "var": "index"
        },
        "filteredDates": {
          "var": "filteredDates"
        },
        "display": {
          "var": "display"
        }
      },
      "outputs": {
        "index": {
          "var": "index"
        },
        "filteredDates": {
          "var": "filteredDates"
        },
        "display": {
          "var": "display"
        }
      },
      "repeatUntil": {
        ">=": [
          {
            "var": "index"
          },
          {
            "var": "numberOfItems"
          }
        ]
      }
    },
    {
      "description": "Output the formatted data for the dates in the range",
      "type": "message",
      "message": "{display}",
      "nextNode": "Dialogue.stop"
    },
    {
      "id": "no data",
      "type": "message",
      "message": "There are no bank holidays for that period"
    }
  ],
  "model": {
    "index": "0",
    "areas": [
      "England and Wales",
      "Scotland",
      "Northern Ireland"
    ]
  }
}        
{      
  "id": "Format UK Public Holidays",
  "description": "Format output for a UK public holiday and append  it to a new-line separated block of text",
  "trigger": {
    "type": "nestedDialogue"
  },
  "nodes": [
    {
      "description": "Using the index loop control variable, get the current holiday record",
      "type": "operation",
      "operation": {
        "method": [
          {
            "var": "filteredDates"
          },
          "getItem",
          [
            {
              "var": "index"
            }
          ]
        ]
      },
      "output": "bankHoliday"
    },
    {
      "description": "Format the date as, for example, Saturday 1st December 2018",
      "type": "operation",
      "operation": {
        "Date.format": [
          {
            "var": "bankHoliday/date"
          },
          "dddd do MMMM yyyy"
        ]
      },
      "output": "formattedDate"
    },
    {
      "description": "Construct a line of output and append to the text block",
      "type": "operation",
      "operation": {
        "cat": [
          {
            "var": "display"
          },
          {
            "var": "bankHoliday/title"
          },
          " - ",
          {
            "var": "formattedDate"
          },
          "\n\r"
        ]
      },
      "output": "display"
    },
    {
      "description": "Increment the loop control variable",
      "type": "operation",
      "output": "index",
      "operation": {
        "+": [
          {
            "var": "index"
          },
          1
        ]
      }
    }
  ]
}

Admin and Studio

If you have been invited to talksuite as an administrator, you can invite other people to use talksuite for your organisation(s). As an administrator, you have access to the Admin area. If you have been given an author role, you will also have access to the studio. You can switch between the two areas.

The admin area looks like this:

Click on the Studio link to go to the studio.

The studio looks like this:

Click on the Admin link to go to the admin area

Invite people

Once you have selected an organisation in the admin area, you can then invite others to join you.

There is an Invite link in the header of the pending invites list. Click on this link to create an invitation.

You will need to specify an email address for the person you want to invite. This must be unique within the organisation

You must also select the person’s roles. One or both of these roles must be selected:

  • An Administrator to allow the invitee to invite people themselves

  • An Author who can create bots, projects, dialogues and languages

When you save the form, a link will be displayed. Email this link to the invitee. They will then need to sign in or sign up to use talksuite. The link will expire in 72 hours.

If you have invited the wrong person by mistake, you can revoke the invite. The record will disappear from the pending invites list and if the invitee has not already signed up, the link will no longer work.

Managing Members

Once someone has accepted an invitation and signed up (or in) to talksuite, they will be removed from the invite list and will appear on the members list in the organisation screen within the admin area. The user will supply their name as part of the sign-up process.

Click on the manage link to amend a member’s details. You can change their name and roles and remove them from the organisation, which will prevent them using the studio for that organisation.

Usage Statistics

Your organisation page will contain a measure of the amount of activity by bot users within the organisation. An activity count is given for each calendar month. This includes

  • All messages sent by the bots in the organisation to users. If typing indicators are in use, this will count as a separate activity

  • All messages typed by users and sent to a bot in the organisation

  • Any activity when the client app reconnects, which will not be visible to the app user

Audit Log

A log of invites and changes to the membership list for your organisation can be exported to a file. This is downloaded as a CSV file. There is no date filter, so all activity will be output. The output will look something like this

Who What Detail When
michael@jumpcrash.co.uk Invited dan@jumpcrash.co.uk to be an author 2019-01-11 09:00
michael@jumpcrash.co.uk Amended Member dan@jumpcrash.co.uk: Daniel Smith to Danny Smith 2019-01-11 11:00

Super Administrators

The super administrator looks after a whole deployment. Their primary role is to create organisations and invite an initial Administrator to manage their own organisation.

A super administrator has the same access as an organisation administrators to an organisation. That is :

In addition they can:

  • Create organisations

  • Rename, suspend, re-active and remove organisations

  • See the super admin activity log

Organisation Management

To create an organisation, select the “Add new organisation” option from the top of the left hand organisation menu in the admin area.

Then enter the organisation name

Organisation name must be unique

An organisation can be active (in which case authors and bot users have access to it) or suspended (in which case nobody can use it)

Once an organisation is created, the action menu on the right can be used to change the name of the organisation, suspend and activate it and delete it.

If you delete an organisation, all the members, projects, languages and dialogues in the organisation will be removed

Other Super Admins

As a super admin, it is a good idea to create at least one additional super admin, so that organisations can be created and managed in your absence.

The super admin menu item at the bottom of the left hand organisation menu takes you to the super admin users management screen.

There is an Invite link in the header of the pending invites list. Click on this link to create an invitation for additional super admins.

You will need to specify an email address for the person you want to invite. This must be unique within the organisation

When you save the form, a link will be displayed. Email this link to the invitee. They will then need to sign in or sign up to use talksuite.

If you have inivited the wrong person by mistake, you can revoke the invite. The record will disappear from the pending invites list and if the invitee has no already signed up, the link will not longer work. The link will expire in 72 hours.

Once the new super admin has signed up, they will disappear from the invites list and appear on the members list. You can change the name of a member, suspend and re-activate then or remove them.

You can’t remove or deactivate your own super admin record

Audit Log

A log of super admin invites and changes to the super admin membership list can be exported to a file.

The Author's Channel

talksuite comes with an application that enables you to test your bots without having to use a third party messaging channel.The talksuite Author’s channel can run as a web application, a desktop application or can be embedded within your own applications.

To sign-in to the channel, enter the discovery URL, for you bot

Bespoke Cards

In addition to the usual channel features, the client supports bespoke cards.

Bespoke cards are only available if you are using the talksuite author’s channel or the people first App

As well as the standard Hero card, the following cards are available:

The Bespoke cards are displayed by specifying the card content in a customContent property. Within this property, the cardType specifies the type of card and the content property contains the card data.

Custom content can be added to the following node types:

If the card needs buttons, there are three types of button available:

Type Description
postBack The dialogue will not wait for a button press. When the button is pressed, the value property of the button is output on the channel (as if the user had typed it)
imBack The dialogue will wait for a button press. The value property of the button selected will be returned in the output property of the node
openUrl The dialogue will wait for a button press. The web site given by the button value property will be displayed

In order to catch and act on the return value from a postBack button, you will need to have a dialogue which is triggered on the button value property. For carousels of cards, you may need to include details of which card has been selected. As the button value will contain variable data, you will need to use an intent or pattern trigger to initiate the dialogue

If you are developing your own directline client, you can create your own bespoke card types.

Examples of each node type are shown on the right

{
  "type": "message",
  "message": "Message to display if the card is not supported in the channel",
  "customContent": {
    "contentType": "insert card type here",
    "content": {
      "insertBespokePropertyNamesHere": "insert card content here"
    }
  }
}  
{
  "type": "stringPrompt",
  "message": "Message to display if the card is not supported in the channel",
  "retryMessage": "Message to display on invalid input",
  "customContent": {
    "contentType": "card Type",
    "content": {
      "insertBespokePropertyNamesHere": "insert card content here",
      "buttons": [
        {
          "type": "postBack",
          "title": "Button label",
          "value": "message text to place in the channel"
        },
        {
          "type": "imBack",
          "title": "Button label",
          "value": "Value to return as output"
        },
        {
          "type": "openUrl",
          "title": "Button label",
          "value": "url"
        }
      ]
    }
  },
  "output": "buttonSelection"
}  
{
  "type": "customCardCollection",
  "listName": "listVariableName",
  "contentItem": "individualRecordName",
  "customContent": {
    "contentType": "card Type",
    "content": {
      "insertBespokePropertyNamesHere": "insert card content here"
    }
  }
}     

Hero Card

Display a card

Properties
contentType

application/vnd.peoplefirst.card.hero

content

Card content

content/title

Card title

content/subtitle

Card subtitle

content/text

Card text

content/images

Card image section

content/images/url

Card image

content/buttons

List of buttons

buttons/type

Type of button

buttons/title

Button label

buttons/value

Value to be returned when the button is pressed

Custom cards are only available using the talksuite or MHR apps.

The hero card is a generic card

Use in a message node to display a single card with no buttons

Use in a string prompt node to display a single card with buttons

Use in a custom card collection node to display a carousel of cards

{
  "type": "message",
  "message": "Not available",
  "customContent": {
    "contentType": "application/vnd.microsoft.card.hero",
    "content": {
      "title": "Title",
      "subtitle": "Subtitle",
      "text": "Text",
      "images": [
        {
          "url": "https://media.giphy.com/media/3owzWmHgH4Mbh0Omre/giphy.gif"
        }
      ]
    }
  }
}    

Person Card

Display personal details on a card

Properties
contentType

application/vnd.peoplefirst.card.person

content

A list of people

content/name

Person’s name

content/email

Person’s email

content/image

Person’s image

content/event

Additional text

content/reminders

A list of reminders

content/reminders/subject

Reminder details

content/buttons

List of buttons

buttons/type

Type of button

buttons/title

Button label

buttons/value

Value to be returned when the button is pressed

Custom cards are only available using the talksuite or MHR apps.

The person card displays personal details and a list of reminders.

Use in a message node to display a single card with no buttons

Use in a string prompt node to display a single card with buttons

Use in a custom card collection node to display a carousel of cards

{
  "type": "message",
   "message": "This card is not available on this channel",
   "customContent": {
     "contentType": "application/vnd.peoplefirst.card.person",
      "content": {
        "name": "John Smith",
        "email": "john.smith@ruddington-software.com",
        "image": "https://media.giphy.com/media/3owzWmHgH4Mbh0Omre/giphy.gif",
        "event": "Next event with John is tomorrow"
       }
    }
} 
{
  "id": "advanced person card",
  "trigger": {
    "type": "message",
    "values": [
      "person card"
    ]
  },
  "nodes": [
    {
      "type": "operation",
      "operation": {
        "method": [
          {
            "var": "reminders"
          },
          "addItem",
          [
            {
              "var": "reminderA"
            }
          ]
        ]
      },
      "output": "reminders"
    },
    {
      "type": "operation",
      "operation": {
        "method": [
          {
            "var": "reminders"
          },
          "addItem",
          [
            {
              "var": "reminderB"
            }
          ]
        ]
      },
      "output": "reminders"
    },
    {
      "type": "operation",
      "operation": {
        "var": "reminders"
      },
      "output": "personA/reminders"
    },
    {
      "type": "operation",
      "operation": {
        "method": [
          {
            "var": "personCollection"
          },
          "addItem",
          [
            {
              "var": "personA"
            }
          ]
        ]
      },
      "output": "personCollection"
    },
    {
      "type": "operation",
      "operation": {
        "method": [
          {
            "var": "personCollection"
          },
          "addItem",
          [
            {
              "var": "personB"
            }
          ]
        ]
      },
      "output": "personCollection"
    },
    {
      "id": "displayEventCard",
      "type": "customCardCollection",
      "listName": "personCollection",
      "contentItem": "person",
      "customContent": {
        "contentType": "application/vnd.peoplefirst.card.person",
        "content": {
          "name": "{person/name}",
          "email": "{person/email}",
          "image": "{person/image}",
          "event": "Next event with {person/name} is {person/dueDate}",               
          "reminders": "{person/reminders}",
          "buttons": [
            {
              "type": "imBack",
              "title": "Clear reminders",
              "value": "clear reminders"
            }
          ]
        }
      },
      "nextNode": null
    }
  ],
  "model": {
    "personA/name": "John Smith",
    "personA/email": "john.smith@ruddinston-software.com",
    "personA/image": "https://media.giphy.com/media/3owzWmHgH4Mbh0Omre/giphy.gif",
    "personA/dueDate": "1/1/2020",
    "reminderA/subject/title": "Approve John's expenses",
    "personB/name": "Jane Williams",
    "personB/email": "j.williams@ruddinston-software.com",
    "personB/image": "https://media.giphy.com/media/3owzWmHgH4Mbh0Omre/giphy.gif",
    "personB/dueDate": "2/2/2020",
    "reminderB/subject/title": "Arrange a meeting with John"
  }
}

Email Card

Display details of an email on a card

Properties
contentType

application/vnd.peoplefirst.message.email

content

Card contents

content/person

Personal details

content/person/name

Person’s name

content/person/email

Person’s email

content/person/imageUrl

Person’s image

content/emailContent

Properties containing details of the email

content/emailContent/id

email Id

content/emailContent/subject

Email subject

content/emailContent/body

Email body

content/buttons

List of buttons

content/buttons/type

Type of button

content/buttons/title

Button label

content/buttons/value

Value to be returned when the button is pressed

Custom cards are only available using the talksuite or MHR apps.

The email card displays personal details plus the email subject and body

Use in a message node to display a single card with no buttons

Use in a string prompt node to display a single card with buttons

Use in a custom card collection node to display a carousel of cards

{    
  "type": "message",
  "customContent": {
    "contentType": "application/vnd.peoplefirst.card.message.email",
    "content": {
      "person": {
        "name": "John Smith",
        "email": "j.smith@jumpcrash.co.uk",
        "imageUrl": "https://media.giphy.com/media/3owzWmHgH4Mbh0Omre/giphy.gif"
      },
      "emailContent": {
        "id": "123",
        "subject": "This is the email subject",
        "body": "This is the email body"
      },
      "buttons": [
        {
          "type": "postBack",
          "title": "Reply",
          "value": "replying"
        }
      ]
    }
  },
  "message": "This is a custom email card that is not supported on this channel."
}

Clock-in Card

Display details of clock-in and out times

Properties
contentType

application/vnd.peoplefirst.card.timeandattendance.clockIn

content

Card contents

content/title

Card title

clockins

List of clock-in and out times

clockins/id

Record id

content/clockins/startDate

Date and time of the clock-in

content/clockins/startTimeZone

Time zone of the start date-time

content/clockins/startAutomatic

true if the clock-in time was calculated, false if the user manually entered a time

content/clockins/endDate

Date and time of the clock-out

content/clockins/endTimeZone

Time zone of the end date-time

content/clockins/endAutomatic

true if the clock-out time was calculated, false if the user manually entered a time

content/buttons

List of buttons

content/buttons/type

Type of button

content/buttons/title

Button label

content/buttons/value

Value to be returned when the button is pressed

Custom cards are only available using the talksuite or MHR apps.

The clock-in card displays one or more clock-in/out time pairs.

If the automatic flag is set to false, “manual” will be displayed against the time.

If the time zone differs from the device time zone, the time zone of the clock in or out time will be displayed.

Use in a message node to display a single card with no buttons

Use in a string prompt node to display a single card with buttons

Use in a custom card collection node to display a carousel of cards

{      
  "type": "message",
  "message": "{unableToDisplayCardMessage}",
  "customContent": {
    "contentType": "application/vnd.peoplefirst.card.timeandattendance.clockIn",
    "content": {
      "title": "{title}",
      "clockIns": [
        {
          "id": "{timePair/id}",
          "startDate": "{timePair/startDateTime}",
          "startTimeZone": "{timePair/startTimeZone}",
          "startAutomatic": "{startAutomatic}",
          "endDate": "{timePair/endDateTime}",
          "endTimeZone": "{timePair/endTimeZone}",
          "endAutomatic": "{endAutomatic}"
        }
      ],
      "buttons": "{buttons}"
    }
  }
}      

Clock-in Approval Card

Display details of people and clock-in and out times

Properties
contentType

application/vnd.peoplefirst.card.timeandattendance.clockInApproval

content

Card contents

content/title

Card title

content/person

Person details

content/person/name

Person name

content/person/imageUrl

Person photo

clockins

List of clock-in and out times

clockins/id

Record id

content/clockins/startDate

Date and time of the clock-in

content/clockins/startTimeZone

Time zone of the start date-time

content/clockins/startAutomatic

true if the clock-in time was calculated, false if the user manually entered a time

content/clockins/endDate

Date and time of the clock-out

content/clockins/endTimeZone

Time zone of the end date-time

content/clockins/endAutomatic

true if the clock-out time was calculated, false if the user manually entered a time

content/buttons

List of buttons

content/buttons/type

Type of button

content/buttons/title

Button label

content/buttons/value

Value to be returned when the button is pressed

Custom cards are only available using the talksuite or MHR apps.

The clock-in approval card differs from the clock-in card in that it displays details of the person. This allows a carousel of cards from mutlple people to be displayed, allowing the manager to make multiple approval/rejection decisions

The data can be set-up so that a separate card is displayed for each person and for each day for a person.

If the automatic flag is set to false, “manual” will be displayed against the time.

If the time zone differs from the device time zone, the time zone of the clock in or out time will be displayed.

Use in a message node to display a single card with no buttons

Use in a string prompt node to display a single card with buttons

Use in a custom card collection node to display a carousel of cards

{
  "type": "customCardCollection",
  "listName": "aPerPersonPerDayList",
  "contentItem": "timePairApprovalItem",
  "customContent": {
    "contentType": "application/vnd.peoplefirst.card.timeandattendance.clockInApproval",
    "content": {
      "person": {
        "name": "{timePairApprovalItem/name}{overtimeDescription}",
        "imageUrl": "{timePairApprovalItem/photo}"
      },
      "clockIns": "{timePairApprovalItem/clockList}",
      "title": "{language/pfTimeAwaitingApprovalCardTitle}",
      "buttons": [
        {
          "type": "postBack",
          "title": "Approve",
          "value": "Approve decision [{timePairApprovalItem/personId}] [{timePairApprovalItem/formattedStartDate}] [{timePairType}]"
        },
        {
          "type": "postBack",
          "title": "Reject",
          "value": "Reject decision [{timePairApprovalItem/personId}] [{timePairApprovalItem/formattedStartDate}] [{timePairType}]"
        }
      ]
    }
  }
}               

Date Period Card 🚧

Display details of a date or date period

Properties
contentType

application/vnd.talksuite.card.dateperiod

content

Card contents

content/title

Card title

content/subtitle

Card subtitle

content/person

Person details

content/person/name

Person name

content/person/thumbnailUrl

Person photo

datePeriod

Date details

datePeriod/start

Start date details

datePeriod/start/date

Start date or date-time

datePeriod/start/caption

Caption for start date

datePeriod/end

Start date details

datePeriod/end/date

Start date or date-time

datePeriod/end/caption

Caption for start date

content/buttons

List of buttons

content/buttons/type

Type of button

content/buttons/title

Button label

content/buttons/value

Value to be returned when the button is pressed

This card will be implemented for the people first App first. The talksuite authors’ channel version will follow later

This card can be used to display details of time-related events. If the event is for a single date, then omit the end section.Therefore this can be used to display details of a single event or a period. The caption allows a date to be annotated (e.g. to show a location for the event)

{
  "type": "message",
  "message": "Not available in this channel",
  "customContent": {
    "contentType": "application/vnd.talksuite.card.dateperiod",
    "content": {
      "title": "1 day off",
      "subtitle": "Leave",
      "person": {
        "name": "Akeem Larkin",
        "ImageUrl": "https://media.giphy.com/media/3owzWmHgH4Mbh0Omre/giphy.gif"
      },
      "datePeriod": {
        "start": {
          "date": "2019-01-01T00:00:00Z",
          "caption": "2:30 hours"
        },
        "end": {
          "date": "2019-02-01T00:00:00Z",
          "caption": "4:30 hours"
        }
      },
      "buttons": [
        {
          "type": "openUrl",
          "title": "Edit absence",
          "value": "http://bbc.co.uk/news"
        }
      ]
    }
  }
}

Quick Chat

talksuite comes with an application that enables you to test your bots without having to use a third party messaging channel.The talksuite Author’s channel can run as a web application, a desktop application or can be embedded within your own applications.

The People First App

The People First App is used to provide chat bot functionality for MHR’s People First and iTrent HR systems.

Talksuite is used to power the back-end of these chat bots. The People First app can be downloaded from the Apple app store and Google play. You can configure the app to work with your own talksuite dialogues, by providing details of one or your bots.

First, generate a QR code from the discovery URL for your bot.

Run the app, select Connect and scan the QR code. You should now be connected to your bot

Inbound events

The People First App sends a number of events to talksuite. Most of these are specifc to MHR’s HR applications. However a couple may be of general use to talksuite authors. See the CustomEvent node.

Outbound events

Talksuite can send events to the people first app using the Custom Event node.

Data Protection Requirements

If you are using talksuite to hold personal data, it’s a good practice to explain to users what data you hold and why, and to have procedures in place that allow users to ensure there data in up to date and only held for a valid reason

Some regions will have legisltative requirements in this area (GDPR for the EU)

The general areas that you need to consider are:

  • Explain to the user what types of personal data are held and why

  • Allow the user to see what their personal data is

  • Allow the user to delete any non-essential personal data

  • Have a retention policy in place to remove any old data that is no longer required.

Implementating Data Protection

Managing Conversation Data

talksuite uses conversation variables to permanently store personal information, and this section concentrates on how to write dialogues to implmement data protection requirements for conversation data. If you are storing data in spreadsheets or using APIs in systems that do not have their own data protection features, you will need to cover this data as well

Firstly, design the data to make its management easy:

  • Divide personal data into related groups (e.g data required for payroll and data required for diversity analysis). A user may want to remove their diversity data, but their payroll data cannot practically be removed. Keeping data held for different reasons separately will allow you to delete non-essential data while allowing an employee to continue to use essential features.

  • Avoid storing data in simple conversation variables. Hold data in lists of objects (even if there is only one item in the list). This allows data to be explicitly deleted (using the removeitem function), rather than justed blanked out.

  • Store the date of change against each personal data object. This allows you to show the user how current the data is and to implement a retention policy, where data over a certain age can be removed. This also gives you the option of creating a new record every time a change is made.

Next, write dialogues that allow the user to manage their own data. This will include dialogues to:

  • Show the users their own data

  • Allow the user (where appropriate) to keep the data up to date

  • Allow the user to remove any non-essential personal data

  • Allow the user to delete the conversation

Administrator Access

If you deal with requests for data disclosure or removal from users who no longer have access to the system, you will need to build administrative functions outside of talskuite to allow administrators to access or remove data. Use the External Event functionality to trigger a dialogue for a user. These dialogues can then perform the same access and removal functions as described above

Privacy Policies

The MHR custom app contains a privacy policy. The app will send an event "requestCurrentDataPrivacyPolicy to get the policy. The app expects an object with a property of text to be returned.

Introduction to the APIs

All talksuite functions are available via APIs.

List of APIs

The APIs controlling administration are:

API Function
superadmins Manage superadmins
organisations Create and manage organisations
invites Create and manage invitations to join talksuite
members Manage members of an organisation
usage Get message activity statistics
audit Get details of organisation and user changes
profilei Get organisations to which you have access

The APIs controlling the studio are:

API Function
bots Create and manage bots
projects Create and manage projects
dialogues Create and manage dialogues
language Create and manage language stores
search Search across bots, language and dialogues
schema Obtain the dialogue JSON schema
snippets List of code snippets

The APIs used by the engine are:

API Function
discovery Connect to a bot
webhook Initiate a dialogue in a conversation

URL structure

The URL structure is:

https://deployment/api/apiname

Headers

All APIs except discovery must have an authorization header containing “Bearer token”.

The studio APIs must have an organisation-id header containing the organisation ID.


Audit

Available to super admins and admins. Admins can only access their own organisations (list obtained via the whoami API)

GET. Use /audit to get the audit activity in CSV format

Super Admins

GET. Use /superadmins to get all superadmins

GET. Use /superadmins/{superadminId} to get a superadmin

PUT. Use /superadmins/{superadminId} to update a superadmin

DELETE. Use /superadmins/{superadminId} to remove a superadmin

{
  "data": {
      "attributes" : {               
         "name" : "Alan Jones"
      }
  }   
}

Organisations

Organisation details

POST, PUT and DELETE to super admins only.

GET. Use /organisations to get all organisations

POST. Use /organisations to create an organistion

GET. Use /organisatons{orgId} to get an individual organisation

PUT. Use /organisations{orgId} to update an organisation

DELETE. Use /organisations{OrgId} to delete an organisation

Initiate a dialogue

POST. Use /organisations/{OrgId}/bots/{botId}/dialogue to initiate a dialogue in a bot. AWS authorisation is required. See External events

Discovery URL

This API does not require authentication. Used by the People First native app to connect to a bot

GET. Use /organisations/{orgId}bots/{botId}/discover to obtain bot discovery information

{
  "data": {
    "active" : true,
    "name" : "Example organisation" 
}

Invites

Invites to organisations

Available to super admins and admins. Admins can only access their own organisations (list obtained via the whoami API)

GET. Use /organisations/{orgId}/invites to get all pending invites for an organisation

POST. Use /organisations/{orgId}/invites to create an invite

DELETE. Use /organisations/{orgId}/invites/{inviteId} to revoke an invite

Invites to super admins

Available to super admins only

GET. Use /invites to get all pending invites for super admins

POST. Use /invites to create an invite for a super admin

POST. Use /invites/{inviteId} to revoke a super admin invite

{
  "data": {
      "attributes" : {
         "admin" : true,
         "author" : true,
         "email" : "andy.mcford@mhr.co.uk"
      }
  }   
}
{
  "data": [
     {
       "attributes": {
       "admin": true,
        "author": true,
        "email": "andy.mcford@mhr.co.uk"
     },
     "links": {
        "self": "invitation url will be shown here"
     }
    }
  ]
}

Members

Available to super admins and admins. Admins can only access their own organisations (list obtained via the whoami API)

GET. Use /{orgId}/members to get all members of an organisation

GET. Use /{orgId}/members/{memberId} to get a member of an organisation

PUT. Use /{orgId}/members/{memberId} to update a member

DELETE. Use /{orgId}/members/{memberId} to remove a member

{
  "data": {
      "attributes" : {
         "admin" : true,
         "author" : true,
         "name" : "Alan Jones"
      }
  }   
}

Usage

Available to super admins and admins. Admins can only access their own organisations (list obtained via the whoami API)

POST. Use /usage to get statistics on the number of messages used each calendar month

The post consists of one or more UTC date ranges. The message is returned for each of the ranges

{
  "dateTimeRanges": [
    {
      "start": "2019-01-01T00:00:00.000Z",
      "end": "2019-02-01T00:00:00.000Z"
    }
  ]
}

Profile

GET. Use /profile to get a list of the organisations to which the user has access

Bots

Available to authors only. Authors can only access their own organisations (list obtained via the whoami API)

See Bot config for details of the body of the API

GET. Use /bots to get all bots

POST. Use /bots to create a bot

GET. Use /bots{botId} to get an individual bot

PUT. Use /bots{botId} to update a bot

DELETE. Use /bots{botId} to delete a bot

GET. Use /bots/search&q=criteria to search for bots with contents matching criteria

{
  "name": "hmactest",
  "address": "john.smith",
  "value": {
          "message":  "Hello John"
  }
}    

Projects

Available to authors only. Authors can only access their own organisations (list obtained via the whoami API)

GET. Use /projects to get all projects

POST. Use /projects to create a project

GET. Use /projects{id} to get an individual project

PUT. Use /projects{id} to update a project

DELETE. Use /projects{id} to delete a project

GET. Use /projects/{id}/dialogues to get all dialogues for a project

GET. Use /projects/{id}/language to get all language records for a project

{
  "data": {
    "name": "My new project"
  }
}

Dialogues

See the Dialogue Schema for the full definition of the json section of the body

Available to authors only. Authors can only access their own organisations (list obtained via the whoami API)

GET. Use /dialogues to get all dialogues

POST. Use /dialogues to create a dialogue

GET. Use /dialogues{dialogueId} to get an individual dialogue

PUT. Use /dialogues{dialogueId} to update a dialogue

DELETE. Use /dialogues{dialogueId} to delete a dialogue

{
  "data": {
    "projectId" : "ad325aee-4b69-4cef-ab8d-c1dcdb37ffca",
    "attributes": {
      "json": {
        "id": "Example Date Input",
        "trigger": {
          "type": "message",
          "values": [
            "example date input"
          ]
        },
        "nodes": [
          {
            "type": "datePrompt",
            "message": "When is your next birthday",
            "retryMessage": "That's not a valid date. Plese try again",
            "output": "birthday"
          },
          {
            "type": "message",
            "message": "OK. I've made a note of that. Next birthday on {birthday}"
          }
        ]
      }
    }
  }
}

Language

Available to authors only. Authors can only access their own organisations (list obtained via the whoami API)

GET. Use /language to get all language record

POST. Use /language to create a language record

GET. Use /language{id} to get an individual language record

PUT. Use /language{id} to update a language record

DELETE. Use /language{id} to delete a language record

{
  "data": {
    "projectId": "ad325aee-4b69-4cef-ab8d-c1dcdb37ffca",
    "attributes": {
      "systemTexts": {
        "notificationMessage": [
          "New message"
        ]
      },
      "texts": [
        {
          "name": "helloMessage",
          "values": [
            "hello",
            "hi"
          ]
        }
      ],
      "region": "en-GB"
    }
  }
}      

Search

Available to authors only. Authors can only access their own organisations (list obtained via the whoami API)

Dialogues and Projects

GET. Use /search/aggregated?q=criteria to get all dialogues and language records matching the criteria

Bots

GET. Use /search/bots?q=criteria to get all bots records matching the criteria

Schema

GET. Use /schema to get the json schema used to validate dialogues

Snippets

GET. Use /schema?context=dialogue to get a list of the node snippets

Discovery

This API does not require authentication. Used by the People First native app to connect to a bot

GET. Use /organisations/{orgId}bots/{botId}/discover to obtain bot discovery information

Web hook

POST. Use /organisations/{OrgId}/bots/{botId}/dialogue to initiate a dialogue in a bot. AWS authorisation is required. See External events

Dialogue Schema

JSON Schema used to validate dialogue syntax

{
  "$schema": "http://json-schema.org/draft-04/schema#",
  "title": "talksuite Dialogue Schema",
  "description": "This is a schema that defines the format for bot dialogue templates",
  "type": "object",
  "properties": {
    "trigger": {
      "oneOf": [
        {
          "$ref": "#/definitions/triggers/attachmentTrigger"
        },
        {
          "$ref": "#/definitions/triggers/customEventTrigger"
        },
        {
          "$ref": "#/definitions/triggers/dialogueTrigger"
        },
        {
          "$ref": "#/definitions/triggers/eventTrigger"
        },
        {
          "$ref": "#/definitions/triggers/intentTrigger"
        },
        {
          "$ref": "#/definitions/triggers/messageTrigger"
        },
        {
          "$ref": "#/definitions/triggers/patternTrigger"
        },
        {
          "$ref": "#/definitions/triggers/processTrigger"
        }
      ]
    },
    "nodes": {
      "type": "array",
      "minItems": 1,
      "items": {
        "anyOf": [
          {
            "$ref": "#/definitions/nodes/actionNode"
          },
          {
            "$ref": "#/definitions/nodes/attachmentPromptNode"
          },
          {
            "$ref": "#/definitions/nodes/cardCollectionNode"
          },
          {
            "$ref": "#/definitions/nodes/cardNode"
          },
          {
            "$ref": "#/definitions/nodes/choicePromptNode"
          },
          {
            "$ref": "#/definitions/nodes/confirmationPromptNode"
          },
          {
            "$ref": "#/definitions/nodes/customCardCollectionNode"
          },
          {
            "$ref": "#/definitions/nodes/customEventNode"
          },
          {
            "$ref": "#/definitions/nodes/datePromptNode"
          },
          {
            "$ref": "#/definitions/nodes/decisionNode"
          },
          {
            "$ref": "#/definitions/nodes/dialogueNode"
          },
          {
            "$ref": "#/definitions/nodes/downloadActionNode"
          },
          {
            "$ref": "#/definitions/nodes/nlpEntitiesNode"
          },
          {
            "$ref": "#/definitions/nodes/eventNode"
          },
          {
            "$ref": "#/definitions/nodes/messageNode"
          },
          {
            "$ref": "#/definitions/nodes/operationNode"
          },
          {
            "$ref": "#/definitions/nodes/repeatDialogueNode"
          },
          {
            "$ref": "#/definitions/nodes/sequenceDialogueNode"
          },
          {
            "$ref": "#/definitions/nodes/simplePromptNode"
          },
          {
            "$ref": "#/definitions/nodes/buildObjectNode"
          }
        ]
      }
    },
    "model": {
      "$ref": "#/definitions/patterns/propertiesObject"
    },
    "entities": {
      "$ref": "#/definitions/patterns/propertiesObject"
    },
    "id": {
      "$ref": "#/definitions/dialogueId"
    },
    "priority": {
      "type": "string",
      "enum": [ "none", "interrupt", "background", "supersede" ]
    },
    "description": {
      "type": "string"
    }
  },
  "additionalProperties": false,
  "required": [
    "trigger",
    "nodes",
    "id"
  ],
  "definitions": {
    "cardContent": {
      "type": "object",
      "properties": {
        "url": {
          "type": "string"
        },
        "title": {
          "$ref": "#/definitions/outputShort"
        },
        "subtitle": {
          "$ref": "#/definitions/outputShort"
        },
        "text": {
          "$ref": "#/definitions/outputLong"
        },
        "image": {
          "$ref": "#/definitions/outputLong"
        },
        "isThumbnail": {
          "$ref": "#/definitions/typeBoolean"
        },
        "retryMessage": {
          "$ref": "#/definitions/outputShort"
        },
        "tapOptions": {
          "type": "object",
          "properties": {
            "displayName": {
              "$ref": "#/definitions/patterns/propertyReference"
            },
            "output": {
              "$ref": "#/definitions/patterns/propertyReference"
            },
            "value": {
              "$ref": "#/definitions/outputShort"
            }
          },
          "additionalProperties": false,
          "required": [
            "displayName",
            "output",
            "value"
          ]
        },
        "buttonOptions": {
          "type": "object",
          "properties": {
            "listName": {
              "$ref": "#/definitions/patterns/propertyReference"
            },
            "displayName": {
              "$ref": "#/definitions/patterns/propertyReference"
            },
            "output": {
              "$ref": "#/definitions/patterns/propertyReference"
            }
          },
          "additionalProperties": false,
          "required": [
            "listName"
          ]
        }
      },
      "anyOf": [
        { "required": [ "title" ] },
        { "required": [ "subtitle" ] },
        { "required": [ "text" ] },
        { "required": [ "image" ] },
        { "required": [ "tapOptions" ] },
        { "required": [ "buttonOptions" ] }
      ],
      "additionalProperties": false
    },
    "contextDataProperty": {
      "anyOf": [
        {
          "$ref": "#/definitions/patterns/propertyReference"
        },
        {
          "type": "string",
          "pattern": "^(?i)\\/*Bot\\/[a-zA-Z0-9]{1,128}$"
        },
        {
          "type": "string",
          "pattern": "^(?i)\\/*Dialogue\\/LastApiStatusCode$"
        },
        {
          "type": "string",
          "pattern": "^(?i)\\/*Dialogue\\/triggerUtterance"
        },
        {
          "type": "string",
          "pattern": "^(?i)\\/*Language\\/[a-zA-Z0-9_]{1,128}$"
        },
        {
          "type": "string",
          "pattern": "^(?i)\\/*User\\/[a-zA-Z0-9_]{1,128}$"
        }
      ]
    },
    "dialogueId": {
      "type": "string",
      "minLength": 1
    },
    "json": {
      "type": "object"
    },
    "mimeTypes": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "minItems": 1,
      "uniqueItems": true
    },
    "outputShort": {
      "type": "string",
      "minLength": 1,
      "maxLength": 320
    },
    "outputLong": {
      "type": "string",
      "minLength": 1
    },
    "languageReference": {
      "type": "object",
      "properties": {
        "var": {
          "type": "string",
          "pattern": "^(?i)\\/*Language\\/[a-zA-Z0-9_]{1,128}$"
        }
      },
      "additionalProperties": false,
      "required": [
        "var"
      ]
    },
    "nodeCustomContent": {
      "type": "object",
      "properties": {
        "contentType": {
          "type": "string"
        },
        "content": {
          "$ref": "#/definitions/json"
        }
      },
      "additionalProperties": false,
      "required": [
        "contentType",
        "content"
      ]
    },
    "patterns": {
      "alphanumeric": {
        "type": "string",
        "pattern": "^[a-zA-Z0-9]{1,128}$"
      },
      "dialogueNodeInputPropertiesObject": {
        "type": "object",
        "patternProperties": {
          "^[a-zA-Z0-9]{1,128}$": {
            "$ref": "#/definitions/jsonLogic"
          }
        },
        "additionalProperties": false
      },
      "dialogueNodeOutputPropertiesObject": {
        "type": "object",
        "patternProperties": {
          "(?!(?i)\\/*(bot\\/+|dialogue\\/+|language\\/+|user\\/+))^.*$": {
            "$ref": "#/definitions/logicData"
          }
        },
        "additionalProperties": false
      },
      "propertyReference": {
        "type": "string",
        "pattern": "(?!(?i)\\/*(bot\\/+|dialogue\\/+|language\\/+|user\\/+))^.*$"
      },
      "propertyReferenceBlob": {
        "type": "string",
        "pattern": "^(?i)(\\/*conversation\\/+)?[^\\/]+$"
      },
      "propertiesObject": {
        "type": "object",
        "patternProperties": {
          "(?!(?i)\\/*(bot\\/+|dialogue\\/+|language\\/+|user\\/+))^.*$": {
            "$ref": "#/definitions/propertiesObjectValue"
          }
        },
        "additionalProperties": false
      }
    },
    "propertiesObjectValue": {
      "oneOf": [
        {
          "type": "string"
        },
        {
          "type": "array",
          "items": {
            "type": "string"
          },
          "minItems": 1
        }
      ]
    },
    "triggers": {
      "attachmentTrigger": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [ "attachment" ]
          },
          "contentTypes": {
            "$ref": "#/definitions/mimeTypes"
          },
          "output": {
            "$ref": "#/definitions/patterns/propertyReferenceBlob"
          }
        },
        "additionalProperties": false,
        "required": [
          "type",
          "contentTypes",
          "output"
        ]
      },
      "customEventTrigger": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [ "customEvent" ]
          },
          "name": {
            "type": "string"
          },
          "output": {
            "$ref": "#/definitions/patterns/propertyReference"
          }
        },
        "additionalProperties": false,
        "required": [
          "type",
          "name"
        ]
      },
      "dialogueTrigger": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [ "nestedDialogue" ]
          }
        },
        "additionalProperties": false,
        "required": [
          "type"
        ]
      },
      "eventTrigger": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [ "event" ]
          },
          "event": {
            "type": "string",
            "enum": [
              "conversationStart",
              "noTriggerMatch"
            ]
          },
          "output": {
            "$ref": "#/definitions/patterns/propertyReference"
          }
        },
        "additionalProperties": false,
        "required": [
          "type",
          "event"
        ]
      },
      "intentTrigger": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [ "intent" ]
          },
          "intent": {
            "type": "string"
          },
          "context": {
            "type": "string",
            "enum": [ "earliest", "latest" ]
          },
          "output": {
            "$ref": "#/definitions/patterns/propertyReference"
          }
        },
        "additionalProperties": false,
        "required": [
          "type",
          "intent"
        ]
      },
      "messageTrigger": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [ "message" ]
          },
          "values": {
            "type": "array",
            "items": {
              "anyOf": [
                {
                  "$ref": "#/definitions/outputShort"
                },
                {
                  "$ref": "#/definitions/languageReference"
                }
              ]
            }
          }
        },
        "additionalProperties": false,
        "required": [
          "type",
          "values"
        ]
      },
      "patternTrigger": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [ "pattern" ]
          },
          "values": {
            "type": "array",
            "items": {
              "anyOf": [
                {
                  "$ref": "#/definitions/outputShort"
                },
                {
                  "$ref": "#/definitions/languageReference"
                }
              ]
            }
          }
        },
        "additionalProperties": false,
        "required": [
          "type",
          "values"
        ]
      },
      "processTrigger": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [ "process" ]
          },
          "name": {
            "type": "string",
            "enum": [ "brief", "debrief" ]
          },
          "precedence": {
            "type": "string",
            "enum": [ "start", "urgent", "standard", "additional", "finish" ]
          }
        },
        "additionalProperties": false,
        "required": [
          "type",
          "name",
          "precedence"
        ]
      }
    },
    "nodes": {
      "actionNode": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [ "action" ]
          },
          "id": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "skip": {
            "type": "boolean"
          },
          "service": {
            "type": "object",
            "properties": {
              "url": {
                "type": "string"
              },
              "headers": {
                "type": "object",
                "patternProperties": {
                  ".+": {
                    "type": "string"
                  }
                },
                "additionalProperties": false
              },
              "method": {
                "type": "string",
                "enum": [ "GET", "PUT", "POST", "DELETE" ]
              },
              "body": {
                "$ref": "#/definitions/json"
              },
              "authorised": {
                "type": "boolean"
              }
            },
            "additionalProperties": false,
            "required": [
              "url",
              "method"
            ]
          },
          "outputs": {
            "type": "object",
            "properties": {
              "body": {
                "$ref": "#/definitions/patterns/propertiesObject"
              },
              "header": {
                "$ref": "#/definitions/patterns/propertiesObject"
              }
            },
            "additionalProperties": false
          },
          "nextNodeIndex": {
            "type": [ "number", "null" ]
          },
          "nextNode": {
            "type": [ "string", "null" ]
          }
        },
        "additionalProperties": false,
        "required": [
          "type",
          "service"
        ]
      },
      "attachmentPromptNode": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [ "attachmentPrompt" ]
          },
          "id": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "skip": {
            "type": "boolean"
          },
          "contentTypes": {
            "$ref": "#/definitions/mimeTypes"
          },
          "message": {
            "$ref": "#/definitions/outputShort"
          },
          "maxRetries": {
            "type": "number"
          },
          "continueOnFail": {
            "type": "boolean"
          },
          "retryMessage": {
            "$ref": "#/definitions/outputShort"
          },
          "customContent": {
            "$ref": "#/definitions/nodeCustomContent"
          },
          "output": {
            "$ref": "#/definitions/patterns/propertyReferenceBlob"
          },
          "nextNodeIndex": {
            "type": [ "number", "null" ]
          },
          "nextNode": {
            "type": [ "string", "null" ]
          }
        },
        "additionalProperties": false,
        "required": [
          "type",
          "message",
          "retryMessage",
          "output"
        ]
      },
      "buildObjectNode": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [ "buildObject" ]
          },
          "object": {
            "type": "object",
            "properties": {}
          },
          "output": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "skip": {
            "type": "boolean"
          },
          "nextNodeIndex": {
            "type": [ "number", "null" ]
          },
          "nextNode": {
            "type": [ "string", "null" ]
          }
        },
        "additionalProperties": false,
        "required": [
          "type",
          "object",
          "output"
        ]
      },
      "cardCollectionNode": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [ "cardCollection" ]
          },
          "id": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "skip": {
            "type": "boolean"
          },
          "listName": {
            "$ref": "#/definitions/patterns/propertyReference"
          },
          "contentItem": {
            "$ref": "#/definitions/patterns/alphanumeric"
          },
          "content": {
            "$ref": "#/definitions/cardContent"
          },
          "messageText": {
            "$ref": "#/definitions/outputShort"
          },
          "finishMessage": {
            "$ref": "#/definitions/outputShort"
          },
          "output": {
            "$ref": "#/definitions/patterns/propertyReference"
          },
          "maxRetries": {
            "type": "number"
          },
          "continueOnFail": {
            "type": "boolean"
          },
          "nextNodeIndex": {
            "type": [ "number", "null" ]
          },
          "nextNode": {
            "type": [ "string", "null" ]
          },
          "pageSize": {
            "type": "number"
          }
        },
        "additionalProperties": false,
        "required": [
          "type",
          "listName",
          "contentItem",
          "content"
        ]
      },
      "cardNode": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [ "card" ]
          },
          "id": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "skip": {
            "type": "boolean"
          },
          "content": {
            "$ref": "#/definitions/cardContent"
          },
          "maxRetries": {
            "type": "number"
          },
          "continueOnFail": {
            "type": "boolean"
          },
          "nextNodeIndex": {
            "type": [ "number", "null" ]
          },
          "nextNode": {
            "type": [ "string", "null" ]
          }
        },
        "additionalProperties": false,
        "required": [
          "type",
          "content"
        ]
      },
      "choicePromptNode": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [ "choicePrompt" ]
          },
          "id": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "skip": {
            "type": "boolean"
          },
          "message": {
            "$ref": "#/definitions/outputShort"
          },
          "retryMessage": {
            "$ref": "#/definitions/outputShort"
          },
          "listName": {
            "$ref": "#/definitions/patterns/propertyReference"
          },
          "displayName": {
            "$ref": "#/definitions/patterns/propertyReference"
          },
          "output": {
            "$ref": "#/definitions/patterns/propertyReference"
          },
          "maxRetries": {
            "type": "number"
          },
          "continueOnFail": {
            "type": "boolean"
          },
          "nextNodeIndex": {
            "type": [ "number", "null" ]
          },
          "nextNode": {
            "type": [ "string", "null" ]
          }
        },
        "additionalProperties": false,
        "required": [
          "type",
          "message",
          "retryMessage",
          "listName",
          "output"
        ]
      },
      "confirmationPromptNode": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [ "confirmationPrompt" ]
          },
          "id": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "skip": {
            "type": "boolean"
          },
          "message": {
            "$ref": "#/definitions/outputShort"
          },
          "retryMessage": {
            "$ref": "#/definitions/outputShort"
          },
          "positiveMessage": {
            "$ref": "#/definitions/outputShort"
          },
          "negativeMessage": {
            "$ref": "#/definitions/outputShort"
          },
          "output": {
            "$ref": "#/definitions/patterns/propertyReference"
          },
          "maxRetries": {
            "type": "number"
          },
          "continueOnFail": {
            "type": "boolean"
          },
          "nextNodeIndex": {
            "type": [ "number", "null" ]
          },
          "nextNode": {
            "type": [ "string", "null" ]
          }
        },
        "additionalProperties": false,
        "required": [
          "type",
          "message",
          "retryMessage",
          "output"
        ]
      },
      "customCardCollectionNode": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [ "customCardCollection" ]
          },
          "id": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "skip": {
            "type": "boolean"
          },
          "listName": {
            "$ref": "#/definitions/patterns/propertyReference"
          },
          "contentItem": {
            "$ref": "#/definitions/patterns/alphanumeric"
          },
          "customContent": {
            "$ref": "#/definitions/nodeCustomContent"
          },
          "messageText": {
            "$ref": "#/definitions/outputShort"
          },
          "outputOperation": {
            "type": "object",
            "properties": {
              "operation": {
                "$ref": "#/definitions/jsonLogic"
              },
              "output": {
                "$ref": "#/definitions/patterns/propertyReference"
              }
            },
            "additionalProperties": false,
            "required": [
              "operation",
              "output"
            ]
          },
          "finishMessage": {
            "$ref": "#/definitions/outputShort"
          },
          "output": {
            "$ref": "#/definitions/patterns/propertyReference"
          },
          "selected": {
            "$ref": "#/definitions/patterns/propertyReference"
          },
          "nextNodeIndex": {
            "type": [ "number", "null" ]
          },
          "nextNode": {
            "type": [ "string", "null" ]
          },
          "pageSize": {
            "type": "number"
          },
          "validation": {
            "$ref": "#/definitions/jsonLogic"
          }
        },
        "additionalProperties": false,
        "OneOf": [
          { "required": [ "type", "listName", "contentItem", "customContent" ] },
          { "required": [ "type", "listName", "contentItem", "customContent", "finishMessage", "selected", "output" ] }
        ]
      },
      "customEventNode": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [ "customEvent" ]
          },
          "id": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "skip": {
            "type": "boolean"
          },
          "name": {
            "type": "string"
          },
          "data": {
            "$ref": "#/definitions/logicData"
          },
          "nextNode": {
            "type": [ "string", "null" ]
          }
        },
        "additionalProperties": false,
        "required": [
          "type",
          "name"
        ]
      },
      "datePromptNode": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [ "datePrompt", "dateTimePrompt" ]
          },
          "id": {
            "type": "string"
          },
          "context": {
            "type": "string",
            "enum": [ "earliest", "latest" ]
          },
          "description": {
            "type": "string"
          },
          "skip": {
            "type": "boolean"
          },
          "message": {
            "$ref": "#/definitions/outputShort"
          },
          "retryMessage": {
            "$ref": "#/definitions/outputShort"
          },
          "customContent": {
            "$ref": "#/definitions/nodeCustomContent"
          },
          "output": {
            "$ref": "#/definitions/patterns/propertyReference"
          },
          "maxRetries": {
            "type": "number"
          },
          "continueOnFail": {
            "type": "boolean"
          },
          "nextNodeIndex": {
            "type": [ "number", "null" ]
          },
          "nextNode": {
            "type": [ "string", "null" ]
          },
          "validation": {
            "$ref": "#/definitions/jsonLogic"
          }
        },
        "additionalProperties": false,
        "required": [
          "type",
          "message",
          "retryMessage",
          "output"
        ]
      },
      "decisionNode": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [ "decision" ]
          },
          "id": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "rule": {
            "$ref": "#/definitions/jsonLogic"
          },
          "passNodeIndex": {
            "type": [ "number", "null" ]
          },
          "passNode": {
            "type": [ "string", "null" ]
          },
          "failNodeIndex": {
            "type": [ "number", "null" ]
          },
          "failNode": {
            "type": [ "string", "null" ]
          }
        },
        "additionalProperties": false,
        "required": [
          "type",
          "rule"
        ]
      },
      "dialogueNode": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [ "dialogue" ]
          },
          "id": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "skip": {
            "type": "boolean"
          },
          "dialogueId": {
            "$ref": "#/definitions/dialogueId"
          },
          "inputs": {
            "$ref": "#/definitions/patterns/dialogueNodeInputPropertiesObject"
          },
          "outputs": {
            "$ref": "#/definitions/patterns/dialogueNodeOutputPropertiesObject"
          },
          "nextNodeIndex": {
            "type": [ "number", "null" ]
          },
          "nextNode": {
            "type": [ "string", "null" ]
          }
        },
        "additionalProperties": false,
        "required": [
          "type",
          "dialogueId"
        ]
      },
      "downloadActionNode": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [ "downloadAction" ]
          },
          "id": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "skip": {
            "type": "boolean"
          },
          "service": {
            "type": "object",
            "properties": {
              "url": {
                "type": "string"
              },
              "headers": {
                "type": "object",
                "patternProperties": {
                  ".+": {
                    "type": "string"
                  }
                },
                "additionalProperties": false
              },
              "authorised": {
                "type": "boolean"
              }
            },
            "additionalProperties": false,
            "required": [
              "url"
            ]
          },
          "outputs": {
            "type": "object",
            "properties": {
              "content": {
                "$ref": "#/definitions/patterns/propertyReferenceBlob"
              },
              "header": {
                "$ref": "#/definitions/patterns/propertiesObject"
              }
            },
            "additionalProperties": false
          },
          "nextNodeIndex": {
            "type": [ "number", "null" ]
          },
          "nextNode": {
            "type": [ "string", "null" ]
          }
        },
        "additionalProperties": false,
        "required": [
          "type",
          "service"
        ]
      },
      "nlpEntitiesNode": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [ "nlpEntities" ]
          },
          "id": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "skip": {
            "type": "boolean"
          },
          "input": {
            "$ref": "#/definitions/patterns/propertyReference"
          },
          "requiredEntities": {
            "$ref": "#/definitions/patterns/propertyReference"
          },
          "outputs": {
            "type": "object",
            "properties": {
              "matchedEntities": {
                "$ref": "#/definitions/patterns/propertyReference"
              }
            },
            "additionalProperties": false,
            "required": [
              "matchedEntities"
            ]
          },
          "rules": {
            "type": "object",
            "properties": {
              "dateContext": {
                "type": "string",
                "enum": [ "earliest", "latest" ]
              },
              "likelyStartTime": {
                "type": [ "number" ]
              }
            },
            "additionalProperties": false,
            "required": [
              "dateContext"
            ]
          },
          "nextNodeIndex": {
            "type": [ "number", "null" ]
          },
          "nextNode": {
            "type": [ "string", "null" ]
          }
        },
        "additionalProperties": false,
        "required": [
          "type",
          "input",
          "outputs",
          "requiredEntities"
        ]
      },
      "eventNode": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [ "event" ]
          },
          "id": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "skip": {
            "type": "boolean"
          },
          "event": {
            "type": "string",
            "enum": [ "leaveConversation", "resetDialogue", "endDialogue", "resetAuthentication" ]
          }
        },
        "additionalProperties": false,
        "required": [
          "type",
          "event"
        ]
      },
      "messageNode": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [ "message" ]
          },
          "id": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "skip": {
            "type": "boolean"
          },
          "message": {
            "$ref": "#/definitions/outputShort"
          },
          "customContent": {
            "$ref": "#/definitions/nodeCustomContent"
          },
          "nextNodeIndex": {
            "type": [ "number", "null" ]
          },
          "nextNode": {
            "type": [ "string", "null" ]
          }
        },
        "additionalProperties": false,
        "required": [
          "type",
          "message"
        ]
      },
      "operationNode": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [ "operation" ]
          },
          "id": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "skip": {
            "type": "boolean"
          },
          "operation": {
            "$ref": "#/definitions/jsonLogic"
          },
          "output": {
            "$ref": "#/definitions/patterns/propertyReference"
          },
          "nextNodeIndex": {
            "type": [ "number", "null" ]
          },
          "nextNode": {
            "type": [ "string", "null" ]
          }
        },
        "additionalProperties": false,
        "required": [
          "type",
          "operation",
          "output"
        ]
      },
      "repeatDialogueNode": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [ "repeatDialogue" ]
          },
          "id": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "skip": {
            "type": "boolean"
          },
          "dialogueId": {
            "$ref": "#/definitions/dialogueId"
          },
          "inputs": {
            "$ref": "#/definitions/patterns/dialogueNodeInputPropertiesObject"
          },
          "maximumRepetitions": {
            "type": [ "number" ]
          },
          "nextNode": {
            "type": [ "string", "null" ]
          },
          "nextNodeIndex": {
            "type": [ "number", "null" ]
          },
          "outputs": {
            "$ref": "#/definitions/patterns/dialogueNodeOutputPropertiesObject"
          },
          "repeatUntil": {
            "$ref": "#/definitions/jsonLogic"
          }
        },
        "additionalProperties": false,
        "required": [
          "type",
          "dialogueId",
          "inputs",
          "outputs",
          "repeatUntil"
        ]
      },
      "sequenceDialogueNode": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [ "sequenceDialogue" ]
          },
          "id": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "skip": {
            "type": "boolean"
          },
          "dialogueId": {
            "$ref": "#/definitions/dialogueId"
          },
          "inputItem": {
            "$ref": "#/definitions/patterns/alphanumeric"
          },
          "inputs": {
            "$ref": "#/definitions/patterns/dialogueNodeInputPropertiesObject"
          },
          "listName": {
            "$ref": "#/definitions/patterns/propertyReference"
          },
          "nextNodeIndex": {
            "type": [ "number", "null" ]
          },
          "nextNode": {
            "type": [ "string", "null" ]
          }
        },
        "additionalProperties": false,
        "required": [
          "type",
          "dialogueId",
          "inputItem",
          "listName"
        ]
      },
      "simplePromptNode": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [ "numberPrompt", "stringPrompt", "timePrompt" ]
          },
          "id": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "skip": {
            "type": "boolean"
          },
          "maxRetries": {
            "type": "number"
          },
          "continueOnFail": {
            "type": "boolean"
          },
          "message": {
            "$ref": "#/definitions/outputShort"
          },
          "retryMessage": {
            "$ref": "#/definitions/outputShort"
          },
          "customContent": {
            "$ref": "#/definitions/nodeCustomContent"
          },
          "output": {
            "$ref": "#/definitions/patterns/propertyReference"
          },
          "nextNodeIndex": {
            "type": [ "number", "null" ]
          },
          "nextNode": {
            "type": [ "string", "null" ]
          },
          "validation": {
            "$ref": "#/definitions/jsonLogic"
          }
        },
        "additionalProperties": false,
        "required": [
          "type",
          "message",
          "retryMessage",
          "output"
        ]
      }
    },
    // Logic
    "logicData": {
      "type": "object",
      "properties": {
        "var": {
          "$ref": "#/definitions/contextDataProperty"
        }
      },
      "additionalProperties": false,
      "required": [
        "var"
      ]
    },
    "logicAnyUnary": {
      "oneOf": [
        {
          "type": "array",
          "maxItems": 1,
          "minItems": 1,
          "items": {
            "$ref": "#/definitions/jsonLogic"
          }
        },
        {
          "$ref": "#/definitions/jsonLogic"
        }
      ]
    },
    "logicAny": {
      "type": "array",
      "maxItems": 2,
      "minItems": 2,
      "items": {
        "$ref": "#/definitions/jsonLogic"
      }
    },
    "logicDate": {
      "type": "array",
      "maxItems": 2,
      "minItems": 2,
      "items": {
        "$ref": "#/definitions/typeDate"
      }
    },
    "logicDateTime": {
      "type": "array",
      "maxItems": 2,
      "minItems": 2,
      "items": {
        "$ref": "#/definitions/typeDateTime"
      }
    },
    "logicNumeric": {
      "type": "array",
      "maxItems": 2,
      "minItems": 2,
      "items": {
        "$ref": "#/definitions/typeNumeric"
      }
    },
    "logicSize": {
      "anyOf": [
        { "$ref": "#/definitions/logicDate" },
        { "$ref": "#/definitions/logicDateTime" },
        { "$ref": "#/definitions/logicNumeric" },
        { "$ref": "#/definitions/logicTime" }
      ]
    },
    "logicTime": {
      "type": "array",
      "maxItems": 2,
      "minItems": 2,
      "items": {
        "$ref": "#/definitions/typeTime"
      }
    },
    // Types
    "jsonLogic": {
      "anyOf": [
        {
          "$ref": "#/definitions/typeAny"
        },
        {
          "$ref": "#/definitions/typeBoolean"
        },
        {
          "$ref": "#/definitions/typeComplex"
        },
        {
          "$ref": "#/definitions/typeDate"
        },
        {
          "$ref": "#/definitions/typeDateTime"
        },
        {
          "$ref": "#/definitions/typeList"
        },
        {
          "$ref": "#/definitions/typeNumeric"
        },
        {
          "$ref": "#/definitions/typeString"
        },
        {
          "$ref": "#/definitions/typeTime"
        },
        {
          "type": "null"
        }
      ]
    },
    "typeAny": {
      "oneOf": [
        {
          "type": "object",
          "properties": {
            "method": {
              "type": "array",
              "items": [
                {
                  "$ref": "#/definitions/typeList"
                },
                {
                  "type": "string",
                  "enum": [ "getItem" ]
                },
                {
                  "type": "array",
                  "items": [
                    {
                      "$ref": "#/definitions/typeNumeric"
                    }
                  ],
                  "minItems": 1,
                  "maxItems": 1
                }
              ],
              "minItems": 3,
              "maxItems": 3
            }
          },
          "additionalProperties": false,
          "required": [
            "method"
          ]
        },
        {
          "type": "object",
          "properties": {
            "if": {
              "type": "array",
              "items": {
                /* if (condition1, output1, [condition2, output2, ... conditionN, outputN], outputN+1) where N >= 1 */
                "$ref": "#/definitions/jsonLogic"
              },
              "minItems": 3
            }
          },
          "additionalProperties": false,
          "required": [
            "if"
          ]
        },
        {
          "type": "object",
          "properties": {
            "current": {
              "type": "string"
            }
          },
          "additionalProperties": false,
          "required": [
            "current"
          ]
        },
        {
          "$ref": "#/definitions/logicData"
        }
      ]
    },
    "typeBoolean": {
      "oneOf": [
        {
          "type": "boolean"
        },
        {
          "type": "object",
          "properties": {
            "method": {
              "type": "array",
              "items": [
                {
                  "$ref": "#/definitions/typeString"
                },
                {
                  "type": "string",
                  "enum": [ "matchesPattern" ]
                },
                {
                  "type": "array",
                  "items": {
                    "$ref": "#/definitions/typeString"
                  },
                  "minItems": 1,
                  "maxItems": 1
                }
              ],
              "minItems": 3,
              "maxItems": 3
            }
          },
          "additionalProperties": false,
          "required": [
            "method"
          ]
        },
        {
          "type": "object",
          "properties": {
            "===": {
              "$ref": "#/definitions/logicAny"
            }
          },
          "additionalProperties": false,
          "required": [
            "==="
          ]
        },
        {
          "type": "object",
          "properties": {
            "==": {
              "$ref": "#/definitions/logicAny"
            }
          },
          "additionalProperties": false,
          "required": [
            "=="
          ]
        },
        {
          "type": "object",
          "properties": {
            "!==": {
              "$ref": "#/definitions/logicAny"
            }
          },
          "additionalProperties": false,
          "required": [
            "!=="
          ]
        },
        {
          "type": "object",
          "properties": {
            "!=": {
              "$ref": "#/definitions/logicAny"
            }
          },
          "additionalProperties": false,
          "required": [
            "!="
          ]
        },
        {
          "type": "object",
          "properties": {
            "and": {
              "type": "array",
              "items": {
                "$ref": "#/definitions/jsonLogic"
              },
              "minItems": 2
            }
          },
          "additionalProperties": false,
          "required": [
            "and"
          ]
        },
        {
          "type": "object",
          "properties": {
            "or": {
              "type": "array",
              "items": {
                "$ref": "#/definitions/jsonLogic"
              },
              "minItems": 2
            }
          },
          "additionalProperties": false,
          "required": [
            "or"
          ]
        },
        {
          "type": "object",
          "properties": {
            "!": {
              "$ref": "#/definitions/logicAnyUnary"
            }
          },
          "additionalProperties": false,
          "required": [
            "!"
          ]
        },
        {
          "type": "object",
          "properties": {
            "!!": {
              "$ref": "#/definitions/logicAnyUnary"
            }
          },
          "additionalProperties": false,
          "required": [
            "!!"
          ]
        },
        {
          "type": "object",
          "properties": {
            ">": {
              "$ref": "#/definitions/logicSize"
            }
          },
          "additionalProperties": false,
          "required": [
            ">"
          ]
        },
        {
          "type": "object",
          "properties": {
            ">=": {
              "$ref": "#/definitions/logicSize"
            }
          },
          "additionalProperties": false,
          "required": [
            ">="
          ]
        },
        {
          "type": "object",
          "properties": {
            "<": {
              "$ref": "#/definitions/logicSize"
            }
          },
          "additionalProperties": false,
          "required": [
            "<"
          ]
        },
        {
          "type": "object",
          "properties": {
            "<=": {
              "$ref": "#/definitions/logicSize"
            }
          },
          "additionalProperties": false,
          "required": [
            "<="
          ]
        },
        {
          "$ref": "#/definitions/typeAny"
        }
      ]
    },
    "typeComplex": {
      "oneOf": [
        {
          "type": "object",
          "properties": {
            "Date.difference": {
              "type": "array",
              "items": [
                {
                  "$ref": "#/definitions/typeDate"
                },
                {
                  "$ref": "#/definitions/typeDate"
                }
              ],
              "minItems": 2,
              "maxItems": 2
            }
          },
          "additionalProperties": false,
          "required": [
            "Date.difference"
          ]
        },
        {
          "type": "object",
          "properties": {
            "DateTime.difference": {
              "type": "array",
              "items": [
                {
                  "$ref": "#/definitions/typeDateTime"
                },
                {
                  "$ref": "#/definitions/typeDateTime"
                }
              ],
              "minItems": 2,
              "maxItems": 2
            }
          },
          "additionalProperties": false,
          "required": [
            "DateTime.difference"
          ]
        },
        {
          "$ref": "#/definitions/typeAny"
        }
      ]
    },
    "typeDate": {
      "oneOf": [
        {
          "type": "string"
        },
        {
          "type": "object",
          "properties": {
            "method": {
              "type": "array",
              "items": [
                {
                  "$ref": "#/definitions/typeDate"
                },
                {
                  "type": "string",
                  "pattern": "^(add|subtract)(Days|Weeks|Months|Years)$"
                },
                {
                  "type": "array",
                  "items": {
                    "$ref": "#/definitions/typeNumeric"
                  },
                  "minItems": 1,
                  "maxItems": 1
                }
              ],
              "minItems": 3,
              "maxItems": 3
            }
          },
          "additionalProperties": false,
          "required": [
            "method"
          ]
        },
        {
          "type": "object",
          "properties": {
            "Date.currentDate": {
              "type": "array",
              "maxItems": 0
            }
          },
          "additionalProperties": false
        },
        {
          "type": "object",
          "properties": {
            "Date.fromUTC": {
              "type": "array",
              "items": [
                {
                  "$ref": "#/definitions/typeString"
                },
                {
                  "$ref": "#/definitions/typeString"
                }
              ],
              "minItems": 2,
              "maxItems": 2
            }
          },
          "additionalProperties": false
        },
        {
          "$ref": "#/definitions/typeAny"
        }
      ]
    },
    "typeDateTime": {
      "oneOf": [
        {
          "type": "string"
        },
        {
          "type": "object",
          "properties": {
            "method": {
              "type": "array",
              "items": [
                {
                  "$ref": "#/definitions/typeDateTime"
                },
                {
                  "type": "string",
                  "pattern": "^(add|subtract)(Seconds|Minutes|Hours|Days|Weeks|Months|Years)$"
                },
                {
                  "type": "array",
                  "items": {
                    "$ref": "#/definitions/typeNumeric"
                  },
                  "minItems": 1,
                  "maxItems": 1
                }
              ],
              "minItems": 3,
              "maxItems": 3
            }
          },
          "additionalProperties": false,
          "required": [
            "method"
          ]
        },
        {
          "type": "object",
          "properties": {
            "DateTime.currentDateTime": {
              "type": "array",
              "maxItems": 0
            }
          },
          "additionalProperties": false
        },
        {
          "type": "object",
          "properties": {
            "DateTime.fromUTC": {
              "type": "array",
              "items": [
                {
                  "$ref": "#/definitions/typeString"
                },
                {
                  "$ref": "#/definitions/typeString"
                }
              ],
              "minItems": 2,
              "maxItems": 2
            }
          },
          "additionalProperties": false
        },
        {
          "$ref": "#/definitions/typeAny"
        }
      ]
    },
    "typeList": {
      "oneOf": [
        {
          "type": "object",
          "properties": {
            "method": {
              "type": "array",
              "items": [
                {
                  "$ref": "#/definitions/typeList"
                },
                {
                  "type": "string",
                  "enum": [ "addItem" ]
                },
                {
                  "type": "array",
                  "items": {
                    "$ref": "#/definitions/jsonLogic"
                  },
                  "minItems": 1
                }
              ],
              "minItems": 3,
              "maxItems": 3
            }
          },
          "additionalProperties": false,
          "required": [
            "method"
          ]
        },
        {
          "type": "object",
          "properties": {
            "method": {
              "type": "array",
              "items": [
                {
                  "$ref": "#/definitions/typeList"
                },
                {
                  "type": "string",
                  "enum": [ "filter" ]
                },
                {
                  "oneOf": [
                    {
                      "type": "array",
                      "items": [
                        {
                          "type": "string"
                        },
                        {
                          "$ref": "#/definitions/jsonLogic"
                        }
                      ],
                      "minItems": 2,
                      "maxItems": 2
                    },
                    {
                      "type": "array",
                      "items": {
                        "$ref": "#/definitions/jsonLogic"
                      },
                      "minItems": 1,
                      "maxItems": 1
                    }
                  ]
                }
              ],
              "minItems": 3,
              "maxItems": 3
            }
          },
          "additionalProperties": false,
          "required": [
            "method"
          ]
        },
        {
          "type": "object",
          "properties": {
            "method": {
              "type": "array",
              "items": [
                {
                  "$ref": "#/definitions/typeString"
                },
                {
                  "type": "string",
                  "enum": [ "split" ]
                },
                {
                  "type": "array",
                  "items": [
                    {
                      "$ref": "#/definitions/typeString"
                    }
                  ],
                  "minItems": 1,
                  "maxItems": 1
                }
              ],
              "minItems": 3,
              "maxItems": 3
            }
          },
          "additionalProperties": false,
          "required": [
            "method"
          ]
        },
        {
          "type": "object",
          "properties": {
            "method": {
              "type": "array",
              "items": [
                {
                  "$ref": "#/definitions/typeList"
                },
                {
                  "type": "string",
                  "enum": [ "sort" ]
                },
                {
                  "type": "array",
                  "items": {
                    "$ref": "#/definitions/jsonLogic"
                  }
                }
              ],
              "minItems": 3,
              "maxItems": 3
            }
          },
          "additionalProperties": false,
          "required": [
            "method"
          ]
        },
        {
          "type": "object",
          "properties": {
            "method": {
              "type": "array",
              "items": [
                {
                  "$ref": "#/definitions/typeList"
                },
                {
                  "type": "string",
                  "enum": [ "removeItem" ]
                },
                {
                  "type": "array",
                  "items": [
                    {
                      "$ref": "#/definitions/typeNumeric"
                    }
                  ],
                  "minItems": 0,
                  "maxItems": 1
                }
              ],
              "minItems": 3,
              "maxItems": 3
            }
          },
          "additionalProperties": false,
          "required": [
            "method"
          ]
        },
        {
          "type": "object",
          "properties": {
            "method": {
              "type": "array",
              "items": [
                {
                  "$ref": "#/definitions/typeList"
                },
                {
                  "type": "string",
                  "enum": [ "updateItem" ]
                },
                {
                  "type": "array",
                  "items": [
                    {
                      "$ref": "#/definitions/typeNumeric"
                    },
                    {
                      "$ref": "#/definitions/jsonLogic"
                    }
                  ],
                  "minItems": 2,
                  "maxItems": 2
                }
              ],
              "minItems": 3,
              "maxItems": 3
            }
          },
          "additionalProperties": false,
          "required": [
            "method"
          ]
        },
        {
          "$ref": "#/definitions/typeAny"
        }
      ]
    },
    "typeNumeric": {
      "oneOf": [
        {
          "type": "number"
        },
        {
          "type": "object",
          "patternProperties": {
            "^[\\+\\-]$": {
              "type": "array",
              "items": [
                {
                  "$ref": "#/definitions/typeNumeric"
                }
              ],
              "minItems": 1
            }
          },
          "additionalProperties": false,
          "minProperties": 1,
          "maxProperties": 1
        },
        {
          "type": "object",
          "properties": {
            "*": {
              "type": "array",
              "items": [
                {
                  "$ref": "#/definitions/typeNumeric"
                }
              ],
              "minItems": 2
            }
          },
          "additionalProperties": false,
          "required": [
            "*"
          ]
        },
        {
          "type": "object",
          "patternProperties": {
            "^[\\/\\%]$": {
              "type": "array",
              "items": [
                {
                  "$ref": "#/definitions/typeNumeric"
                }
              ],
              "minItems": 2,
              "maxItems": 2
            }
          },
          "additionalProperties": false,
          "minProperties": 1,
          "maxProperties": 1
        },
        {
          "type": "object",
          "properties": {
            "method": {
              "type": "array",
              "items": [
                {
                  "$ref": "#/definitions/logicData"
                },
                {
                  "type": "string",
                  "enum": [ "getCount" ]
                }
              ],
              "minItems": 2,
              "maxItems": 2
            }
          },
          "additionalProperties": false,
          "required": [
            "method"
          ]
        },
        {
          "type": "object",
          "properties": {
            "method": {
              "type": "array",
              "items": [
                {
                  "$ref": "#/definitions/typeString"
                },
                {
                  "type": "string",
                  "enum": [ "indexOf" ]
                },
                {
                  "type": "array",
                  "items": [
                    {
                      "$ref": "#/definitions/typeString"
                    },
                    {
                      "$ref": "#/definitions/typeNumeric"
                    }
                  ],
                  "minItems": 1,
                  "maxItems": 2
                }
              ],
              "minItems": 3,
              "maxItems": 3
            }
          },
          "additionalProperties": false,
          "required": [
            "method"
          ]
        },
        {
          "type": "object",
          "properties": {
            "method": {
              "type": "array",
              "items": [
                {
                  "$ref": "#/definitions/typeString"
                },
                {
                  "type": "string",
                  "enum": [ "length" ]
                }
              ],
              "minItems": 2,
              "maxItems": 2
            }
          },
          "additionalProperties": false,
          "required": [
            "method"
          ]
        },
        {
          "type": "object",
          "patternProperties": {
            "^Date[.]current(Year|Month|Day)$": {
              "type": "array",
              "maxItems": 0
            }
          },
          "additionalProperties": false,
          "minProperties": 1,
          "maxProperties": 1
        },
        {
          "type": "object",
          "properties": {
            "String.toLower": {
              "type": "array",
              "items": [
                {
                  "$ref": "#/definitions/typeString"
                }
              ],
              "minItems": 1,
              "maxItems": 1
            }
          },
          "additionalProperties": false,
          "required":