Tutorial: Easily integrating with data sources in a Twyla chatbot

Paul Gibbins
Twyla AI
Published in
6 min readOct 23, 2019

The following tutorial covers:

  • Creating and deploying a simple RESTful middleware using ZEIT, Python and GitHub.
  • Formatting returned data to display as rich content (Facebook Messenger, Twyla Web Widget).

Introduction

When building a chatbot conversation project, a conversation designer might want to enrich or personalise the end user’s experience with contextual information provided by 3rd party services or data sources. These may also be services internal to an organisation, like customer databases, knowledge bases, order tracking services or even a booking management service. We at Twyla, endeavour to making these capabilities avalable to the Conversation Designer as seamlessly as possible. Out of the box we provide integrations to services like Salesforce Service Cloud, Salesforce Live Agent, Freshchat, Zendesk, Sendgrid etc. to name a few.

We also enable conversation projects to make use of RESTful HTTP APIs as a first class integration. For now we only support JSON payloads only (request and responses), but we may in the future support other payload formats and various other mechanisms. This means that anyone can build a simple integration that effortlessly integrates into your conversational experiences.

In this post, we do not want to talk about the core integration per se, as it is pretty boring, and we intend to cover the configuration and use of it in another post (it is as simple as a few clicks). Instead, here, we want to provide a simple tutorial wherein you can deploy a middleware service on ZEIT Now (it can be hosted anywhere really) that will talk to a 3rd party service and return with a response your your chatbot can process, including rich content like Facebook Messenger buttons, quick replies etc. This allows for complex custom logic to be incorporated into your project’s conversation flows.

What are we assuming in this tutorial?

  1. You have a GitHub account and are familiar with using it.
  2. You know how to write basic Python code.
  3. You have basic understand of how RESTful APIs are built, hosted and accessed.
  4. You are using Twyla as your chatbot platform.

What do you need before you get started?

  • Go over to https://zeit.co/ to get an account if you do not already have one. This is where we will deploy the code and your API hosted.
  • A GitHub account.

What you need to know?

  • Once the repository you create is linked, everytime you commit code, ZEIT Now deploys it to a location that is publically accessible (if one knows where to look).
  • The service is stateless. This means, you cannot store something in a variable and expect it to be available for subsequent requests.

Deploying the sample code

In order for you to hit the ground running we have made available a template repository that you can use to create your new repository. Refer to the “Creating a repository from a template” article from GitHub for more information on using it.

Create project repository

Create a new repository from the template repository as described here.

Make use of the “Use this template” button.
Give your repository a name, “supreme-fiesta” works too!

Create ZEIT Now project

Once you have created a project repository. You are now ready to connect this to your ZEIT Now account. Refer to their documentation for more information on how to do this.

Import your GitHub Project
Your imported project page.

Testing

If everything went according to plan, you should see the deployment status change to “Ready” and domains appear. If not, you can debug your build by following the URL under the “Deployment” column.

Once the deployment is ready, you can visit any of the domains to be greeted by this message.

{"payload":"Hello there, I am up and running."}

Payload Format

You will notice that the response payload is JSON and follows the format required for the correct rendering in your conversation on the various channels supported by your Twyla Conversation Project.

You can refer to the documentation of our Twyla Chat Templates project for information on how the various templates like Quick Replies, Buttons etc. can be used seamlessly with your middleware.

The repository template shows an example of using the Button template when handling a 404 response.

@app.exception_handler(404)
async def not_found(request: Request, exc: HTTPException) -> JSONResponse:
buttons = Buttons(
text="These are not the droids you are looking for. What would you like to do?"
)
buttons.add(UrlButton(title="Visit Canvas", url="https://canvas.twyla.ai"))
buttons.add(UrlButton(title="Visit Google", url="https://google.com"))
return JSONResponse(buttons.asdict(), status_code=exc.status_code)

Integration middleware for your TODO service

Let’s say you are building a conversational interface to your awesome TODO application. The Conversation Designer would like to be able to list the first few items upto 3 on the list as numbered buttons, that when pressed will display the corresponding content.

To break that down to interactions with the middleware:

  1. A request is made to generate a numbered button emissions, with a limit of 3 items.
  2. A button emission is returned.
  3. A request is made with the item the user wishes to retrive.
  4. A text emission is returned.

For the purposes of this tutorial, we will use the JSONPlaceholder Service as an example third-party serivce. A completed version of this tutorial is available here in case you need it.

Before you proceed, you will have to add aiohttp package to the requirements.txt file and update your development environment. You can now import the package in your app.py file.

import aiohttp

Once this is done, you simply have to add the required endpoints as shown below. The first part, is to list the first three TODO items as a Button template with a “post-back” that will let the chatbot know that the user wants to retrieve the selected item.

@app.route("/api/todo")
async def todo_buttons(_: Request) -> JSONResponse:
async with aiohttp.ClientSession() as client:
async with client.get("https://jsonplaceholder.typicode.com/todos") as resp:
data = await resp.json()
buttons = Buttons(text="Select Item")
for item in data[: min(len(data), 3)]:
i = item.get("id")
buttons.add(PostBackButton(title=f"Item {i}", payload=f"x_todo_item_{i}_x"))
return JSONResponse(buttons.asdict())

For more information on the button templates, see the Twyla Chat Templates project.

Once this is done, you can implement the endpoint required to retrieve the TODO item as requested by the user like shown below.

@app.route("/api/todo/{item}")
async def todo_item(request: Request) -> JSONResponse:
item = request.path_params.get("item")
async with aiohttp.ClientSession() as client:
async with client.get(
f"https://jsonplaceholder.typicode.com/todos/{item}"
) as resp:
data = await resp.json()
return JSONResponse(TextTemplate(payload=data.get("title")))

You can test these endpoints locally after making the required changes, by executing python api/app.py from within your python virtual environment. This will start a server locally at http://127.0.0.1:8000.

These changes, including your requirements.txt file can now be committed to your project repository. This should trigger a new Zeit build which once complete should return the expected outputs.

All that is left now, is to connect this into your conversation flow on Twyla Canvas.

A word about security

Out of the box, we do not enable any security measures. It is recommended, if dealing with otherwise non-public data, that you consider either proxying your Authorization header received from the client (Twyla’s HTTP Integration Service) to your backend service/data-source, or make use of a Starlette Middleware to handle authentication and authorisation. You can also rely on third-party services like Auth0 (see Authlib project).

If environment variables and or secrets need to be configured refer to the ZEIT Documentation.

Within the context of a conversation, each session can in theory request a JWT bearer token (this can be configured in your conversation project on Twyla Cavas) which can then be passed to the middleware via a custom header or the Authorization header and this in turn can be forwarded to the third-party service or data source as required.

--

--