Messaging protocol & Bot side API's
Synchronous and asynchronous channels
Depending on channel specifics, some of them requires to gather all responses synchronously. The good example is Google assistant.
- synchronous channels - requires replies in a webhook's response body and has limited or none support for asynchronous replies
- asynchronous channels - allows replies in a webhook's response body and supports asynchronous replies
Bot Applications webhook
Application's webhook API
POST
/bot
- bot application webhookOrchestrator uses this API to notify bot about new messaging events
Webhook data
The request can contain more messaging events because of optimization of execution - it lowers a number of sent requests.
entry
: (object[]) - list of messaging event groupsentry.id
: (string) - ID of messaging group[entry.requires_response]
: (boolean) - if true, there's no way to respond the conversation asynchronously using the /send APIentry.app_id
: (string) - application to which events are sent toentry.messaging
: (object[]) - list of messaging events direcly passed to applicationentry.standby
: (object[]) - list of messaging events passed to application in standby mode
There are either messaging
or standby
events. Never both in same time.
{"entry":[{"id":"<CHANNEL_ID>","requires_response": true, // optional"app_id": "1234","messaging": [ // or "standby"...list of messaging events at specific channel]}]}
Event sent to a bot application
sender.id
: (string) - identification of the senderrecipient.id
: (string) - identifies a channeltimestamp
: (number) - when the event was emitted (taken from the original event)mid
: (string) - unique identification of the event (given by the orchestrator)features
: (string[]) - supported channel features (text
,ssml
,voice
,phrases
,tracking
)
{"sender": {"id": "<USER_ID>"},"recipient": {"id": "<CHANNEL_ID>"},"timestamp": 1458692752478,"mid": "<unique event identification>","features": ["text"]...event contents}
Responding to webhook's event
If the channel requires to respond synchronously (the requires_response
is set to true
), the bot's webhook
has to send responses back to the orchestrator immediatelly.
- for each
entry
object from webhook there should be a corresponding object in response - each messaging event could have corresponding response object between responses
- messaging events in a single response are processed sequentionally - so this makes able to insert delays between each response
{"entry": [{"id": "<CHANNEL_ID>","responses": [{"response_to_mid": "<original message id>","messaging": [...list of responses]}]}]}
Sending responses to Orchestrator's API
At channels, that supports asynchronous communication, there's possibility to send responses also after dispatching the webhook's event.
- POST
/webhook/api
- when acting as an application - POST
/webhook/api/<CHANNEL_ID>
- when acting as a channel
Event sent by bot as a response
response_to_mid
: (string) - identifies the source messaging event for aggregation purposesexpected
: (object) - optional channel guidace for expected user responseexpected.input
: (object) - input type guidanceexpected.input.type
: (string) - type of the inpupt (password
,none
,upload
)expected.phrases
: (string[]) - list of expected phrases (when supported by voice channel)expected.entities
: (string[]) - list of expected entities (when supported by voice channel)
{"recipient":{"id":"<USER_ID>"},"sender":{"id":"<CHANNEL_ID>"},"response_to_mid": "source event identification","expected": { // optional"input": {"type": "password"}}...response contents}
Messaging event types
Text message
message.text
: (string) - required text message
incomming event
{"sender":{"id":"<USER_ID>"},"recipient":{"id":"<CHANNEL_ID>"},"timestamp":1458692752478,"mid": "<unique event identification>","message":{"text":"hello, world!"}}
responding to user
{"recipient":{"id":"<USER_ID>"},"sender":{"id":"<CHANNEL_ID>"},"response_to_mid": "source event identification","message":{"text":"hello, world!"}}
Responding with an alternative content for voice
incomming event: not available
responding to user
message.voice
: (object) - optinal alternative for voice interface[message.voice.voice]
: (string) - voice name[message.voice.language]
: (string) - voice language[message.voice.ssml]
: (string) - optional SSML for providing better voice experience
Speech object can be attached to any message
response type.
{"recipient":{"id":"<USER_ID>"},"sender":{"id":"<CHANNEL_ID>"},"response_to_mid": "source event identification","message":{"text":"hello, world!","voice": {"ssml": "SSML alternative", // optional"voice": "cs-CZ-AntoninNeural"}}}
Quick reply suggestions
message.text
: (string) - required text message
incomming event
text
(string) - the text of selected quick reply suggestionquick_reply.payload
(string) - the content of quick reply
{"sender":{"id":"<USER_ID>"},"recipient":{"id":"<CHANNEL_ID>"},"timestamp":1458692752478,"mid": "<unique event identification>","message":{"text":"hello, world!","quick_reply": {"payload": "<DEVELOPER DEFINED PAYLOAD>"}}}
responding to user
text
(string) - text message the suggestions are bound toquick_replies
(object[]) - list of quick repliesquick_replies[].content_type
(enum) - place for alternative suggestion typestext
- textual quick reply types
quick_replies[].title
(string) - suggestion titlequick_replies[].payload
(string) - bot payload helps to identify the chosen suggestion
{"sender":{"id":"<CHANNEL_ID>"},"responder":{"id":"<USER_ID>"},"response_to_mid": "source event identification","message":{"text":"hello, world!","quick_replies": [{"content_type": "text","title": "<SUGGESTION CAPTION>","payload": "<DEVELOPER_DEFINED_PAYLOAD>"}]}}
Incomming intent
message.intent
: (object) - requiredmessage.intent.intent
: (string|string[]) - the intent[message.intent.score]
: (number) - detected intent score (0.0 - 1.0)[message.intent.entities]
: (object[]) - optional array of entitiesmessage.intent.entities.entity
: (string) - detected entity namemessage.intent.entities.value
: (string) - detected entity value[message.intent.entities.score]
: (number) - detected entity score (0.0 - 1.0)[message.text]
: (string) - optional text uterance
incomming event
{"sender": {"id":"<USER_ID>"},"recipient": {"id":"<CHANNEL_ID>"},"timestamp":1458692752478,"mid": "<unique event identification>","message": {"intent": {"intent": "detected-intent","entities": [ // optional{ "entity": "entity-name", "value": "entity-value" }]},"text":"hello, world!" // optional}}
responding to user: not available
Postback event
postback.payload
(string) - requiredpostback.title
(string) - optional button title, or call to actionpostback.target_app_id
(string,enum) - event is targetted for a special appPRIMARY
- the primary application of a channelOWNER
- thread owner (default)
incomming event
The postback is a background event. It could be visible, if there is a title
attribute.
{"sender": {"id": "<USER_ID>"},"recipient":{"id": "<CHANNEL_ID>"},"timestamp": 1458692752478,"mid": "<unique event identification>","postback":{"payload": "<USER_DEFINED_PAYLOAD>","title": "<OPTIONAL_CTA_TITLE>","target_app_id": "<OPTIONAL_TARGET_APP>"}}
responding to user
{"sender":{"id":"<USER_ID>"},"recipient":{"id":"<CHANNEL_ID>"},"response_to_mid": "source event identification","postback":{"payload": "<USER_DEFINED_PAYLOAD>",}}
Attachments
Attachment: media (audio, video, image, file)
incomming event
message.attachments
(object[]) - attachment typemessage.attachments[].type
(enum) - attachment typeaudio
video
image
file
message.attachments[].payload.url
(string) - url of media content
{"sender":{"id":"<USER_ID>"},"recipient":{"id":"<CHANNEL_ID>"},"timestamp":1458692752478,"mid": "<unique event identification>","message":{"attachments": [{"type":"image","payload":{"url": "<MEDIA URL>"}}]}}
responding to user
message.attachment.type
(enum) - attachment typeaudio
video
image
file
template
- has different format of properties
message.attachment.payload.url
(string) - url to media contentmessage.attachment.payload.is_reusable
(string) - optionally tells the channel adapter to cache the content
{"sender":{"id":"<USER_ID>"},"recipient":{"id":"<CHANNEL_ID>"},"response_to_mid": "source event identification","message":{"attachment":{"type":"image","payload":{"url":"<MEDIA URL>","is_reusable":true // optional}}}}
Button (element description)
type
(enum) - button typepostback
web_url
url
(string) - link forweb_url
buttonpayload
(string) - developer defined payload forpostback
button
URL button
Opens a web page.
{"type": "web_url","url": "<TARGET URL>","title": "<VISIBLE CAPTION>"}
Postback button
Sends a postback to the bot, that created the button regardless who's the current thread owner.
{"type": "postback","payload": "<DEVELOPER DEFINED PAYLOAD>","title": "<VISIBLE CAPTION>"}
Attachment: template
message.attachment.type
(enum) - attachment typetemplate
message.attachment.payload.template_type
(enum)button
- for the button templategeneric
- for the carousel of cards
incomming event: not available
sending a button template
message.attachment.payload.text
(string) - text above buttonsmessage.attachment.payload.buttons
(Button[]) - list of buttons (1-3)
{"recipient":{"id":"<ID>"},"message":{"attachment":{"type":"template","payload":{"template_type":"button","text":"<TEXT ABOVE BUTTONS>","buttons":[{"type": "postback","payload": "<DEVELOPER DEFINED PAYLOAD>","title": "<VISIBLE CAPTION>"}]}}}}
sending a carousel of cards (generic template)
message.attachment.payload.elements
(object[]) - list of cardsmessage.attachment.payload.elements[].title
(string) - card titlemessage.attachment.payload.elements[].subtitle
(string) - optional card subtitlemessage.attachment.payload.elements[].buttons
(Button[]) - optional buttons (0-3)
{"recipient":{"id":"<ID>"},"message":{"attachment":{"type":"template","payload":{"template_type":"generic","elements":[{"title":"<REQUIRED CARD TITLE>","image_url":"<IMAGE URL>", // optional"subtitle":"<SUBTITLE TEXT>", // optional"buttons":[ // optional{"type": "postback","payload": "<DEVELOPER DEFINED PAYLOAD>","title": "<VISIBLE CAPTION>"}]}]}}}}
Handover protocol
The protocol allows do orchestrate the conversation and makes bot able to decide, which skill should handle the conversation.
Pass thread - transfering the context to another application
passing a thread
target_app_id
(string,enum) - application to pass the thread toPRIMARY
- the primary application of a channel
metadata
(string) - optional metadata to pass within the pass thread event
{"sender":{"id":"<USER_ID>"},"recipient":{"id":"<CHANNEL_ID>"},"response_to_mid": "source event identification","target_app_id": "<TARGET_APP>","metadata": "<OPTIONAL_STRING_PAYLOAD>"}
incomming event
The postback is a background event. It could be visible, if there is a title
attribute.
pass_thread_control.new_owner_app_id
(string) - identifier of the target apppass_thread_control.previous_owner_app_id
(string) - id of the last thread owner apppass_thread_control.metadata
(string) - optional metadata from the original eventcontext
(object) - shared context if the app is subscribed to it's updatescontext.timestamp
(number) - unix timestamp of the last modification of shared context
{"sender":{"id":"<USER_ID>"},"recipient":{"id":"<CHANNEL_ID>"},"timestamp":1458692752478,"mid": "<unique event identification>","pass_thread_control":{"new_owner_app_id": "<TARGET_APP_ID>","previous_owner_app_id": "<PREVIOUS_APP_ID>","metadata": "<OPTIONAL_STRING_PAYLOAD>"},"context": {"timestamp": 129392434398// if the app is subscribed to shared context updates}}
Passing thread with text
To make the target application able to answer the incomming event it's possible to include a previous text event from the user.
message.text
: (string) - text uterance
passing a thread
{..."target_app_id": "<TARGET_APP>","message":{"text": "<TEXT_TO_BE_PROCESSED_WITH_TARGET_APP>"}}
incomming event
{..."pass_thread_control":{"new_owner_app_id": "<TARGET_APP_ID>","previous_owner_app_id": "<PREVIOUS_APP_ID>"},"message":{"text": "<TEXT_TO_BE_PROCESSED_WITH_TARGET_APP>"}}
Passing thead with an intent
It's also possible to to pass the thread with an intent
message.intent
: (string) - required[message.entities]
: (object[]) - optional array of entitiesmessage.entities.entity
: (string) - detected entity namemessage.entities.value
: (string) - detected entity value[message.entities.score]
: (number) - detected entity confidence (0.0 - 1.0)[message.text]
: (string) - optional text uterance
passing a thread
{..."target_app_id": "<TARGET_APP>","message":{"intent":"received-intent","entities": [ // optional{ "entity": "entity-name", "value": "entity-value" }],"text":"hello, world!" // optional}}
incomming event
{..."pass_thread_control":{"new_owner_app_id": "<TARGET_APP_ID>","previous_owner_app_id": "<PREVIOUS_APP_ID>"},"message":{"intent":"received-intent","entities": [{ "entity": "entity-name", "value": "entity-value" }],"text":"hello, world!"}}
Passing the thread to trigger the specific action or dialogue
To be able to pass the thread to a specific dialogue of a specific bot, a postback can be bundled in pass thread event. For example, it's useful when passing a thread to human agent.
postback.payload
(string) - required - will be passed to target application
incomming event
The postback is a background event. It could be visible, if there is a title
attribute.
{..."target_app_id": "<TARGET_APP>","postback":{"payload": "<USER_DEFINED_PAYLOAD>"}}
incomming event
{..."pass_thread_control":{"new_owner_app_id": "<TARGET_APP_ID>","previous_owner_app_id": "<PREVIOUS_APP_ID>"},"postback":{"payload": "<USER_DEFINED_PAYLOAD>"}}
Sharing the conversation context between skills
Setting the context and listening to updates
Each conversation has an own key-value store of shared data.
setting a shared context
The shared context is changed just when the following request is received by orchestrator.
set_context
(object) - keys and values to be changedset_context.<KEY>
(any) - the changed value
{"sender":{"id":"<USER_ID>"},"recipient":{"id":"<CHANNEL_ID>"},"set_context": {"<KEY>": "<VALUE>"}}
getting notified
The application will get notified, if
- a prop, the app is subrcibed to, has been changed
- the app is not an emmiter of the change
There's only one object to be received
set_context
(object) - new context of the conversation
{"sender":{"id":"<USER_ID>"},"recipient":{"id":"<CHANNEL_ID>"},"set_context": {"<KEY>": "<VALUE>"}}
When handover event occurs, the target application will receive the whole context wihin a
context
object
Tracking protocol
The protocol allows to attach usefull informations from dialogue platform to source conversation.
Attaching tracking events to the bot response
In ideal case, a tracking event should be sent after all conversation response events.
tracking
(object) - tracking object containertracking.events[].type
(enum:log,report,conversation,audit,user) - category of the tracking eventtracking.events[].category
(string) - category of the tracking eventtracking.events[].label
(string) - optional label of the tracking eventtracking.events[].value
(number) - optional numeric value of the tracking event - 0 by defaulttracking.events[].payload
(object) - optional unstructured data related to tracking eventtracking.meta
(object) - optional metadata related to response eventstracking.meta.actions
(string[]) - list of dispatched actionstracking.meta.intent
(string) - detected intenttracking.meta.confidence
(number) - used confidence thresholdtracking.meta.intents
({ intent: string, score number }) - detailed intent detection breakdowntracking.meta.entities
({ entity: string, value: any, score number }) - detected entities
{..."response_to_mid": "abc123","tracking": {"events": [ // optional{"type": "","category": "<TRACKING_CATEGORY>","label": "<TRACKING_LABEL>", // optional"value": 1, // optional"payload": {} // optional}],"meta": {"actions": ["start"],"intent": null,"confidence": 0.86}}}
Other events
Sender action event (typing)
This event enhances a text channel conversation.
sender_action
: (enum) - optinal alternative for voice interfacetyping_on
- show typing indicatortyping_off
- hide a typing indicator
incomming event
{"recipient": {"id": "<CHANNEL_ID>"},"sender": {"id": "<USER_ID>"},"response_to_mid": "source event identification","sender_action": "typing_off"}
responding to user
{"recipient": {"id": "<USER_ID>"},"sender": {"id": "<CHANNEL_ID>"},"response_to_mid": "source event identification","sender_action": "typing_on"}
Wait event - for synchronous responses
This event helps to make a pause between visual messaging events at synchronous response mode.
incomming event: not available
responding to user
the event is ignored in asynchronous communication
wait
: (number) - number of milisecods to wait
{"recipient": {"id": "<USER_ID>"},"sender": {"id": "<CHANNEL_ID>"},"response_to_mid": "source event identification","wait": 1000}
Application subscribtion
Each app can be subscribed to following events
messages
(boolean) - text messages and quick replieshandovers
(boolean) - handover eventspostbacks
(boolean) - postbackssenderActions
(boolean) - client side sender actionscontextUpdates
(boolean) - subscribe to context updatesstandbyIncoming
(boolean) - the app will receive all standby messages from pagestandbyOutgoing
(boolean) - the app will receive all standby messages from applicationstracking
(boolean) - the app will receive all tracking events sent to orchestrator even if it's not a thread owner