NAV Navbar
Logo

Introduction

This is the simplest possible bot in botSON.

{
  "version": "1.0",
  "states": [
    {
      "label": "initial",
      "output": "Hello World!",
      "next_step": "exit"
    }
  ]
}

botSON is a JSON based programming language to create chatbots for the Hubtype platform. The goal of botSON is to be a simple yet powerful tool to build conversational flows that the user navigates by feeding in text or media. In a simplified way, chatbots are defined as finite state machines, at each state the chatbot outputs some text and waits for the user input that makes the bot transition to another state. You can optionally incorporate Natural Language Understanding to your bot by integrating services like IBM Watson or API.ai. A context is kept for every bot session (the extent of a user’s conversation) which can be updated at every step with variables and external http calls.

Additionally, botSON supports triggers, which are high priority actions that the user can invoke regardless of the current state of the conversation.

botSON is messenger agnostic, meaning that it’s not designed specifically for a single messaging app but it works with any messaging channel supported by Hubtype, however not all interactive elements are supported by all channels (like carrousels).

We encourage you to watch the screencasts:

Examples

Dynamic carrousel

{
  "name": "Clothes Bot",
  "triggers": {},
  "version": "1.0",
  "literals": { #we store our secret api key
      "api_key": "uid8900-40385330-57"
  },
  "states": [
    {
      "label": "initial",
      "output":
      {
          "type": "text",
          "data": "Hey, what clothes are you interested in?",
          "keyboard": [
            {"label": "Mens shirt", "data": "mens-shirts"},
            {"label": "Womens shirt", "data": "womens-tops"}
          ]
      },
      "input": {
          "variable": "user_response",
          "action": "get_in_keyboard"
      },
      "next_step": "create_carrousel"
    },
    {
      "label": "create_carrousel",
      "context":[
          {"items": { #we get all the items id's related with the user_response
                  "url": "http://api.shopstyle.com/api/v2/products?pid={{api_key}}&fts={{user_response.data}}&offset=0&limit=5",
                  "method": "GET",
                  "params": {
                  }
              }
          },
          {"full_items": "[
              {# for every item, we store all the information in 'full_items' #}
              {% for item in items.products %}
                  {{http('http://api.shopstyle.com/api/v2/products/' +
                      item.id|string +
                      '?pid=' +
                      api_key|string
                  )}},
              {% endfor %}
          ]"
          }
      ],
      "output": [
          {
              "type": "carrousel", #for every item, we display his information in a carrousel
              "elements": "[
                  {% for item in full_items %}
                      {
                          'title': '{{item.name}}',
                          'subtitle': '{{item.priceLabel}}',
                          'image_url': '{{item.image.sizes.Best.url}}',
                          'buttons':[
                              {
                                  'type': 'web_url',
                                  'title':'Open product',
                                  'url': '{{item.clickUrl}}'
                              }
                          ]
                      },
                  {% endfor %}
              ]"
          }
      ],
      "next_step": "exit"
    }
  ]
}

This is a simple bot, that displays five men or woman shirts and its price, and we are getting the clothes information from the Shopstyle API.

First of all, we ask what are the user interests. When we get it, we generate the carrousel of the products.

When we want to make REST API request, we create the context, and we put all the logic in there. In order to get all the information, we store all the items making a request with the user prefence and our API_KEY.

The request goes against a specific url, with his specific method, and if necessary with the params.

For getting all the information of every product, we need to make a call to the API for all the shirt ids. The id of a product, it’s in JSON format, and we can do item.id or item['id'] to access it.

When we have all the product information, we can display the carrousel.

The carrousel can have at most ten elements, where each element will represent one product and will have his name, the price, an image and a link to the product page.

For access to this values, we do item.name , item.priceLabel, etc.

Location Bot

{
  "name": "Location bot",
  "input_retry": 3,
  "triggers": {},
  "version": "1.0",
  "states": [
      {
        "label": "initial",
        "output":
        {
            "type": "text",
            "data": "Hey, where I have to pick you up?",
            "keyboard": [
                {
                    "location": ""
                }
            ]
        },
        "input":{
            "action":"get_location",
            "variable":"location"
        },
        "next_step": "localizacion_obtenida"
      },
      {
        "label": "localizacion_obtenida",
        "output": [
          {
            "type": "text",
            "data": "Okai, I will pick up right now: \nLocation = long: {{location.latitude}} , lat: {{location.longitude}}"
          }
        ],
        "next_step": "initial"
      }
  ]
}

Being able to manage location is a powerfull tool nowadays.

This bot ask for your location, and with a simple click, the user can send his location to the bot. In the state initial, we ask to the user where is he, and with the option location in the keyboard, we display a keyboard that will get the user location.

Then, in the input, we store the user location in the variable location.

Handover Bot

{
  "name": "Handover bot",
  "input_retry": 3,
  "triggers": {
      "text": [
          {
              "match": "^reset$",
              "next_step": "initial"
          }    
      ]
  },
  "version": "1.0",
  "states": [
    {
      "label": "initial",
      "output":
      {
          "type": "text",
          "data": "Hey, how can I help you?"
      },
      "input":{
          "type":"text",
          "variable":"response"
      },
      "next_step": "transfer_case"
    },
    {
      "label": "transfer_case",
      "context": { #we create a new case in the queue q1.
          "case": {
            "action": "create_case",
            "queue": "q1"
          }
      },
      "output": {
          "type": "text",
          "data": "An agent will immediately take care of your case"
      },
      "input": {
          "variable": "trash",
          "action": "free_text"
      },
      "next_step": "exit"
    }
  ]
}

As bots aren’t perfects, we can transfer a case to an agent in an easy way, let’s have a look how this bot works.

This bot don’t understand anything, so any input that we enter it will be redirected to an agent. In order to do that, when we reach the state where we want to transfer the case, we declare in the context an object called case. This object will have two values, the action that must be ‘create_case’ and queue that it’s the name or id of the queue where we will generate the case.

When the case it’s resolved by the agent, the bot will continue to the next_step of the state.

Failure Bot

{
  "name": "Failure bot",
  "input_retry": 3,
  "max_loop": 10,
  "triggers": {},
  "version": "1.0",
  "states": [
      {
            "label": "initial",
            "output": {
                "type": "text",
                "data": "Here you have to choose:",
                "keyboard": [
                    {"label": "State not defined", "data": "not_defined"},
                    {"label": "HTTP_REQUEST", "data": "http_request"},
                    {"label": "LOOP", "data": "loopA"}
                ]
            },
            "input": {
                "action": "get_in_keyboard",
                "variable": "user_choice"
            },
            "next_step": "{{user_choice.data}}" #next step depending on the user response
        },
        {
            "label": "http_request",
            "context":{ #we make a request to an invented link that doesn't exist.
                "image_test": {
                    "url": "www.randomurl.com/randomimage.jpg",
                    "method": "GET",
                    "params": {}
                }
            },
            "output": {
                "type": "image",
                "data": "{{url}}"
            },
            "next_step": "exit"
        },
        {
            "label": "loopA",
            "output": "A",
            "next_step": "loopB"
        },
        {
            "label": "loopB",
            "output": "B",
            "next_step": "loopA" #creates a bucle: loopA --> loopB --> loopA
        },
        {
            "label": "input_failure",
            "output": "You have failed answering the bot",
            "next_step": "exit"
        },
        {
            "label": "fallback_instruction",
            "output": "The bot don't recognize this state",
            "next_step": "exit"
        },
        {
            "label": "external_request_failure",
            "output": "There were an error with some http request",
            "next_step": "exit"
        },
        {
            "label": "loop_overflow",
            "output": "The bot enter to a loop",
            "next_step": "exit"
        }
  ]
}

In this bot, we can find all the possible states where the bot can be redirected when it gets lost or experience some error.

In the first demo, we see how the user enters 3 times some input that the bot isn’t expecting, and it will display the same question as many times as you have declares in the input_retry. If the bot reaches the number of failures as declared, it will go into the input_failure state.

In the other demo, we can find the other three types of failure states. The first one, the fallback_instruction, it’s reached when the bot tries to enter into the state not_defined, and doesn’t find it.

The second case is the external_request_failure, in this state we make a get request, and as we don’t get any response, the bot goes into external_request_failure.

And the last one is reached when the user enters a loop. We are jumping from state loopA to loopB and vice versa. When it hits 10 times (defined at max_loop), the bot jumps to the state loop_overflow.



Dynamic next state

{
    "name": "Next State Bot",
    "input_retry": 1,
    "triggers": {},
    "version": "1.0",
    "states": [
        {
            "label": "initial",
            "output": [
                {
                    "type": "text", 
                    "data": "What do you prefer, rigth or left?",
                    "keyboard": [
                        {"label": "Right", "data": "right"},
                        {"label": "Left", "data": "left"}
                    ]
                }
            ],
            "input": {
                "type": "in_keyboard",
                "variable": "user_choice"
            },
            "next_step": [
                "{% if user_choice.data == 'right' %}",
                    "right_state",
                "{% elif user_choice.data == 'left' %}",
                    "left_state",
                "{% else %}",
                    "exit",
                "{% endif %}"
            ]
        },
        {
            "label": "right_state",
            "output": {
                "type": "text",
                "data": "Let's go right"
            },
            "next_step": "exit"
        },
        {
            "label": "left_state",
            "output": {
                "type": "text",
                "data": "Let's go left"
            },
            "next_step": "exit"
        }
    ]
}

When the next_state depends on the user response option, we can make a dynamic next_state definition.

In this bot, we can see how we store the user decision in user_choice. Depending on what the user has chosen, the bot will continue to one state or another.

If the user decided 'right’, the bot will go to right_state, else if he decided 'left’ it will got to left_state. Otherwise, for avoiding possible errors, we say that if the data it’s not right or left, it will go to exit.

Getting Started

Creating a state

{
  "version": "1.0",
  "states": [
    {
      "label": "initial",
      "next_step": "helloworld"
    },
    {
      "label": "helloworld",
      "output": "Hello World!",
      "next_step": "exit"
    }
  ]
}

A bot is composed by states. Each state has a label, a next_step and usually an output or input (or both). When the bot enters a state, the first thing it does is to send the output (if there’s one) to the user, then awaits for the user input if there’s an input section. Finally it jumps to the state defined in the expression next_step. The state 'exit’ indicates the end of the session, if the user talks again then a new session will start from the state defined in 'initial_state’.

When the bot enters a state with no input, it just sends the output (if any) and jumps to next_state immediatelly. It is possible to chain several state jumps without any input, the bot will just send all the outputs consecutively until it reaches a state with an input statement, then it will pause until the user sends a new message.

Basic bot

{
    "input_retry": 3,
    "version": "1.0",
    "states": [
        {
            "label": "first_state",
            "next_step": "choice"
        },
        {
            "label": "choice",
            "output": {
                "type": "text",
                "data": "Here you have to choose:",
                "keyboard": [
                    {"label": "Red", "data": "RED"},
                    {"label": "Blue", "data": "BLUE"},
                    {"label": "Green", "data": "GREEN"}
                ]
            },
            "input": {
                "type": "in_keyboard",
                "variable": "user_choice"
            },
            "next_step": "result"
        },
        {
            "label": "result",
            "output": "Your choice was {{user_choice.data}}",
            "next_step": "choice"
        },
        {
            "label": "input_failure",
            "output": "I don't understand what you're trying to tell me, it seems I'm not smart enough for you =(",
            "next_step": "choice"
        }
    ]
}

A complete bot usually never ends, it loops back to previous states. Also, it can use variables and other forms of input and output other than simple text.

In this case, after the first text of the user, this bot will show three quick replies: 'Red’, 'Blue’ and 'Green’.

The bot will wait for the user to click on one of these quick replies and then it will store the key pressed in the context variable user_choice. For example, if the user clicks on “Red”, the bot will set user_choice to {"label": "Red", "data": "RED"} and then jump to the next state result.

If the user wrote any random sentence, the chatbot would ignore that input and prompt the keyboard again. If the user fails to give an apropiate answer 3 times (this can be configured using the input_retry setting) it will go to the state input_failure which is defined under the hood by default but it can be overridden.

Finally, in the state result the bot sends a text message saying what was the pick of the user and returns to the choice state.

botSON code

{
  "something": "this is a multiline
    line because
    botSON is cooler than JSON"
}

botSON and JSON are slightly different, botSON allows multiline strings while JSON don’t.

Top level properties

input_retry

{
  "input_retry": 2,
  "name": "The Weather Bot",
  "defaults": {
    "requests": {
      "headers": {
        "Authorization": "Bearer mytoken"
      }
    },
    "context": {
      "API_URL": "https://api.example.com",
      "foo": "bar"
    }
  },
  "triggers": {},
  "states": []
}

Specifies the number of input failures before the machine goes to the 'input_failure’ state. By default is set to 3. An input failure is given when the type of input doesn’t match the required data.

name

The name of the bot.

version

Version of the BotSON language. We encourage you to explicitly set this setting to “1.0” as it’s the version this documentation refers to. If not set, the default value in “0.1”, a version that is NOT compatible with “1.0”.

initial_state

Label of the first state of the bot. Defaults to “initial”.

defaults

Defines the default values that are used throughout the chatbot flow in different states:

requests

Currently only supports default headers to be used in every url request.

delay:

Time (in seconds) to delay sending any outputs. It can be a decimal number. This parameter can be overridden in each output. Defaults to 0.

typing

Time (in seconds) that the bot will display the “typing…” effect before sending this output. It can be a decimal number. If the typing effect is not supported by the messenger it will result in extra delay time. This parameter can be overridden in each output. Defaults to 0.

context

Variables that will be always available at any state of the bot. They are recalculated every time there is an input, so, if a bot definition is changed, previous conversations will have updated variables.

literals

Simple literals


{
    "literals": {
        "text1": "Hello!",
        "text2": "Goodbye my friend!"
    },
    "states": [
        {
            "label": "initial",
            "output": "{{text1}}",
            "next_step": "exit"
        }
    ]
}

Literals for multiple languages

{
    "literals": {
        "text1": {
            "en": ["Hello!", "Hey there!", "Alohaaa"],
            "es": ["Hola!", "Hey que tal!", "Buenaaas"]
        },
        "text2": {
            "en": ["Goodbye my friend!", "bye byee"],
            "es": ["Adiós amigo!", "Hasta luegoo"]
        }
    },
    "defaults": {
        "context": {
            "lang": "en"
        }
    },
    "states": [
        {
            "label": "initial",
            "output": "{{text1[lang] | random}}",
            "next_step": "exit"
        }
    ]
}

When your bot grows you’ll likely want to separate the content of your outputs from the logic of the states. One thing you could do is placing all your texts in a variable in the default context and then use {{my_copys[1]}} to dinamically build your output. While this is perfectly fine for small bots, this is generally a bad idea since the default context is evaluated every time the bot jumps from one state to another, so if you have hundreds or thousands of copys it will slow down your bot. Instead, literals is a static section where you can put as many variables as you want without incurring into a performance hit.

The way you organize your copys is entirely up to you, but we found that following a convention makes it easy to find them and to keep the code consistent even for really big chatbots. The example “Literals for multiple languages” on the right is the convention we recommend following.

session_timeout

Time (in minutes) after the last user interaction to wait before the session is closed automatically. The default is null which means the session will never be closed automatically, only if a state with "next_step": "exit" is reached.

keep_session

A boolean that indicates whether the variables from the context of the previous session should be copied to the new session. Defaults to false.

keep_trace

A boolean that indicates whether the _trace special variable should be kept between different sessions. Defaults to false.

early_typing

A boolean that indicates whether the bot should send a typing indicator as soon as it gets an input from the user. This is usually desirable, so that user gets instant feedback that his message is being processed, however if the bot does not generate an output it results in a weird effect where the typing indicator starts and then ends without any message being sent.

If set to false, the bot will delay sending the typing indicator until it knows for sure there’s some message to send back. This effectively removes the weird effect, but it will delay the typing feedback for some milliseconds.

Defaults to true.

triggers

{
  "triggers": {
    "payload":[
      {
        "match": "^WATCH_VIDEO_(?P<video_id>[a-z0-9-]+)$",
        "context": {
          "video_url": "videos/{{video_id}}.mp4"
        },
        "next_step": "watch_video"
      }
    ],
    "text":[
      {
        "match": "trigger_(?P<id>\\w+)",
        "next_step": "trigger_{{id}}"
      },
      {
        "match": "help",
        "next_step": "trigger_help"
      }
    ]
  }
}

Triggers are commands that are available at any point during the bot execution, regardless of its current state.

Triggers are matched with regular expressions. If the regexp contains named groups, those will be added as variables to the context with the corresponding matched value. When a user input is captured by a trigger, it is not consumed by the current state.

Triggers must have valid next_step and match attributes. If next_step is null the bot will consume that input without any change, which is useful if you want to ignore some words. See next_step reference for more info.

Usually triggers are used to capture button clicks (what Facebook calls 'Postbacks’) and general commands like “help”.

states

This field contains an array of all the states of the bot.

When the bot enters a state, the first thing it does is to update the context, then it writes all the outputs (if any) and finally wait for a user input (if defined). Finally, it jumps to the state defined in the next_step expression.

{
  "version":"1.0",
  "initial_state":"first_state",
  "states": [
    {
      "label": "first_state",
      "input": {
        "type": "free_text",
        "variable": "first_input_from_user"
      },
      "output": {
        "type": "text",
        "data": "data_of_output"
      },
      "next_step": "next_state_in_flow"
    },
    {
      "label": "next_state_in_flow",
      "context": {
        "variable_name": "variable_value"
      },
      "output": [
        {
          "type": "text",
          "data": "data_of_output_in_array"
        },
        {
          "type": "text",
          "data": "another_output_in_array"
        },
        {
          "type": "text",
          "data": "variable_name value: {{variable_name}}"
        }
      ],
      "next_step": "next_state_in_flow"
    }
  ]
}

Default input_failure

{
    "label": "input_failure",
    "output": "input_failure",
    "next_step": "exit"
}

Default external_request_failure

{
    "label": "external_request_failure",
    "output": "external_request_failure",
    "next_step": "exit"
}

Default fallback_instruction

{
    "label": "fallback_instruction",
    "output": "fallback_instruction",
    "next_step": "exit"
}

Default loop_overflow

{
    "label": "loop_overflow",
    "output": "loop_overflow",
    "next_step": "exit"
}

It is required to set some 'initial_state’, that is the first state in the conversation flow. 'initial_state’ will only start after the user sends it’s first input. Then, it will update the context, write the outputs and go to next_step.

label

The state name.

context

(Optional)

Variables to be assigned when entering the state. See context.

output

(Optional)

The bot will send in order all outputs defined here. It has different possible data types.

input

(Optional)

Input of the bot. It can come from the user or as a response to url calls. The field “actions” specifies the expected type of input.

next_step

Name of the next state in the flow of the bot. For a conditional next_step (i.e. jumping to different states depending of variables) see templating.

Implicit states

There are some implicit states that are always defined under the hood. You can always redefine these states in your code in order to customize their default behaviour.

Context

The context is just a set of variables that can contain any JSON type

{
  "context": {
    "user": {
      "name": "John",
      "last_name": "Doe"
    },
    "is_customer": true
  }
}

Context with a forced order of execution

{
  "context": [
    {"first_var": 1},
    {"second_var": "This depends on the {{first_var}}"}
  ]
}

The context is a set of variables that is carried along during the whole session. Everytime the bot transitions to a new state, the context is updated with all the variables defined in the new state context statement. Context variables can then be used to build the outputs dynamically and in other places by using the templating syntax.

Context actions

In this example, 'new_user’ will contain the json response of the call.


{
  "new_user": "{{http('http://my-api.example.com/new_user', params={'q': 'apples'})}}"
}

In this example, the variable 'queues’ will contain an array of names: the queues that are open in Hubtype DESK.


{
  "context": {
    "queues": {
      "action": "active_queues"
    }
  }
}

Here we create a note so the human agent that is going to attend the conversation can quickly see a summary of the customer.


{
  "context": {
    "note": {
      "action": "add_note",
      "text": "User name: {{user.name}}. Age: {{user.age}}. Order number: {{order_num}}."
    }
  }
}

Finally, we create a case in one of the available queues or in the default one if there are none available.


{
  "context": {
    "case": {
      "action": "create_case",
      "queue": "{{available_queues[0] if available_queues | length > 0 else 'Default queue'}}"
    }
  }
}

Context actions are used to interact with the outside world. Probably the most handy action is http, which allows you to make calls to any standard JSON API. Here’s a complete list of available actions:

Action Type
url String
method String (GET, POST, PUT, PATCH, DELETE, HEAD) Optional. Defaults to GET
headers Object Optional
params Object Optional. Query parameters (GET params)
data Object Optional. Form data to send along with the request
json Object Optional. JSON data to send in the body of the request
Action Type
text String Content of the note
Action Type
queue String ID or name of the queue that the case will be created on

Special context variables

There are some special variables in the context that the bot populates automatically when a new session starts or when certain events occur:

User

{
  "user":{
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "John Doe",
    "provider": "facebook | telegram | twitter...",
    "username": "j_doe",
    "provider_id": "12345677"
  }
}

This variable contains the main information about the provider from which the user contacts. It can be useful to prevent sending some kind of output that is not supported on specifics providers.

Organization

{
  "organization": "ACME"
}

This is the name of your organization at Hubtype.

Bot

{
  "bot": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Charlie Bot"
  }
}

Contains a unique identifier set when you created the bot. Also the name of the bot stated in the “name” attribute.

Trace

{
  "_trace":["initial", "welcome_message", "show_products", "buy_item"]
}

List of states for which the user has passed. It can be useful to retrieve behaviour data about your users.

First text

{
  "first_text": "Hey there!"
}

First text message from the user

Last session

{
  "last_session":{
    "last_state": "buy_item",
    "created_at": "2017-10-24T15:05:31.949553Z",
    "last_interaction": {
      "created_at": "2017-01-24T16:05:31.949553Z",
      "_input": "Bye bye!"
    }
  }
}

All information you need about the last time this user contacts the bot.

Last keyboard

{
  "_last_keyboard": [
    {"label": "Yeah", "data": "yes"},
    {"label": "Nope", "data": "no"}
  ]
}

Array with the different options of last shown keyboard

Hubtype case typification and status

{
  "_hubtype_case_typification": "timetable",
  "_hubtype_case_status": "result_ok"
}

The typification and status of a case originated from this bot given by the human agent that resolved the case.

Outputs

Valid output expressions

{
    "output": "This is a simple text"
}
{
    "output": ["This", "is", "a", "sequence", "of", "messages"]
}
{
    "output": {
        "type": "text",
        "delay": 0.5,
        "typing": 1,
        "data": "This is a text in its expanded form"
    }
}

Outputs are the messages that the bot sends to the user when it reaches the current state. Outputs can be defined in a variety of different ways: a simple text string, an object representing the expanded message format (see types of messages in the next section) or an array of messages (either in the simple text format or the expanded object).

All outputs accept the following parameters:

Field Value
delay Number Time (in seconds) to delay sending this output. It can be a decimal number. Defaults to 0.
typing Number Time (in seconds) that the bot will display the “typing…” effect. It can be a decimal number. Defaults to 0.

delay and typing defined in the output will override the global settings in the defaults section.

Next, we will define all the available output types:

Text

{
  "type": "text",
  "data": "Done! 😉"
}
Field Value
type “text”
data String Any UTF-8 encoded string

Image

{
  "type": "image",
  "data": "http://this.is.the/url/of/the/image.jpg"
}
Field Value
type “image”
data String The image URL

Video

{
  "type": "video",
  "data": "http://this.is.the/url/of/the/video.mp4"
}

Field Value
type “video”
data String

Audio

{
  "type": "audio",
  "data": "http://this.is.the/url/of/the/audio.mp3",
  "caption": "Some video subtitle"
}

Field Value
type “audio”
data String
caption String optional

Document

{
  "type": "document",
  "data": "http://this.is.the/url/of/the/document.pdf",
  "caption": "Some document subtitle"
}
Field Value
type “image”
data String
caption String optional

Location

{
  "type": "location",
  "latitude": 41.412255,
  "longitude": 2.2079313,
  "title": "My Home",
  "address": "Hollywood Boulevard 32",
  "url": "http://any.url.com"
}
Field Value
type “location”
latitude Number
longitude Number
title String optional
32 chars max
address String optional
url String optional

Contact

{
  "type": "contact",
  "first_name": "John",
  "last_name": "Doe",
  "phone_number": "678909909",
  "vcard": "vcard:data"                 
}
Field Value
type “contact”
first_name String
last_name String optional
phone_number Number optional
vcard String optional

Buttonmessage

{
  "type": "buttonmessage",
  "text": "How can I help you?",
  "buttons": [
    {
      "type": "postback",
      "title": "Buy pizza",
      "next_step": "buy_pizza"
    },
    {
      "type": "postback",
      "title": "See menu",
      "payload": "SHOW_MENU"
    },
    {
      "type": "web_url",
      "title": "Visit website",
      "url": "https://www.google.es/"
    },
    {
      "type": "phone_number",
      "title": "Call us",
      "payload": "{% raw %}+44 7700 900200{% endraw %}"
    }
  ]
}

A text message followed by a collection of at most 4 buttons displayed vertically.

Button:

Field Value
type “web_url”, “postback” or “phone_number”
title String
webview_height_ratio String Possible in web_url
messenger_extensions String Possible in web_url
fallback_url String Possible in web_url
url String mandatory if type is “web_url”
payload String mandatory if type is “postback” or “phone_number”
next_step String optional substitute of payload when type is “postback”

Carrousel

{
  "type": "carrousel",
  "elements":[
    {
      "title": "Pepperoni",
      "subtitle": "Authentic soft New York style dough topped with tasty pepperoni",
      "image_url": "https://www.cover.image.jpg",
      "buttons": [
        {
          "type": "postback",
          "title": "Add to cart",
          "payload": "ADD_CART_PEPPERONI"
        }
      ]
    },
    {
      "title": "Tandoori Chicken",
      "subtitle": "Succulent chicken, Tandoori spices, fresh tomato and red onion",
      "image_url": "https://www.cover.image.jpg",
      "buttons": [
        {
          "type": "postback",
          "title": "Add to cart",
          "payload": "ADD_CART_TANDOORI"
        }
      ]
    }
  ]
}

A rich interactive message that displays a horizontal scrollable carousel of items, each composed of an image, short description and buttons to request input from the user.

Field Value
type “carrousel”
elements Array of Elements 10 elements max (will truncate)

Element:

Field Value
title String
subtitle String
image_url String
buttons Array of Buttons 3 elements max (will truncate)

List

{
    "type": "list",
    "elements": [{
        "title": "First title",
        "subtitle": "Some subtitle",
        "image_url": "https://www.url/g/image.png",
        "buttons": [{
            "type" : "phone_number",
            "title" : "Call",
            "payload" : "{% raw %}+44 7700 900200{% endraw %}"
        }]
        },
        {
        "title": "Second title",
        "subtitle": "Oh my god",
        "image_url": "https://ToBeOrNot.ToBe.an.url/image.jpg",
        "buttons": [{
            "type": "web_url",
            "title": "Go details",
            "url": "http://www.details.url/#section"

        }]
    }]
}
Field Value
elements Array of Elements min 2 elements
max 4 elements

Media Message

{
  "type": "mediamessage",
  "image": "https://ToBeOrNot.ToBe.an.url/image.jpg",
  "buttons": [
      {
          "type": "web_url",
          "title":"Go to image",
          "url": "http://www.details.es"
      }
  ]
}

{
  "type": "mediamessage",
  "video": "https://randompage.es/video.mp4",
  "buttons": [
      {
          "type": "web_url",
          "title":"Go to Video",
          "url": "http://www.videourl.es"
      }
  ]
}

It’s composed of an image or a video, and followed by a button.

Field Value
image String   
video String
buttons Array of Buttons max of 1 button

Keyboard (Quick Replies)

{
    "label": "select_restaurant_type",
    "output": {
        "type": "text",
        "data": "What's your budget for dinner?",
        "keyboard": [
            {
              "label": "$10 or less",
              "data": "cheap"
            },
            {
              "label": "$15 - $25",
              "data": "medium"
            },
            {
              "label": "$25 or more",
              "data": "expensive"
            }
        ]
    },
    "input": {
        "type": "in_keyboard",
        "variable": "budget"
    },
    "next_step": "show_{{budget.data}}"
}

Location keyboard

{
    "label": "ask_location",
    "output": {
        "data": "Please, share your location with me",
        "keyboard": [
            {
                "location": ""
            }
        ]
    },
    "input": {
        "type": "location",
        "variable": "location"
    },
    "next_step": "show_nearby_shops"
}

Quick Replies are a convenient way to let users know the available options to type. Also, they save the end-user the effort to type all words and let them navigate through options much faster.

Any message type accepts a "keyboard" field that typically consists of an array of ("label", "data") pairs. There’s a special type of keyboard that allows you to request the user for a location. It will display a special button that, when tapped, will prompt a maps view so that the user can share her location.

Receipt

{
  "type": "receipt",
  "recipient_name": "{{user.name}}",
  "order_number": "123",
  "currency": "EUR",
  "payment_method": "Visa",
  "summary": {
    "total_cost": 10.0
  }
}
{
  "type": "receipt",
  "recipient_name": "{{user.name}}",
  "order_number": "1010101010",
  "currency": "EUR",
  "payment_method": "MasterCard",
  "summary": {
    "total_cost": 30.96,
    "subtotal": 27.4,
    "total_tax": 3.56,
    "shipping_cost": 0
  },
  "merchant_name": "Hubtype",
  "timestamp": "1515671650",
  "order_url": "https://hubtype.com/",
  "elements": [
    {
      "title": "item1",
      "price": 3,
      "quantity": 2
    },
    {
      "title": "item2",
      "price": 1,
      "subtitle": "this is a subtitle",
      "currency": "USD"
    },
    {
      "title": "kitten",
      "subtitle": "adorable",
      "quantity": 1,
      "price": 10,
      "currency": "EUR",
      "image_url": "http://i.telegraph.co.uk/multimedia/archive/02830/cat_2830677b.jpg"
    }
  ],
  "address": {
    "street_1": "Street1",
    "city": "city",
    "postal_code": "010101",
    "state": "state",
    "country": "country",
    "street_2": "Street2"
  },
  "adjustments": [
    {
      "name": "Special discount",
      "amount": 10
    }
  ]
}

This output is used to send receipts to users. It can be simple with not much information or very detailed. The user will see a summarised version that will be expanded with all the information when the user click it.

Simple receipt, with minimum information. (code example produces this receipt)

Detailed receipt that uses all possible fields.

Previous receipt expanded when clicked by the user.

Field Value
recipient_name String
merchant_name String Optional
order_number String
currency String
payment_method String
timestamp String Optional
order_url String Optional
elements Array of ReceiptElement Optional
Facebook doesn’t
guarantee the order
address Address Optional
summary Summary
adjustments Array of Adjustment Optional

ReceiptElement

Field Value
title String
subtitle String Optional
quantity Number Optional
price Number Can be 0
currency String Optional
image_url Link Optional

Address

Field Value
street1 String
street2 String Optional
city String
postal_code String
state String
country String

Summary

Field Value
subtotal Number Optional
shipping_cost Number Optional
total_tax Number Optional
total_cost Number

Adjustment

Field Value
name String
amount Number

Input actions

"type" specifies what is the expected action and "data" will be stored in the specified variable. To access the variable see Templating.

Free Text

{
  "type": "free_text",
  "variable": "text_var_name"
}

Accepts any text from the user and stores it in the variable.

Get Integer

{
  "type": "int",
  "variable": "name_of_int_var"
}

Accepts any integer as input , other kinds of inputs are rejected.

Get in Set

{
  "type": "in_set",
  "variable": "name_of_option",
  "action_parameters": ["op1", "op2"]
}

Only accepts these parameters as answer. It will ask at most input_retry times.

Field Value
action_parameters Array of strings

Get in Set Fuzzy

{
  "type": "in_set_fuzzy",
  "variable": "object_user_choice",
  "action_parameters": ["object1", "object2"]
}

Same as get in set, but it does not require a perfect match.

Field Value
action_parameters Array of strings

Get in keyboard

{
  "label": "in_keyboard_example",
  "output": {
    "type": "text",
    "data": "Select one option:",
    "keyboard": [
      {"label": "Item 1", "data": "1"},
      {"label": "Item 2", "data": "2"}
    ]
  },
  "input": {
    "type": "in_keyboard",
    "variable": "key_user_choice"
  },
  "next_step": "another_state"
}

Puts in the variable the key object (label, data) of the key pressed by the user. There must be a keyboard present.

You can access both variables using jinja: {{key_user_choice.label}} and {{key_user_choice.data}}

Get yes or no

{
  "type": "yes_no",
  "variable": "confirm_var_name"
}

The accepted outputs are yes, si, sí and no in lowercase or uppercase.

Get from url

{
  "type": "from_url",
  "variable": "var_name_to_save",
  "action_parameters": {
    "url": "{{BASE_URL}}path/search/",
    "params": {
      "param1": "{{_input}}",
      "param2": "val_2"
    }
  }
}

It makes a call to the url and stores the response json object in the variable name.

Field Value
url Get url
params Array of parameters for the get call

Get name

{
  "type":"name",
  "variables":"confirm_get_name"
}

The user input should be a text with at most 3 words.

Get email

{
  "type": "email",
  "variable": "user_email"
}

Gets the email and check if it is well writed. something@someother

Get age

{
  "type":"age",
  "variable":"age"
}

Saves a number representing the age. Age should match 1 <= age < 120.

Get location

{
  "type":"location",
  "variable":"location"
}

The variable location will contain the components: latitude , longitude, title, address, url. For field info see location.

Get image

{
  "type": "image",
  "variable": "the_image_url"
}

It will save in the variable the url given by the provider.

Templating

Basic templating

{
  "output": "Good morning {{user.name}}"
}

Templating with array expansion

{
  "output": "[{% for t in toppings %}
      {
        'type': 'text',
        'data': 'Topping {{loop.index}}: {{t.name}}',
      },
    {% endfor %}]"
}

Variables stored in the context can be used to construct outputs dynamically, to define new context variables or to determine the next_step on runtime.

We use Jinja templating framework under the hood, we encourage you to check out their docs to learn all its features.

Usually, templating engines transform text strings into new text strings, however, BotSON templating engine is able to expland strings into more complex structures like arrays or objects.

Filters

{
  "context": {
    "quote": "Ceci n'est pas une pipe. Les Deux Mystères."
  },
  "output": [
    "{{quote | slugify}} == ceci-nest-pas-une-pipe-les-deux-mysteres",
    "{{quote | type}} == <class 'str'>",
    "{{quote | escape_quotes}} == Ceci n\\'est pas une pipe. Les Deux Mystères.",
    "{{quote | ascii}} == Ceci n'est pas une pipe."
  ]
}

Filters are used to modify the variables inside your template and are expressed with a pipe symbol (|). Multiple filters can be chained, the output of one filter is applied to the next. You can use all the builtin filters supported by Jinja like join, length or random. Checkout the full list here.

Additionally, BotSON defines the following custom filters:

Filter
slugify Returns the slug of any string
type Returns the type of a variable
escape_quotes Escapes single quotes
ascii Returns the closest ascii codification of any unicode string

Dates

{
    "context": {
        "a1": "{{date()}}",
        "a2": "{{date('tomorrow')}}",
        "a3": "{{date('1984/05/05')}}",
        "a4": "{{date('now', 'Europe/Paris')}}"
    },
    "output": [
        "{{date(a1) - date() < date_interval(minutes=1)}} == True",
        "{{date(a2).start_of('week') < date()}} == True",
        "{{date(a3).year}} == 1984",
        "{{date(a4).to_datetime_string()}} == current time in Paris"
    ]
}

It’s possible to get the current time and operate with dates and time intervals using the functions date and date_interval. Under the hood we use the Pendulum library, we encourage you to check out their documentation to learn all the options available.

Comments

{
    "type": "carrousel", #This is a botSON comment
    "elements": "[
        {% for item in full_items %}
            {
                {# This is a JINJA comment #}
                'title': '{{item.name}}',
                'subtitle': '{{item.priceLabel}}',
                'image_url': '{{item.image.sizes.Best.url}}', 
                'buttons':[
                    {
                        'type': 'web_url', 
                        'title':'Open product',
                        'url': '{{item.clickUrl}}'
                    }
                ]
            },
        {% endfor %}
    ]"
}

We allow two types of comments. You can make a simple comment in the JSON code with a # followed by your comment, or if you want to put a comment inside a jinja template, it must be done with this syntaxis: {# your comment #}

Error types

There may be errors during the parsing and execution of a bot. They are of different types inheriting from BotException, and are mainly classified as JSON or BotSON related.

BotException
JSONParseException Raises when the JSON is invalid
BotSONException Raises otherwise

JSONParseException

Attributes Contains
message Reason why the json is invalid
line Line of the problem in the json
column Column of the problem in the json

BotSONException

Types
BotSONParseException Raises during parsing
BotSONExecException Raises during execution
Attributes Contains
message Explanation of the error
trace Approximate path to the error in the json. Note: in states and trigger arrays it will use label/match value instead of index number

Jobs

TODO

Integrations

AI/NLP

The ai_backends attribute allows you to easily integrate 3rd party services like Google’s Dialogflow (former API.ai) or IBM Watson to add natural language understanding capabilities to your bot. Usually you’ll want to pass the input from the user to the AI/NLP service and get an intent back, then use that intent to navigate to a particular state of your bot.

You must create an attribute named after the service you want to integrate, this attribute should contain an object with the credentials needed to talk to that service.

{
  "ai_backends": {
    "watson": {
      "username": "<WATSON_USERNAME>",
      "password": "<WATSON_PASSWORD>",
      "workspace_id": "<WATSON_WORKSPACE_ID>"
    },
    "api_ai": {
      "token": "<API.AI_TOKEN>"
    }
  }
}

Get the user intent using AI and fetch an API. Here we assume our AI backend has a couple of intents defined: “search” and “buy”, and 1 entity: “item”.

AI calls

[
    {
        "label": "search_menu",
        "output": "What do you want to do?",
        "input": {
            "type": "intent",
            "variable": "ai_result"
        },
        "next_step": "{{ai_result.intent}}"
    },
    {
        "label": "search",
        "context": {
            "search_result": {
                "url": "http://my-api.my-company.com/search",
                "params": { "query": "{{ai_result.item}}" }
            }
        },
        "output": "I've searched for {{ai_result.item}} and found {{search_result | length}} results.",
        "next_step": "exit"
    },
    {
        "label": "buy",
        "output": "Are you sure you want to buy {{ai_result.item}}?",
        "next_step": "exit"
    }
]

Manually invoke the AI backend with an arbitrary input

{
  "context": {
    "ai_result": "{{ai_conversation(ai_backends, 'hello world')}}"
  }
}

You are able to use watson or api.ai bots to manage the botSON flow.

To start just configure the credentials

Then you can call the ai in two different ways.

The object return has intent, entities. The object saved in the variable will have the following fields:

Field Value
intent String
entities String
output_text String Verbose response of the AI in that intent
raw Object Raw response from the AI backend

Available integrations:

Service Availability Credentials
Dialogflow Available {token}
Watson Available {username, password, workspace_id}
Wit.ai Coming Soon

Analytics

{
    "defaults": {
        "context": {
            "analytics": {
                "data": {
                    "my_id": "123" # This will be sent with all default events
                },
                "splunk": {
                    "url": "SPLUNK_INSTANCE_URL",
                    "token": "AUTH_TOKEN"
                }
            }
        }
    },
    "states": [
        ...
        {
            "label": "buy_item",
            "context": [
                {"_": "{{track(analytics, {'event': 'buy_item'})}}"}
            ],
            "output": "You have just bought an item",
            "next_step": "exit"
        }
    ]
}

The analytics attribute allows you to easily integrate 3rd party services like Mixpanel or Google Analytics to track events of your bot. You can use this to measure the activity of your bot’s users and create different reports like retention or funnels.

You must create an attribute named after each of the services you want to integrate, this attribute should contain an object with the credentials needed to talk to that service. Then BotSON will automatically track several default events:

Event Data Description
user_message BotSON encoded message A message sent by an end-user
bot_mesage BotSON encoded message A message sent by the bot
bot_state State label The bot moved to a new state

You can set the attribute `defaults.analytics.data` in order to share data with all the events sent

Track your own events using the context function `track(analytics, data)`, where `data` is an object with arbitrary parameters that will be merged with the default `analytics.data`

Available integrations:

Service Availability Credentials
Splunk Available {url, token}
Mixpanel Coming Soon
Google Analytics Coming Soon