Skip to main content

Turn Context

info

This context api is currently versioned as 1.0.0-alpha. This API is still subject to change as the use cases for it crystalise.

The Turn context automatically surfaces information specific to the ongoing conversation from integrated systems via the integrations API. The Turn context is not stored, it is a real time system. As conversations unfold, integrations are polled for supplementary information on the conversation members which may be relevant to support the conversation's goals. This information is displayed in the Turn in realtime.

Turn Integration Library

We've released a Javascript library to help with the development of Turn integrations.

It is available on GitHub at turnhub/turnio-integration or you can install it with

$ npm install @turnio/integration
$ yarn install @turnio/integration

Below is a sample integration that does the following:

  1. Exposes a table and an ordered list in the context details panel.
  2. Suggests password reset content in the reply box
  3. Exposes an menu item that allows one to change a language.
info

Integrations built with @turnio/integration can be run as serverless cloud functions on platforms like Google Cloud Functions and similar.

const TurnIntegration = require("@turnio/integration");

const app = new TurnIntegration(process.env.SECRET)
.context("Language", "table", ({ chat, messages }) => ({
Language: "English",
Confidence: "Very high",
}))
.context("A list of things", "ordered-list", ({ chat, messages }) => ["first item", "second item", "third item"])
.suggest(({ chat, messages }) => [
{
type: "TEXT",
title: "Password reset",
body: "To reset your password click the link on the login page.",
confidence: 0.4,
},
])
.action(({ chat, messages }) => [
{
description: "Change Language",
payload: {
really: "yes",
},
options: {
afr_ZA: "Afrikaans",
eng_ZA: "English",
zul_ZA: "Zulu",
},
callback: ({ message, option, payload: { really } }, resp) => {
console.log({ message, option, really });
// Notify the frontend to refresh the context by setting
// the response header
resp.setHeader("X-Turn-Integration-Refresh", "true");
// this is return as JSON in the HTTP response
return { ok: "done" };
},
},
])
.serve();

const port = process.env.PORT || 3000;

app.listen(port, () => console.log(`Example app listening on port ${port}!`));

Handshake

{
"version": "1.0.0-alpha",
"capabilities": {
"actions": True,
"suggested_responses": True,
"context_objects": [
{
"title": "The title",
"code": "details",
"type": "table"
}
]
}
}

The handshake needs to be performed after the integration is configured. This is a call to the context url with a handshake query parameter set to true.

Turn context objects can be one of two types, either an ordered-list or a table. The type key is used to identify them.

Every context object has the following keys:

  • title, the title of the context block (i.e. Language or Patient Information)
  • type, either ordered-list or table
  • code, a unique identifier for this content. This is used to be able to refer to the block when needing to refresh a piece of information in the UI.

Endpoints need to supply a context_objects key which contains a key for each context object configured by the handshake. The order of the context objects in the context panel is determined by the order they are supplied in the handshake.

A limited subset of Markdown is supported in rendering the values passed as context. The following types are supported:

Other Markup features like images, headings and lists are not supported.

Retrieving the context, actions and suggestions

When a conversation loads in the user interface, the browser will initiate a request to the integration requesting an update.

The integration will hit your integration url with an HTTP POST request with the following payload:

{
"chat": {
"owner": "<the phone number>",
"state": "<the state of the chat>",
...
},
"messages": [{
// the 10 most recent inbound and outbound messages
// that are part of this conversation
}]
}

Context Updates

When receiving the HTTP POST request, you can supply context object updates under the context_objects key as per the formats below.

Ordered lists

{
"version": "1.0.0-alpha",
"context_objects": {
"details": ["first time", "*second item*", "~third item~"]
},
"actions": {}
}

Ordered lists have a list as a payload. They're automatically prefixed with numbers, starting with 1. They do not support any kind of nesting. The key inside context_objects must match the code in your handshake return.

Tables

{
"version": "1.0.0-alpha",
"context_objects": {
"details": {
"Language": "[Tagalog](https://en.wikipedia.org/wiki/Tagalog_language)",
"Confidence": "Very High"
}
},
"actions": {}
}

Tables have a dictionary as a payload. The dictionary key is printed in bold text, the dictionary value is printed as normal. The key inside context_objects must match the code in your handshake return.

Suggested Responses

When receiving the HTTP POST request, you can supply suggested responses under the suggested_responses key as per the format below.

{
"version": "1.0.0-alpha",
"context_objects": {},
"actions": {},
"suggested_responses": [
{
"type": "TEXT",
"title": "Password Reset",
"body": "To reset your password click the link on the login page.",
"confidence": 0.4
}
]
}

Suggested responses can be specified and will be displayed in a select menu above the response textbox. They will be ordered by confidence

Suggested responses have the following keys:

  • type, the type of response
  • title, the title of the response
  • body, the body of the response
  • confidence, confidence score of this response, used for ordering the list

Actions

When receiving the HTTP POST request, you can supply custom actions under the actions key as per the format below.

{
"version": "1.0.0-alpha",
"context_objects": {},
"actions": {
"change_language": {
"description": "Change Language",
"url": "/api/v1/turn/action",
"payload": {
"really": "yes"
},
"options": {
"afr_ZA": "Afrikaans",
"eng_ZA": "English",
"zul_ZA": "Zulu"
}
}
}
}

Actions can be specified and will be displayed in a dropdown above the context. They will POST the payload to the url specified on the configured integration when clicked.

Actions have the following keys:

  • description, the text in the dropdown
  • url, path of the integration action
  • payload, payload that will be posted
  • options, (optional) will create a nested menu and add a option key to the payload with the selected option.

Example callback JSON payload

{
"address": "+16315551003",
"integration_uuid": "the-uuid-of-the-integration",
"integration_action_uuid": "the-uuid-of-the-action",
"message": {
// the message that was used to trigger this action
},
"option": "afr_ZA",
"payload": {
"really": "yes"
}
}

Refreshing the context and actions

There's a good chance you would want to have the UI refresh the available context and actions after having completed an action.

Immediate refresh

One can force an immediate refresh of the context and actions by returning the X-Turn-Integration-Refresh header with a value of true.

There's an example Turn Integration Demo app that shows how to achieve this. The demo application exposes an opt-in or opt-out action depending on the state. Once opted in the context is refreshed to show the new state and the action is refreshed to display only an opt-out option.

Delayed refresh

Using the integration_uuid and the integration_action_uuid one can make an HTTP request to the integration events endpoint. This will trigger Turn to poll your integration and refresh the UI based on the information returned.

Hitting the integration events endpoint

$ curl -X POST "https://whatsapp.turn.io/api/integrations/<your-integration_uuid>/notify/finish" \
-H "Content-Type: application/json" \
-d '{
"integration_action_uuid": "<your-integration-action-uuid>"
}'