Introduction
This website provides documentation for Twnel Chatbots Development by explaining a JSON definition.
Our goals are:
- Allow users to create chatbots using both a Web Visual Low-Code Tool and a JSON Spec considering external APIs
- Allow company members to define which chatbots to show for each audience using business units
- Allow company members to access data collected by chatbots
- Allow chatbot developers to define logic using javascript functions
- Provide input types for structured chatbots, better user experience and low error rates
- Give Instant-Deployment tools to developers to see chatbots in actions as soon as possible
- Give Debugging and Monitoring tools to developers to measure how well chatbots are working
JSON Spec 1.0
chatbot flow spec
{
"version": "1.0",
"variables": {...},
"messages": {...},
"files": {...},
"storage": {...},
"entrypoint": "...",
"transitions": {...},
"actions": {...},
"functions": {...}
}
The code to create a chatbot is a JSON file, as seen to the right. The spec, or JSON file, is based on a finite state machine model using transitions and actions. The actions include sending messages, calling APIs and executing functions.
Transitions
The spec also include the resources needed for more complex functionality: variables, messages, templates, files and storage configurations to send data collected by chatbots to customers' infrastructures.
This is the web tool to define the JSON spec or visual representation.
Version
Version
{
"version": "1.0"
}
Indicates which version of the spec it is being used.
Variables (Chatbot, User, Session)
variables
{
"user": {
"session_id": {
"type": "string",
"value": ""
},
"phone": {
"type": "string",
"value": ""
},
"country": {
"type": "string",
"value": ""
},
"name": {
"type": "string",
"value": ""
},
"tags": {
"type": "array",
"value": []
}
},
"chatbot": {
"token": {
"type": "string",
"value": "token"
}
},
"session": {
"user_passcode": {
"type": "integer",
"value": 1
}
}
}
The framework will consider 3 variables types and chatbots will read or write to them based on the type
Types
Chatbot: Read-only parameters specific to the chatbot (properties)
User: Read-only parameters provided by Twnel Platform (session_id, phone, country, name and tags if any)
Session: Read-Write app parameters which live in the context of the user and chatbot session
JSON Types
6 Basic JSON types are considered: boolean, integer, number, string, array, object
Assignments will be allowed for session variables and operations will be immutable
Transitions
transitions
{
"store_lottery_result": {
"action": "store_lottery_result",
"next": "get_lottery_message"
},
"get_lottery_message": {
"action": "eval_next_for_passcode",
"next": {
"lose": "message_lose",
"win": "message_win"
}
},
"message_lose": {
"action": "message_lose"
},
"message_win": {
"action": "message_win"
}
}
Each transition is a state. Each state has:
- 1 action
- 1 next state
The next parameter can be fixed or can be calculated using session variables
Session variables hold the next state and on any transition values can be overwritten given logic
At the right, we can see how the store_lottery_result
transition executes the store_lottery_result
action and the next transition will be get_lottery_message
The next value can be conditional, using a dictionary. In transition get_lottery_message
, if its
action result is lose
, the next transition will be message_lose
; message_win
otherwise, if the
action result is win
Entrypoint
entrypoint
{
"entrypoint": "ask_passcode"
}
Start transition to execute when the chatbot is triggered.
String value pointing to the transition. Any transition can be setup as the starting one
Storage
storage
{
"customer": {
"enable": false,
"drivers": {
"s3": {
"bucket": "AWS_S3_BUCKET_NAME",
"service_account": {
"name": "my_aws_s3_account",
"credentials": {
"aws_access_key_id": "",
"aws_secret_access_key": ""
}
}
},
"azure": {
"bucket": "AZURE_BLOB_CONTAINER_NAME",
"service_account": {
"name": "my_azure_blob_account",
"credentials": {
"azure_storage_account_name": "",
"azure_storage_account_key": ""
}
}
},
"gcloud": {
"bucket": "GOOGLE_CLOUD_BUCKET_NAME",
"service_account": {
"name": "my_google_storage_account",
"credentials": {
"type": "",
"project_id": "",
"private_key_id": "",
"private_key": "",
"client_email": "",
"client_id": "",
"auth_uri": "",
"token_uri": "",
"auth_provider_x509_cert_url": "",
"client_x509_cert_url": ""
}
}
}
}
},
"twnel": {
"enable": true,
"mode": "secure"
}
}
In this section we can define where files, collected by chatbots, will be stored.
2 modes of storage are provided:
- customer: This one is chosen when customers want to store files in their systems. Amazon S3, Azure Blob Storage or Google Cloud Drivers can be setup.
- twnel: If Twnel storage is chosen, 2 sub-modes are provided: public or secure. If secure is chosen, Twnel will store files in its infrastructure by default using pre-signed URLs with a custom expiration date. Public mode will use a public storage bucket.
Enable boolean parameters are added to turn on or off any storage option
In this example we can see the configurations for Amazon S3, Azure Blob Storage and Google Cloud Storage
Messages
messages
{
"error_message": "An error has occurred: {{ error }}"
}
This option was included to define messages templates using jinja syntax, with placeholders.
In the future this list will be used for language translations, using i18n.
Names for messages are provided by the developer. We will see how this list of messages templates are used in chatbot actions.
Files
files
{
"logo": "https://twnelassets.s3.amazonaws.com/twnel_logo.png",
"my_audio": "AUDIO_S3_URL",
"my_video": "VIDEO_S3_URL",
"my_cert": "CERT_S3_URL",
"my_pdf": "PDF_S3_URL"
}
Chatbot developer uploads files to be used and provides URLs.
Given Twnel media_url API functionality, the only thing that is needed for files are URLs.
Twnel reads files from Amazon S3 or Azure Storage and gets the right content type automatically
Functions
functions
{
"get_passcode_next": {
"runtime": "js",
"version": "5",
"code": "function get_passcode_next(input) { var code = input['code']; if (code == 'allow') { return 'win'; } else { return 'lose'; } }",
"input": {
"code": "string"
},
"output": {
"type": "string",
"values": [
"win",
"lose"
],
"sample": "lose"
}
}
}
{
"check_customer": {
"runtime": "jsonlogic",
"version": "",
"code": {
"conditions": [
{
"label": "vip customer",
"rule": {
"and": [
{">=": [{"var": "age"}, 21]},
{"==": [{"var": "vip"}, "yes"]}
]
},
"output": "c1"
},
{
"label": "child user",
"rule": {
"and": [
{"<=": [{"var": "age"}, 18]},
{"==": [{"var": "school"}, "yes"]}
]
},
"output": "c2"
}
]
},
"input": {
"age": "integer",
"vip": "string",
"school": "string"
},
"output": {
"type": "string",
"values": [
"c1",
"c2",
"default"
],
"sample": "c1"
}
}
}
List of functions to be used. All functions receive an input object and return an output. The programmer can define functions in JavaScript ES5 for complex functionality or JSON Logic for simple conditions based on rules.
For conditions,
the developer defines a list of conditions and they are evaluated in order. If condition is true, its output will be the
result of the function. If no condition is met, default
is returned. To see the documentation for JSON Logic, go to
json logic website
Parameters
- runtime: 2 runtimes are supported:
js
andjsonlogic
- version:
5
is the only one supported forjs
and no value forjsonlogic
- code: ES5 javascript code (
string
) or JSON Logic rule (object
) - input: description of inputs with types
- output: type, expected values and response sample
Variable Types
boolean
integer
number
string
array
object
Actions
send_message
send_message
{
"type": "send_message",
"vars": {},
"messages": [
{
"template": "{{ messages.ask_passcode_message }}",
"data": {}
},
"Second message"
],
"input": {
"type": "number",
"method": "keyboard",
"timeout": 3,
"data": []
},
"media_url": "",
"metadata": {
"media": "image|audio|video|map|application/pdf"
},
"delay": 3,
"close_chat": true
}
Use to send a message with the right input type to the client and expect data. Files can be sent by giving the right media url as parameter.
Parameters
messages: List or array of strings to be used by the framework. Only 1 will be chosen randomly to be sent to the end user. Messages can be strings or objects, using messages templates.
input: It refers the media input type. This indicates an input is expected from the user. Here the user might enter a number, map location, text or photo.
media_url: It is the url in S3, Azure. Mobile clients will show the file given its content type on the screen.
metadata: It is a key-value dictionary for twnel media messages responses. Useful to define media content types and metadata, like locations. Only normal strings or JSON strings are allowed.
delay: Value in seconds to wait after the current action has been executed. Useful to define a time to give the end user some breathing space to read messages or synchronize actions.
timeout: Value in minutes to wait for an answer from the user. If the user does not respond, the chatbot session is closed. If no timeout defined, the bot will always wait. If defined, the maximum value allowed is
10 minutes
. Timeout is only valid when a question is asked to the user by specifying aninput
from the ones available.close_chat: Value that indicates if the chat should be closed or not at the end of the session. This parameter is only read in the last transition. The default value is
true
. It only makes sense to include it if the value isfalse
given the platform will release the chat at the end of the session in order for a human agent to take it.
map_result
map_result
{
"type": "map_result",
"vars": {},
"eval": {
"targets": [
{
"from": "{{ transition.ask_passcode }}",
"assign": [
{
"map": "val",
"to": "variables.session.user_passcode"
}
]
}
]
}
}
body example
{
"k": "v",
"a": [1, 2],
"o": {
"x": "2"
}
}
action output
"output": {
"answer": {
"user": {...},
"chatbot": {...},
"session": {...}
}
}
Define mapping data in order to read or write arbitrary JSON API responses and perform variables assignment. This is useful when building payloads to call APIs or to read API responses and perform assignments.
The mapper uses dot routes notation to read or write objects.
This is a powerful feature given one can read or write any value inside an API JSON response and assign the value to a variable, for further processing. Also, this action can be used to do transformations for variables used in functions or APIs.
At the rigth, we can see a body example. One can use the mapper with these routes:
- "k": read or write at position
k
- "a.0": read or write at 1st element of array at position
a
- "o.x": read or write at
x
element of object at positiono
The map_result actions uses object trees and reads or writes values using dot notation paths.
output
An output object will be created with the resulting variables as answer after applying all mappings
- Only session variables are allowed to be rewritten
- This answer may be helpful for debugging purposes
call_api
call_api
{
"type": "call_api",
"vars": {},
"eval": {
"method": "POST",
"content_type": "application/json | application/x-www-form-urlencoded | text/xml",
"url": "https://api.production.twnel.io/users/random/numbers",
"timeout": 5000,
"params": {},
"headers": {
"Authorization": "Bearer {{ variables.chatbot.token }}"
},
"body": "{ \"min\": {{ variables.chatbot.random_min }}, \"max\": {{ variables.chatbot.random_max }} }"
},
"post_eval": {
"verify": {
"status": [200, 201],
"schema": {
"type": "object",
"properties": {
"number": {
"type": "number"
},
"input": {
"type": "object"
}
}
},
"timecap": 1000
}
},
"on_error": {
"next": "message_api_error"
}
}
"on_error": {
"next": {
"eval": "message_api_error_eval",
"timeout": "message_api_error_timeout",
"status": "message_api_error_status",
"schema": "message_api_error_schema",
"default": "message_api_error_default"
}
}
action output
"output": {
"status": 200,
"response_time": 250,
"answer": ...
}
Define parameters to call HTTP endpoints. It supports all HTTP methods, query strings with params
,
headers and body payloads. The response will be added inside the transition as eval_response
or in the output
Given the spec uses JSON and references values with JINJA syntax, in order
to support integers or arrays inside the body
, it is useful to use JSON string values and not body
objects. If all attributes are strings, then the body object is a good option.
Status HTTP code will be added as a result in the transition as eval_response_data.status
This action also provides a way to verify the extected HTTP status code in the response. A list of
expected codes can be provided. Also, in order to verify the response structure, we support
json schema. The post_eval
section can be seen to the right. Another
verifier timecap
is added to set the expected response time, in milliseconds
, for the API call. If
verifiers fail, error logs will be shown
A timeout
parameter has been added to specify the time to wait in milliseconds before triggering an error. Max timeout
is 27.5 seconds.
If no post_eval
section is specified for verifiers, 200 OK
will be the default status code and
the expected timecap or response time will be 2 seconds
.
A new section named on_error
has been added to set the next transition to execute in case of any error. If the
verify
section in post_eval
is defined and an unexpected response is received, this is considered an error. If no
on_error
is defined, the bot will executed the next
action defined in the transitions
section as expected.
Content Types:
- json: use
application/json
- form-data: use
application/x-www-form-urlencoded
- xml: use
text/xml
Types of Errors and Priorities:
on_error
can also be used to define conditional transitions based on the specific error and 1 transition will be
executed given this order if there are multiple errors:
eval
for bad parameters or requesttimeout
for request timeoutstatus
for unexpected HTTP status codeschema
for unexpected HTTP response given json schemadefault
as the default error transition
The timecap
does not trigger errors and must be used for debugging, logging or measuring.
output
An output object will be created with the resulting HTTP status, response time in ms and API answer
- The API answer can be any valid JSON type:
string
,number
,array
,object
, etc. - The
status
can be used as input parameter for functions for dynamic flows
call_function
call_function
{
"type": "call_function",
"vars": {},
"eval": {
"function": "evalutate_threshold",
"input": {
"number": "{{ variables.session.random_number }}",
"threshold": "{{ variables.chatbot.threshold }}"
}
},
"on_error": {
"next": "message_func_error"
}
}
action output
"output": {
"function": "func_name",
"input": {...},
"answer": ...
}
Use to execute functions, conditions for instance. Useful to calculate the next state in chatbot executions.
Given the functions specify contracts, or interfaces, the action just provides the values needed for execution.
We just need to reference the function
to execute, evalutate_threshold
in this example, and its
input
object. The response will be added inside the transition as eval_response
A new section named on_error
has been added to set the next transition to execute in case of any error. If no
on_error
is defined, the bot will executed the next
action defined in the transitions
section as expected.
output
An output object will be created with the evaluated function name, input and answer
- The JS function answer can be any valid JSON type:
string
,number
,array
,object
, etc. input
may be helpful for debugging purposes
conditional_step
conditional_step
{
"type": "conditional_step",
"vars": {},
"eval": {
"function": "get_passcode_next",
"input": {
"code": "{{ variables.session.passcode_result }}"
}
}
}
conditional transitions
{
"action": "conditional_step_action",
"next": {
"lose": "func_message_lose",
"win": "func_message_win"
}
}
action output
"output": {
"function": "func_name",
"input": {...},
"answer": "next_step"
}
There are times when we need to execute an action given a condition. We have no idea which transition is going to be
executed. For these cases, we use this action to set the next
state dinamically. For instance, if my client is VIP, we
want to execute transitions for VIP customers; otherwise, we can execute other actions. In order to define the next
transition, we define a function
that is going to be executed and will return the string
of the key for the next
transition or action to be executed
In this example, if the function returns lose
, the func_message_lose
will be executed. if the function returns win
,
the func_message_win
will be executed.
We use the same syntax as call_function
, but conditional_step
signals developers that a conditional
transition is taking place. The output of the function will be a string
that matches the next
dictionary conditionals
output
An output object will be created with the evaluated function name, input and answer
- The JS function answer will be a string indicating the next_step to follow
input
may be helpful for debugging purposes
store_file
store_file
{
"type": "store_file",
"eval": {
"driver": "s3",
"uri": "{{ transition['capturar_foto']['output']['data'][1]['url'] }}"
}
}
action output
"output": {
"driver": "s3",
"uri": "from_uri",
"answer": "new_uri"
}
Used to store files in a storage service (ie: s3)
In this example a media will be copied to S3, using the storage
setup, and the uri
will be calculated
by the framework. This new url
can be used in other transitions. The access policy of the new file
will be public if the bucket is public.
output
An output object will be created with the driver used, input URI and final URI as answer
- Remember
answer
will contain the New URI
Input Types
When sending messages, one defines an input type or data we expect from the user. Here we describe the supported inputs and its parameters. Any chatbot will wait for the user input before resuming its execution.
text
text
{
"type": "send_message",
"input": {
"method": "keyboard",
"type": "text",
"data": [
{ "placeholder": "..." }
]
}
}
A simple text input will be requested.
A placeholder can be defined to be displayed in the input box and help the user
number
number
{
"type": "send_message",
"input": {
"method": "keyboard",
"type": "number",
"data": [
{ "placeholder": "..." },
{ "integer": true/false },
{ "min": 1 },
{ "max": 200 }
]
}
}
A number pad will be shown. If integer
is true, dots in the keyboard will be disable.
A min
and max
parameters are used to define a valid range for the number
A placeholder can be defined to be displayed in the input box and help the user
phone
phone
{
"type": "send_message",
"input": {
"method": "keyboard",
"type": "phone",
"data": [
{ "placeholder": "..." }
]
}
}
Include a valid phone number using the E164 ISO format
A placeholder can be defined to be displayed in the input box and help the user
{
"type": "send_message",
"input": {
"method": "keyboard",
"type": "email",
"data": [
{ "placeholder": "..." }
]
}
}
Include a valid email using user@domain.com syntax
A placeholder can be defined to be displayed in the input box and help the user
radio
radio (spec v1)
{
"type": "send_message",
"input": {
"method": "keyboard",
"type": "radio",
"data": [
{"id": "yes", "label": "Acepto"},
{"id": "no", "label": "No"}
]
}
}
radio (spec v0)
{
"type": "send_message",
"input": {
"method": "keyboard",
"type": "radio",
"data": [
{"yes": "Acepto"},
{"no": "No"}
]
}
}
Perfect to allow the user to choose 1 option from a list. data
includes available options. Keys
are used to control the next transition and values
will be displayed to the user.
In our example, Acepto
and No
will be the options the user can choose from. If Acepto
is chosen,
the yes
will be given for the next transition
checkbox
checkbox (spec v1)
{
"type": "send_message",
"input": {
"method": "keyboard",
"type": "checkbox",
"data": [
{"id": "a", "label": "A"},
{"id": "b", "label": "B"},
{"id": "c", "label": "C"}
]
}
}
checkbox (spec v0)
{
"type": "send_message",
"input": {
"method": "keyboard",
"type": "checkbox",
"data": [
{"a": "A"},
{"b": "B"},
{"c": "C"}
]
}
}
Perfect to allow the user to choose multiple options from a list. data
includes available options.
date
date
{
"type": "send_message",
"input": {
"method": "keyboard",
"type": "date",
"data": [
{ "format": "AAAA-MM-DD"|"YYYY-mm-dd"|"dd/mm/YYYY"|"mm/dd/YYYY" },
{ "placeholder": "dd/mm/YYYY" },
{ "default" : { "day": 12, "month": 12, "year": 1988 } }
]
}
}
Define dates. format
follows ISO 8601 standard.
If no default
date is provided, clients will not suggest a date. They will display the placeholder
if any to help the user on writing the expected date. If default
date is provided, and
day
, month
or year
is undefined, clients will use current time as default value
summary
"default": {}
: will suggest current time (day, month, year) as default valueno default
: will show format placeholder if any on an empty date text field
Values for the default date can be either integers (12) or strings ("30")
password
password
{
"type": "send_message",
"input": {
"method": "keyboard",
"type": "password",
"data": [
{ "subtype": "number|text" },
{ "size": 4|16 },
{ "confirm": true },
{ "confirm_message": "<CONFIRM_MESSAGE>" },
{ "fail_message": "<FAIL_MESSAGE>" },
{ "pub" : "[...]" }
]
}
}
Allow the user to define a password, either numeric or alphanumeric, using the subtype
parameter
A confirm and messages are provided to allow the user to define the password twice, following industry practices.
The pub
is the rsa public key to be used to encrypt the password payload, making sure not even Twnel
knows the secret and only the company can used the private key to decrypt the message.
location
location
{
"type": "send_message",
"input": {
"method": "keyboard",
"type": "location",
"data": [
{ "subtype": "current"|"map" },
{ "format": "degrees"|"minutes"|"seconds" },
{ "timestamp": true/false },
{ "accuracy" : true/false }
]
}
}
The user is shown a map and is asked to share a location. Using the subtype
we have 2 modes:
- current: The map will show the user's current location and the pin can't be moved
- map: The map will show the user's current location and the pin can be moved
If timestamp
and accuracy
are true, the gps parameters will be included in the response
media
media
{
"type": "send_message",
"input": {
"method": "keyboard",
"type": "media",
"geotag": true/false,
"data": [
{ "media_type": "image|audio|video|application/pdf" },
{ "picker": "camera/gallery/all" },
{ "facing": "back/front" }
]
}
}
Useful to ask media to the user: images
, audios
, videos
etc. The functionality to geotag this
media file is also available and the output will have the location
parameters.
It is also possible to set the picker to choose the media from. Both camera and gallery are supported. When using the camera, its facing can be set for a selfie or normal photo.
barcode
barcode
{
"type": "send_message",
"input": {
"method": "keyboard",
"type": "barcode",
"subtype": "QR_CODE",
"geotag": true/false,
"data": [
{ "response": "RESPONSE_MESSAGE_AFTER_SCAN" }
]
}
}
Useful to scan barcodes using the camera:
- QR Codes
- Product Bar Codes
- Shipping Labels Bar Codes
The functionality to geotag this barcode is also available and the output will have the location
parameters
subtype
specifies the kind of barcode that is expected and the application setups the scanner accordingly. If subtype
is not defined, is empty or value is not supported, the client setups the scanner for autodetection. Supported values:
QR_CODE
more values could be added over time to setup the scanner
Response
response
indicates the output message body to send after the scan
signature
signature
{
"type": "send_message",
"input": {
"type": "signature",
"method": "keyboard",
"geotag": true/false,
"data": [
{ "caption": "SIGNATURE_CAPTION_TEXT" }
]
}
}
Mobile client shows a canvas and lets the user draw a signature. Once completed, the client creates and sends an image.
caption
indicates the text to show below the signature box. If not provided, these texts will be displayed:
ES:
Firma aquí
EN:
Sign here
PR:
Assine aqui
Caption Rules
- if caption is defined and empty, show nothing
- if caption is defined and has a value, show caption
Location
The functionality to geotag this signature is also available and the output will have the location
parameters
Output Types
When receiving messages after input types, an output type is given inside the message info object. Here we describe the supported outputs and parameters. This is useful for further processing with functions or api calls
Use answer
for processing or mappings
text_output
text
{
"info": {
"output": {
"version": "1.0",
"type": "text",
"data": [],
"answer": {
"value": "received text"
}
}
}
}
Text in the message body or in answer object
number_output
number
{
"info": {
"output": {
"version": "1.0",
"type": "number",
"data": [],
"answer": {
"value": 7
}
}
}
}
Number in the message body or in answer object
phone_output
phone
{
"info": {
"output": {
"version": "1.0",
"type": "phone",
"data": [],
"answer": {
"value": "+16170000102"
}
}
}
}
Phone number in the message body or in answer object
email_output
{
"info": {
"output": {
"version": "1.0",
"type": "email",
"data": [],
"answer": {
"value": "me@mail.com"
}
}
}
}
Email address in the message body or in answer object
radio_output
radio (spec v1)
{
"info": {
"output": {
"version": "1.0",
"type": "radio",
"data": [
{"id": "yes", "label": "Acepto"}
],
"answer": {
"selected": {"id": "yes", "label": "Acepto"},
"id": "yes",
"label": "Acepto"
}
}
}
}
radio (spec v0)
{
"info": {
"output": {
"version": "1.0",
"type": "radio",
"data": [
{"yes": "Acepto"}
],
"answer": {
"selected": {"id": "yes", "label": "Acepto"},
"id": "yes",
"label": "Acepto"
}
}
}
}
checkbox_output
checkbox (spec v1)
{
"info": {
"output": {
"version": "1.0",
"type": "checkbox",
"data": [
{"id": "a", "label": "A"},
{"id": "c", "label": "C"}
],
"answer": {
"selected": [
{"id": "a", "label": "A"},
{"id": "c", "label": "C"}
],
"ids": ["a", "c"],
"labels": ["A", "C"]
}
}
}
}
checkbox (spec v0)
{
"info": {
"output": {
"version": "1.0",
"type": "checkbox",
"data": [
{"a": "A"},
{"c": "C"}
],
"answer": {
"selected": [
{"id": "a", "label": "A"},
{"id": "c", "label": "C"}
],
"ids": ["a", "c"],
"labels": ["A", "C"]
}
}
}
}
Selected choice is given
date_output
date
{
"info": {
"output": {
"version": "1.0",
"type": "date",
"data": [
{ "format" : "dd/mm/YYYY|AAAA-MM-DD|mm/dd/YYYYTHH:mm:ssZ" },
{ "selected_date" : "11/09/2001|2001/09/11|09/11/1989T03:00:00Z" }
],
"answer": {
"format": "dd/mm/YYYY|AAAA-MM-DD|mm/dd/YYYYTHH:mm:ssZ",
"selected_date" : "11/09/2001|2001/09/11|09/11/1989T03:00:00Z"
}
}
}
}
Selected date is given. format
follows ISO 8601 standard.
password_output
password
{
"info": {
"output": {
"version": "1.0",
"type": "password",
"data": [
{"ciphered": "<CIPHERED_DATA>"}
],
"answer": {
"ciphered": "<CIPHERED_DATA>"
}
}
}
}
The ciphered password will be included. In order to decrypt it, the private key must be used.
We recommend creating an endpoint that receives this ciphered password to decrypt it and do not use the private key as a bot variable
location_output
location
{
"info": {
"output": {
"version": "1.0",
"type": "location",
"data": [
{"latitude": 42.32996014},
{"longitude": -71.07001367},
{"timestamp": 1562087482},
{"accuracy": 20}
],
"answer": {
"latitude": 42.32996014,
"longitude": -71.07001367,
"timestamp": 1562087482,
"accuracy": 20
}
}
}
}
Geolocation parameters using degrees
format.
timestamp
in seconds and accuracy
in meters. Default values if not present in the response:
timestamp
will be the current time in secondsaccuracy
will be-1
media_output
media
{
"info": {
"output": {
"version": "1.0",
"type": "media",
"data": [
{"media_type": "image|audio|video|application/pdf"},
{"url": "FILE_URL"}
],
"answer": {
"media_type": "image|audio|video|application/pdf",
"url": "FILE_URL"
},
"location": {
"latitude": 42.32996014,
"longitude": -71.07001367,
"timestamp": 1562087482,
"accuracy": 20
}
}
}
}
Media URIs will be included with its media type. If geotag is true
in input definition, geolocation
parameters will also be included. -1
is the default value for any of the 4 location attributes
timestamp
in seconds and accuracy
in meters. Default values if not present in the response:
timestamp
will be the current time in secondsaccuracy
will be-1
barcode_output
barcode
{
"info": {
"output": {
"version": "1.0",
"type": "barcode",
"data": [
{"format": "BARCODE_FORMAT"},
{"raw": "DECODED_TEXT"},
{"type": "RESULT_TYPE"},
{"value": "RESULT_VALUE"}
],
"answer": {
"format": "BARCODE_FORMAT",
"raw": "DECODED_TEXT",
"type": "RESULT_TYPE",
"value": "RESULT_VALUE"
},
"location": {
"latitude": 42.32996014,
"longitude": -71.07001367,
"timestamp": 1562087482,
"accuracy": 20
}
}
}
}
If geotag is true
in input definition, geolocation parameters will also be included. -1
is the
default value for any of the 4 location attributes
Barcode Formats
QR_CODE
EAN_13
EAN_8
UPC_A
UPC_E
UPC_EAN_EXTENSION
AZTEC
CODABAR
CODE_128
CODE_39
CODE_93
DATA_MATRIX
ITF
MAXICODE
PDF_417
RSS_14
RSS_EXPANDED
Barcode Types
TEXT
URI
EMAIL_ADDRESS
TEL
GEO
SMS
ADDRESSBOOK
CALENDAR
ISBN
PRODUCT
VIN
WIFI
timestamp
in seconds and accuracy
in meters. Default values if not present in the response:
timestamp
will be the current time in secondsaccuracy
will be-1
signature_output
signature
{
"info": {
"output": {
"version": "1.0",
"type": "signature",
"data": [
{"media_type": "image"},
{"url": "FILE_URL"}
],
"answer": {
"media_type": "image",
"url": "FILE_URL"
},
"location": {
"latitude": 42.32996014,
"longitude": -71.07001367,
"timestamp": 1562087482,
"accuracy": 20
}
}
}
}
url
will contain the image with the provided signature
If geotag is true
in input definition, geolocation parameters will also be included. -1
is the
default value for any of the 4 location attributes
timestamp
in seconds and accuracy
in meters. Default values if not present in the response:
timestamp
will be the current time in secondsaccuracy
will be-1
Text Formatting
This small section describes how to format messages in Twnel
Reference
example
{
"type": "send_message",
"vars": {},
"messages": [
"text *text* _text_ ~text~ *_text_* *~text~* ~_text_~ *~_text_~*"
]
}
Format | Code |
---|---|
Normal | text |
Bold | *text* |
Italic | _text_ |
~text~ |
|
Monospace |
```text``` |
At the right we can see possible combinations
Filters
It is possible to transform the data processed in the strings used within the bot spec
whenever the syntax with double brackets ({{ var }}
) is available.
The data transformation can be achieved using any of the Jinja builtin filters. Please check the official Jinja documentation for further information.
Besides the builtin Jinja filters, there are additional custom filters implemented which are shown in the samples bellow.
Reference
example
{
"type": "send_message",
"vars": {
"empty_array": [],
"currency": 2019,
"url": "http://example.com",
"json_obj": {},
"json_str": "{}"
},
"messages": [
"{{ vars.empty_array | length | ternary(true, false) }}"
"{{ vars.currency | currency('en_US.UTF-8') }}"
"{{ vars.url | urlencode }}"
"{{ vars.json_str | to_json_obj }}"
"{{ vars.json_obj | to_json_str }}"
]
}
Filter | Description | Output (sample) |
---|---|---|
ternary | Evaluates an expression and returns either of the parameters depending on its falsiness | false |
currency | Converts a quantity into a given currency format | $2,019.00 |
urlencode | Makes a string URL safe | http%3A%2F%2Fexample.com |
json_obj | Returns JSON object from a string | {} |
json_str | Returns JSON string from an object | '{}' |
Examples
This section will provide examples with problems, specs and explanations that will allow developers to get familiar with the SDK and how it can be used to go from dream to chatbot as soon as possible
Sum Bot
{
"version": "1.0",
"variables": {
"user": {
"phone": {
"type": "string",
"value": ""
},
"country": {
"type": "string",
"value": ""
},
"name": {
"type": "string",
"value": ""
},
"tags": {
"type": "array",
"value": []
}
},
"chatbot": {},
"session": {
"first_number": {
"type": "number",
"value": 0
},
"second_number": {
"type": "number",
"value": 0
},
"total_value": {
"type": "number",
"value": 0
}
}
},
"messages": {
"intro_message": "Ok, let's start!",
"first_number_message": "Insert your first number",
"second_number_message": "Insert your second number",
"total_message": "The result is: {{ total }}",
"end_message": "Thank you for use me!"
},
"entrypoint": "start",
"transitions": {
"start": {
"action": "start",
"next": "ask_first_number"
},
"ask_first_number": {
"action": "ask_first_number",
"next": "map_first_number"
},
"map_first_number": {
"action": "map_first_number",
"next": "ask_second_number"
},
"ask_second_number": {
"action": "ask_second_number",
"next": "map_second_number"
},
"map_second_number": {
"action": "map_second_number",
"next": "make_sum"
},
"make_sum": {
"action": "make_sum",
"next": "map_sum_result"
},
"map_sum_result": {
"action": "map_sum_result",
"next": "send_total"
},
"send_total": {
"action": "send_total",
"next": "end"
},
"end": {
"action": "end"
}
},
"actions": {
"start": {
"type": "send_message",
"vars": {},
"messages": [
{
"template": "{{ messages.intro_message }}",
"data": {}
}
]
},
"ask_first_number": {
"type": "send_message",
"vars": {},
"messages": [
{
"template": "{{ messages.first_number_message }}",
"data": {}
}
],
"input": {
"type": "number",
"method": "keyboard",
"data": []
}
},
"map_first_number": {
"type": "map_result",
"vars": {},
"eval": {
"targets": [
{
"from": "{{ transition.ask_first_number.output }}",
"assign": [
{
"map": "answer.value",
"to": "variables.session.first_number"
}
]
}
]
}
},
"ask_second_number": {
"type": "send_message",
"vars": {},
"messages": [
{
"template": "{{ messages.second_number_message }}",
"data": {}
}
],
"input": {
"type": "number",
"method": "keyboard",
"data": []
}
},
"map_second_number": {
"type": "map_result",
"vars": {},
"eval": {
"targets": [
{
"from": "{{ transition.ask_second_number.output }}",
"assign": [
{
"map": "answer.value",
"to": "variables.session.second_number"
}
]
}
]
}
},
"make_sum": {
"type": "call_function",
"vars": {},
"eval": {
"function": "sum_numbers",
"input": {
"first_number": "{{ variables.session.first_number }}",
"second_number": "{{ variables.session.second_number }}"
}
}
},
"map_sum_result": {
"type": "map_result",
"vars": {},
"eval": {
"targets": [
{
"from": "{{ transition.make_sum.output }}",
"assign": [
{
"map": "answer",
"to": "variables.session.total_value"
}
]
}
]
}
},
"send_total": {
"type": "send_message",
"vars": {},
"messages": [
{
"template": "{{ messages.total_message }}",
"data": {
"total": "{{ variables.session.total_value }}"
}
}
]
},
"end": {
"type": "send_message",
"vars": {},
"messages": [
{
"template": "{{ messages.end_message }}",
"data": {}
}
]
}
},
"functions": {
"sum_numbers": {
"runtime": "js",
"version": "5",
"code": "function sum_numbers(input) { var total = 0; var first_number = input['first_number']; var second_number = input['second_number']; total = (first_number + second_number); return total; }",
"input": {
"first_number": "number",
"second_number": "number"
},
"output": {
"type": "number",
"values": [],
"sample": 0
}
}
}
}
Simple bot that shows how to sum 2 numbers and get the result in a final message. Also shows how to:
- Use variables
- Perform assignments with map_result actions
- Execute functions
- Reference other transition outputs
- Messages templates
Enable Passcode Game
chatbot_spec
{
"version": "1.0",
"variables": {
"user": {
"phone": {
"type": "string",
"value": ""
},
"country": {
"type": "string",
"value": ""
},
"name": {
"type": "string",
"value": ""
},
"tags": {
"type": "array",
"value": []
}
},
"chatbot": {
"token": {
"type": "string",
"value": "token"
},
"random_min": {
"type": "integer",
"value": 1
},
"random_max": {
"type": "integer",
"value": 100
},
"threshold": {
"type": "integer",
"value": 50
}
},
"session": {
"user_passcode": {
"type": "integer",
"value": 1
},
"random_number": {
"type": "integer",
"value": 1
},
"passcode_result": {
"type": "string",
"value": ""
}
}
},
"messages": {
"error_message": "An error has occurred: *{{ error }}*",
"ask_passcode_message": "Please enter your ticket number",
"code_rejection_message": "We are sorry. Your input is *({{ number }}, {{ threshold }})* for code *{{ code }}* and result is: *{{ result }}*",
"code_success_message": "Congratulations. Your input is *({{ number }}, {{ threshold }})* for code *{{ code }}* and result is: *{{ result }}*"
},
"files": {
"logo": "https://twnelassets.s3.amazonaws.com/twnel_logo.png"
},
"storage": {
"customer": {
"enable": false,
"drivers": {
"s3": {
"bucket": "twnel-storage-botcore",
"service_account": {
"credentials": {
"aws_access_key_id": null,
"aws_secret_access_key": null
}
}
}
}
},
"twnel": {
"enable": true,
"mode": "secure"
}
},
"entrypoint": "ask_passcode",
"transitions": {
"ask_passcode": {
"action": "ask_passcode",
"next": "store_passcode"
},
"store_passcode": {
"action": "store_passcode",
"next": "get_random_number"
},
"get_random_number": {
"action": "get_random_number",
"next": "store_random_number"
},
"store_random_number": {
"action": "store_random_number",
"next": "exec_lottery_func"
},
"exec_lottery_func": {
"action": "exec_lottery_func",
"next": "store_lottery_result"
},
"store_lottery_result": {
"action": "store_lottery_result",
"next": "get_lottery_message"
},
"get_lottery_message": {
"action": "eval_next_for_passcode",
"next": {
"lose": "message_lose",
"win": "message_win"
}
},
"message_lose": {
"action": "message_lose"
},
"message_win": {
"action": "message_win"
},
"message_api_error": {
"action": "message_api_error"
},
"message_api_error_status": {
"action": "message_api_error_status"
},
"message_func_error": {
"action": "message_func_error"
}
},
"actions": {
"ask_passcode": {
"type": "send_message",
"vars": {},
"messages": [
{
"template": "{{ messages.ask_passcode_message }}",
"data": {}
}
],
"input": {
"type": "number",
"method": "keyboard",
"data": [
{
"placeholder": "Please enter passcode"
},
{
"integer": true
}
]
}
},
"store_passcode": {
"type": "map_result",
"vars": {},
"eval": {
"targets": [
{
"from": "{{ transition.ask_passcode.output }}",
"assign": [
{
"map": "answer.value",
"to": "variables.session.user_passcode"
}
]
}
]
}
},
"get_random_number": {
"type": "call_api",
"vars": {},
"eval": {
"method": "POST",
"content_type": "application/json",
"url": "https://api.beta.twnel.me/users/random/numbers",
"timeout": 5000,
"params": {},
"headers": {
"Authorization": "Bearer {{ variables.chatbot.token }}"
},
"body": "{ \"min\": {{ variables.chatbot.random_min }}, \"max\": {{ variables.chatbot.random_max }} }"
},
"post_eval": {
"verify": {
"status": [
200,
201
],
"schema": {
"type": "object",
"properties": {
"number": {
"type": "number"
},
"input": {
"type": "object"
}
}
},
"timecap": 3000
}
},
"on_error": {
"next": {
"default": "message_api_error",
"status": "message_api_error_status"
}
}
},
"store_random_number": {
"type": "map_result",
"vars": {},
"eval": {
"targets": [
{
"from": "{{ transition.get_random_number.output }}",
"assign": [
{
"map": "answer.number",
"to": "variables.session.random_number"
}
]
}
]
}
},
"exec_lottery_func_new": {
"type": "call_function",
"vars": {},
"eval": {
"function": "wrong_json",
"input": {
"code": "{{ variables.session.random_number }}"
}
},
"on_error": {
"next": "message_func_error"
}
},
"exec_lottery_func": {
"type": "call_function",
"vars": {},
"eval": {
"function": "evalutate_threshold",
"input": {
"number": "{{ variables.session.random_number }}",
"threshold": "{{ variables.chatbot.threshold }}"
}
},
"on_error": {
"next": "message_func_error"
}
},
"store_lottery_result": {
"type": "map_result",
"vars": {},
"eval": {
"targets": [
{
"from": "{{ transition.exec_lottery_func.output }}",
"assign": [
{
"map": "answer.result",
"to": "variables.session.passcode_result"
}
]
}
]
}
},
"eval_next_for_passcode": {
"type": "eval_next",
"vars": {},
"eval": {
"function": "get_passcode_next",
"input": {
"code": "{{ variables.session.passcode_result }}"
}
}
},
"message_lose": {
"type": "send_message",
"vars": {},
"messages": [
{
"template": "{{ messages.code_rejection_message }}",
"data": {
"number": "{{ variables.session.random_number }}",
"threshold": "{{ variables.chatbot.threshold }}",
"code": "{{ variables.session.user_passcode }}",
"result": "{{ variables.session.passcode_result }}"
}
}
]
},
"message_win": {
"type": "send_message",
"vars": {},
"messages": [
{
"template": "{{ messages.code_success_message }}",
"data": {
"number": "{{ variables.session.random_number }}",
"threshold": "{{ variables.chatbot.threshold }}",
"code": "{{ variables.session.user_passcode }}",
"result": "{{ variables.session.passcode_result }}"
}
}
],
"close_chat": true
},
"message_api_error": {
"type": "send_message",
"vars": {},
"messages": [
{
"template": "{{ messages.error_message }}",
"data": {
"error": "API call did not succeed. Please try again later"
}
}
],
"close_chat": true
},
"message_api_error_status": {
"type": "send_message",
"vars": {},
"messages": [
{
"template": "{{ messages.error_message }}",
"data": {
"error": "API call did not succeed given a status"
}
}
],
"close_chat": true
},
"message_func_error": {
"type": "send_message",
"vars": {},
"messages": [
{
"template": "{{ messages.error_message }}",
"data": {
"error": "Function call did not succeed. Please try again later"
}
}
],
"close_chat": true
}
},
"functions": {
"evalutate_threshold": {
"runtime": "js",
"version": "5",
"code": "function evalutate_threshold(input) { var output = {}; var number = input['number']; var threshold = input['threshold']; if (number < threshold) { output['result'] = 'reject'; } else { output['result'] = 'allow'; } return output; }",
"input": {
"number": "integer",
"threshold": "integer"
},
"output": {
"type": "object",
"values": [],
"sample": {
"result": "reject"
}
}
},
"get_passcode_next": {
"runtime": "js",
"version": "5",
"code": "function get_passcode_next(input) { var code = input['code']; if (code == 'allow') { return 'win'; } else { return 'lose'; } }",
"input": {
"code": "string"
},
"output": {
"type": "string",
"values": [
"win",
"lose"
],
"sample": "lose"
}
},
"wrong_json": {
"runtime": "js",
"version": "5",
"code": "function wrong_json(input) { return JSON.stringify(xyz) }",
"input": {
"code": "string"
},
"output": {
"type": "string",
"values": [],
"sample": "anything"
}
}
}
}
Simple game that shows how to use the spec and create a bot using APIs, Functions and Conditional Branching
Description
Bot asks user to enter a passcode number, number is used to call an API that returns a random number between 1 and 100. If random number is less than N, where N is a threshold the bot builder sets, bot will send a rejection message. If random number is greater or equal than N, the passcode will be authorized and a success message will be send.
Explanation
The main idea for this chatbot will be to ask the user a code, execute a random process and decide if the user wins the game. Given the randomness, the bot needs conditional branching to let the user know that happened with a success or error message.
To do this, we will use these actions:
- send_message: Ask the user a passcode or send a success/error message at the end
- map_result: Store values in variables
- call_api: Call random number API
- call_function: Execute function to know if the user is a winner or not
- eval_next: Execute a conditional branching to choose the right path and end game message
And these resources:
- variables: Decide which variables will be constants and which ones will live in the session
- messages: Messages templates to be used across the chatbot
- functions: Available JS Functions for the game
Variables
User
Here Twnel provides always the phone number, country code and contact name, if any
Chatbot
Given these properties do not change for every user, they are constants or properties for the bot
- token: (string) property to be used for authentication in API calls
- random_min: (number) lower bound for the random API call
- random_max: (number) upper bound for the random API call
- threshold: (number) number set to decide winners and losers
Session
These variables change on every user interaction and will live on every user context
- user_passcode: (number) code that is entered by the user
- random_number: (number) random integer included in the API response
- passcode_result: (string) final game result
Transitions
The basic functionality is described with the next steps:
- Ask the user a passcode
- Save the passcode into a variable
- Call API to get a random number
- Store random number into a variable
- Execute function to decide if the User is a winner or not given threshold
- Store function game result
- Evaluate next step given game result calling a function
- Send a last message to the user informing the result for entered passcode
The functionality is a basic Finite State Machine and the next state is known most of the time.
The action eval_next
at the end will execute the function get_passcode_next
and given the result
is a string, win
or lose
, the framework will know exactly where to go between these 2 transitions:
message_lose
message_win
At the end the user will receive 1 of 2 possible messages:
code_rejection_message
code_success_message
API
The call_api
action is used and the body
is a JSON string in order to support sending a payload
with numbers or integers, not only strings.
Also, this example show how to include a bearer token using headers
and Authorization
.
Functions
2 JavaScript functions are defined: evalutate_threshold
and get_passcode_next
. Each function
defines its input
object, with variables names and their types. An output
object is used to
set the type of the result and possible values.
Inputs and Media
chatbot_spec
{
"version": "1.0",
"variables": {
"user": {
"phone": {
"type": "string",
"value": ""
},
"country": {
"type": "string",
"value": ""
},
"name": {
"type": "string",
"value": ""
},
"tags": {
"type": "array",
"value": []
}
},
"chatbot": {
"pdf_name": {
"type": "string",
"value": "who.pdf"
}
},
"session": {
"location": {
"type": "object",
"value": {
"latitude": 3.4982500076293945,
"longitude": -76.47799682617188
}
},
"choices": {
"type": "object",
"value": {
"number": 0,
"text": "text"
}
},
"barcode": {
"type": "object",
"value": {
"format": "",
"raw": "",
"type": "",
"value": ""
}
},
"geobarcode": {
"type": "object",
"value": {
"latitude": -1,
"longitude": -1,
"timestamp": -1,
"accuracy": -1
}
}
}
},
"messages": {
"ask_file_msg": "Please select the file you want",
"checkbox_text": "You selected {{number}} and text is: {{text}}",
"barcode_text": "Barcode Scan:\n\nformat: {{format}}\nraw: {{raw}}\ntype: {{type}}\nvalue: {{value}}",
"barcode_geotext": "Barcode Scan:\n\nformat: {{format}}\nraw: {{raw}}\ntype: {{type}}\nvalue: {{value}}\n\nLocation: ({{lat}}, {{lng}}, {{tsp}}, {{acc}})",
"media_geotext": "Media with Geotag:\n\nurl: {{url}}\n\nLocation: ({{lat}}, {{lng}}, {{tsp}}, {{acc}})"
},
"files": {
"image": "https://twnelusermedia.s3-us-west-2.amazonaws.com/rpi.png",
"audio": "https://twnelusermedia.s3-us-west-2.amazonaws.com/short.mp3",
"video": "https://twnelusermedia.s3-us-west-2.amazonaws.com/space.mp4",
"pdf": "https://www.minsalud.gov.co/sites/rid/Lists/BibliotecaDigital/RIDE/VS/PP/ET/abece-coronavirus.pdf"
},
"storage": {
"twnel": {
"enable": true,
"mode": "secure"
}
},
"entrypoint": "media",
"transitions": {
"media": {
"action": "media",
"next": "message"
},
"gallery": {
"action": "gallery",
"next": "message"
},
"camera": {
"action": "camera",
"next": "message"
},
"camera_all": {
"action": "camera_all",
"next": "message"
},
"camera_front": {
"action": "camera_front",
"next": "message"
},
"camera_back": {
"action": "camera_back",
"next": "message"
},
"geomedia": {
"action": "geomedia",
"next": "geomedia_msg"
},
"geomedia_msg": {
"action": "geomedia_msg",
"next": "message"
},
"signature": {
"action": "signature",
"next": "signature_message"
},
"signature_message": {
"action": "signature_message"
},
"message": {
"action": "message"
},
"location": {
"action": "location",
"next": "message"
},
"map": {
"action": "map",
"next": "message"
},
"radio": {
"action": "radio",
"next": {
"a": "message_a0",
"b": "message_b0"
}
},
"radio_v1": {
"action": "radio_v1",
"next": {
"a": "message_a1",
"b": "message_b1"
}
},
"radio_media": {
"action": "radio_media",
"next": "message"
},
"message_a0": {
"action": "message_a0",
"next": "message"
},
"message_b0": {
"action": "message_b0",
"next": "message"
},
"message_a1": {
"action": "message_a1",
"next": "message"
},
"message_b1": {
"action": "message_b1",
"next": "message"
},
"password": {
"action": "password",
"next": "message"
},
"checkbox": {
"action": "checkbox",
"next": "checkbox_func"
},
"checkbox_func": {
"action": "checkbox_func",
"next": "checkbox_map"
},
"checkbox_map": {
"action": "checkbox_map",
"next": "checkbox_msg"
},
"checkbox_msg": {
"action": "checkbox_msg",
"next": "message"
},
"email": {
"action": "email",
"next": "message"
},
"phone": {
"action": "phone",
"next": "message"
},
"date": {
"action": "date",
"next": "message"
},
"date_str": {
"action": "date_str",
"next": "message"
},
"date_year": {
"action": "date_year",
"next": "message"
},
"number": {
"action": "number",
"next": "message"
},
"integer": {
"action": "integer",
"next": "message"
},
"tags": {
"action": "tags"
},
"barcode": {
"action": "barcode",
"next": "barcode_map"
},
"barcode_map": {
"action": "barcode_map",
"next": "barcode_msg"
},
"barcode_msg": {
"action": "barcode_msg",
"next": "message"
},
"geobarcode": {
"action": "geobarcode",
"next": "geobarcode_map"
},
"geobarcode_map": {
"action": "geobarcode_map",
"next": "geobarcode_confirm"
},
"geobarcode_confirm": {
"action": "geobarcode_confirm",
"next": {
"yes": "geobarcode_msg",
"no": "geobarcode_msg"
}
},
"geobarcode_msg": {
"action": "geobarcode_msg",
"next": "message"
},
"file": {
"action": "file",
"next": {
"image": "image",
"audio": "audio",
"video": "video",
"pdf": "pdf",
"address": "address",
"medias": "medias",
"multimedia": "multimedia",
"delays": "msg_with_delay_one"
}
},
"image": {
"action": "image"
},
"audio": {
"action": "audio"
},
"video": {
"action": "video"
},
"pdf": {
"action": "pdf"
},
"address": {
"action": "address"
},
"medias": {
"action": "image",
"next": "audio"
},
"multimedia": {
"action": "image",
"next": "radio_v1"
},
"msg_with_delay_one": {
"action": "msg_with_delay_one",
"next": "msg_with_delay_two"
},
"msg_with_delay_two": {
"action": "msg_with_delay_two",
"next": "message"
}
},
"actions": {
"message": {
"type": "send_message",
"vars": {},
"messages": [
"EOF"
]
},
"signature_message": {
"type": "send_message",
"vars": {},
"messages": [
"{{ transition.signature.output.answer.url | safe }}"
]
},
"tags": {
"type": "send_message",
"vars": {},
"messages": [
"{{ variables.user.tags | join(',') }}"
]
},
"message_a0": {
"type": "send_message",
"vars": {},
"messages": [
"A: {{ transition.radio.output }}"
]
},
"message_b0": {
"type": "send_message",
"vars": {},
"messages": [
"B: {{ transition.radio.output }}"
]
},
"message_a1": {
"type": "send_message",
"vars": {},
"messages": [
"A: {{ transition.radio_v1.output }}"
]
},
"message_b1": {
"type": "send_message",
"vars": {},
"messages": [
"B: {{ transition.radio_v1.output }}"
]
},
"number": {
"type": "send_message",
"vars": {},
"messages": [
"Number:"
],
"input": {
"method": "keyboard",
"type": "number",
"display": "vertical",
"data": []
}
},
"integer": {
"type": "send_message",
"vars": {},
"messages": [
"Integer:"
],
"input": {
"method": "keyboard",
"type": "number",
"display": "vertical",
"data": [
{
"placeholder": "Integer in 1 and 200"
},
{
"integer": true
},
{
"min": 1
},
{
"max": 200
}
]
}
},
"radio": {
"type": "send_message",
"vars": {},
"messages": [
"Radio:"
],
"input": {
"method": "keyboard",
"type": "radio",
"data": [
{
"a": "A"
},
{
"b": "B"
}
]
}
},
"radio_v1": {
"type": "send_message",
"vars": {},
"messages": [
"Radio v1:"
],
"input": {
"method": "keyboard",
"type": "radio",
"data": [
{
"id": "a",
"label": "A"
},
{
"id": "b",
"label": "B"
}
]
}
},
"password": {
"type": "send_message",
"vars": {},
"messages": [
"Password:"
],
"input": {
"type": "password",
"method": "keyboard",
"data": [
{
"subtype": "number"
},
{
"size": 4
},
{
"confirm": true
},
{
"confirm_message": "puntoscolombia_ask_pin_confirm_message"
},
{
"fail_message": "puntoscolombia_ask_pin_fail_message"
},
{
"pub": "PUBKEY"
}
]
}
},
"checkbox": {
"type": "send_message",
"vars": {},
"messages": [
"Checkbox:"
],
"input": {
"method": "keyboard",
"type": "checkbox",
"display": "vertical",
"data": [
{
"id": "a",
"label": "Option A"
},
{
"id": "b",
"label": "Long Option B"
},
{
"id": "c",
"label": "Long Long Option C"
},
{
"id": "d",
"label": "Option D"
},
{
"id": "e",
"label": "Long Option E"
}
]
}
},
"checkbox_func": {
"type": "call_function",
"vars": {},
"eval": {
"function": "get_checkbox_text",
"input": {
"data": "{{ transition.checkbox.output.answer.selected }}"
}
}
},
"checkbox_map": {
"type": "map_result",
"vars": {},
"eval": {
"targets": [
{
"from": "{{ transition.checkbox_func.output }}",
"assign": [
{
"map": "answer",
"to": "variables.session.choices"
}
]
}
]
}
},
"checkbox_msg": {
"type": "send_message",
"vars": {},
"messages": [
"{{ transition.checkbox.output }}",
{
"template": "{{ messages.checkbox_text }}",
"data": {
"number": "{{ variables.session.choices.number }}",
"text": "{{ variables.session.choices.text }}"
}
}
]
},
"email": {
"type": "send_message",
"vars": {},
"messages": [
"Email:"
],
"input": {
"method": "keyboard",
"type": "email",
"data": []
}
},
"phone": {
"type": "send_message",
"vars": {},
"messages": [
"Phone:"
],
"input": {
"method": "keyboard",
"type": "phone",
"data": []
}
},
"date": {
"type": "send_message",
"vars": {},
"messages": [
"Date (Integers):"
],
"input": {
"type": "date",
"data": [
{
"format": "dd/mm/YYYY"
},
{
"placeholder": "dd/mm/YYYY"
}
]
}
},
"date_str": {
"type": "send_message",
"vars": {},
"messages": [
"Date (Strings):"
],
"input": {
"type": "date",
"data": [
{
"format": "AAAA-MM-DD"
},
{
"default": {
"day": "15",
"month": "12",
"year": "2018"
}
},
{
"placeholder": "AAAA-MM-DD"
},
{
"default_day": "01"
},
{
"default_month": "01"
},
{
"default_year": "2000"
}
]
}
},
"date_year": {
"type": "send_message",
"vars": {},
"messages": [
"Date (Now):"
],
"input": {
"type": "date",
"data": [
{
"format": "AAAA-MM-DD"
},
{
"default": {}
},
{
"placeholder": "AAAA-MM-DD"
},
{
"default_day": "01"
},
{
"default_month": "01"
},
{
"default_year": "2000"
}
]
}
},
"location": {
"type": "send_message",
"vars": {},
"messages": [
"Current Location:"
],
"input": {
"type": "location",
"method": "keyboard",
"data": [
{
"subtype": "current"
},
{
"format": "degrees"
},
{
"timestamp": true
},
{
"accuracy": true
}
]
}
},
"map": {
"type": "send_message",
"vars": {},
"messages": [
"Map Location:"
],
"input": {
"type": "location",
"method": "keyboard",
"data": [
{
"subtype": "map"
},
{
"format": "degrees"
},
{
"timestamp": true
},
{
"accuracy": true
}
]
}
},
"media": {
"type": "send_message",
"vars": {},
"messages": [
"Media:"
],
"input": {
"type": "media",
"method": "keyboard",
"data": [
{
"media_type": "image"
}
]
}
},
"gallery": {
"type": "send_message",
"vars": {},
"messages": [
"Gallery:"
],
"input": {
"type": "media",
"method": "keyboard",
"geotag": false,
"data": [
{
"media_type": "image"
},
{
"picker": "gallery"
}
]
}
},
"camera": {
"type": "send_message",
"vars": {},
"messages": [
"Camera:"
],
"input": {
"type": "media",
"method": "keyboard",
"geotag": false,
"data": [
{
"media_type": "image"
}
]
}
},
"camera_all": {
"type": "send_message",
"vars": {},
"messages": [
"Camera All:"
],
"input": {
"type": "media",
"method": "keyboard",
"geotag": false,
"data": [
{
"media_type": "image"
},
{
"picker": "all"
}
]
}
},
"camera_front": {
"type": "send_message",
"vars": {},
"messages": [
"Camera Front:"
],
"input": {
"type": "media",
"method": "keyboard",
"geotag": false,
"data": [
{
"media_type": "image"
},
{
"picker": "camera"
},
{
"facing": "front"
}
]
}
},
"camera_back": {
"type": "send_message",
"vars": {},
"messages": [
"Camera Back:"
],
"input": {
"type": "media",
"method": "keyboard",
"geotag": false,
"data": [
{
"media_type": "image"
},
{
"picker": "camera"
},
{
"facing": "back"
}
]
}
},
"signature": {
"type": "send_message",
"vars": {},
"messages": [
"Signature:"
],
"input": {
"type": "signature",
"method": "keyboard",
"geotag": true,
"data": []
}
},
"geomedia": {
"type": "send_message",
"vars": {},
"messages": [
"Media with Location:"
],
"input": {
"type": "media",
"geotag": true,
"method": "keyboard",
"data": [
{
"media_type": "image"
}
]
}
},
"geomedia_msg": {
"type": "send_message",
"vars": {},
"messages": [
{
"template": "{{ messages.media_geotext }}",
"data": {
"url": "{{ transition.geomedia.output.answer.url }}",
"lat": "{{ transition.geomedia.output.location.latitude }}",
"lng": "{{ transition.geomedia.output.location.longitude }}",
"tsp": "{{ transition.geomedia.output.location.timestamp }}",
"acc": "{{ transition.geomedia.output.location.accuracy }}"
}
}
]
},
"barcode": {
"type": "send_message",
"vars": {},
"messages": [
"Barcode:"
],
"input": {
"method": "keyboard",
"type": "barcode",
"display": "vertical",
"data": [
{
"response": "Your Barcode was sent!"
}
]
}
},
"barcode_map": {
"type": "map_result",
"vars": {},
"eval": {
"targets": [
{
"from": "{{ transition.barcode }}",
"assign": [
{
"map": "output.answer",
"to": "variables.session.barcode"
}
]
}
]
}
},
"barcode_msg": {
"type": "send_message",
"vars": {},
"messages": [
{
"template": "{{ messages.barcode_text }}",
"data": {
"format": "{{ variables.session.barcode.format }}",
"raw": "{{ variables.session.barcode.raw }}",
"type": "{{ variables.session.barcode.type }}",
"value": "{{ variables.session.barcode.value }}"
}
}
]
},
"geobarcode": {
"type": "send_message",
"vars": {},
"messages": [
"Barcode with Location:"
],
"input": {
"method": "keyboard",
"type": "barcode",
"geotag": true,
"display": "vertical",
"data": [
{
"response": "Your Barcode was sent with geotagging!"
}
]
}
},
"geobarcode_map": {
"type": "map_result",
"vars": {},
"eval": {
"targets": [
{
"from": "{{ transition.geobarcode }}",
"assign": [
{
"map": "output.answer",
"to": "variables.session.barcode"
},
{
"map": "output.location",
"to": "variables.session.geobarcode"
}
]
}
]
}
},
"geobarcode_confirm": {
"type": "send_message",
"vars": {},
"messages": [
"Is this your current location?"
],
"input": {
"method": "keyboard",
"type": "radio",
"display": "vertical",
"data": [
{
"id": "yes",
"label": "Yes"
},
{
"id": "no",
"label": "No"
}
]
},
"metadata": {
"media": "map",
"data": "{ \"lat\": {{ variables.session.geobarcode.latitude }}, \"lng\": {{ variables.session.geobarcode.longitude }}, \"address\": \"\", \"name\": \"\" }"
}
},
"geobarcode_msg": {
"type": "send_message",
"vars": {},
"messages": [
{
"template": "{{ messages.barcode_geotext }}",
"data": {
"format": "{{ variables.session.barcode.format }}",
"raw": "{{ variables.session.barcode.raw }}",
"type": "{{ variables.session.barcode.type }}",
"value": "{{ variables.session.barcode.value }}",
"lat": "{{ variables.session.geobarcode.latitude }}",
"lng": "{{ variables.session.geobarcode.longitude }}",
"tsp": "{{ variables.session.geobarcode.timestamp }}",
"acc": "{{ variables.session.geobarcode.accuracy }}"
}
}
]
},
"file": {
"type": "send_message",
"vars": {},
"messages": [
{
"template": "{{ messages.ask_file_msg }}",
"data": {}
}
],
"input": {
"method": "keyboard",
"type": "radio",
"display": "vertical",
"data": [
{
"image": "Image"
},
{
"audio": "Audio"
},
{
"video": "Video"
},
{
"pdf": "PDF"
},
{
"address": "Map"
},
{
"medias": "Medias"
},
{
"multimedia": "Multimedia"
},
{
"delays": "Delays"
}
]
}
},
"image": {
"type": "send_message",
"vars": {
"chosen": "{{ ['https://twnelusermedia.s3-us-west-2.amazonaws.com/rpi.png','https://images.barcodelookup.com/1073/10737859-1.jpg','https://i.pinimg.com/originals/90/d8/cf/90d8cf5d0b5ede7751fb0d4f30147ee9.png'] | random }}"
},
"messages": [
"{{ vars.chosen }}"
],
"media_url": "{{ vars.chosen }}",
"metadata": {
"media": "image"
},
"delay": 1
},
"audio": {
"type": "send_message",
"vars": {},
"messages": [
""
],
"media_url": "{{ files.audio }}",
"metadata": {
"media": "audio"
}
},
"video": {
"type": "send_message",
"vars": {},
"messages": [
""
],
"media_url": "{{ files.video }}",
"metadata": {
"media": "video"
}
},
"pdf": {
"type": "send_message",
"vars": {},
"messages": [
""
],
"media_url": "{{ files.pdf }}",
"metadata": {
"media": "application/pdf",
"filename": "some name"
}
},
"address_raw": {
"type": "send_message",
"vars": {},
"messages": [
""
],
"metadata": {
"media": "map",
"data": {
"lat": 3.4982500076293945,
"lng": -76.47799682617188,
"address": "Street A XYZ ",
"name": "Some Building"
}
}
},
"address": {
"type": "send_message",
"vars": {},
"messages": [
""
],
"metadata": {
"media": "map",
"data": "{ \"lat\": {{ variables.session.location.latitude }}, \"lng\": {{ variables.session.location.longitude }}, \"address\": \"Street A XYZ \", \"name\": \"Some Building\" }"
}
},
"radio_media": {
"type": "send_message",
"vars": {},
"messages": [
"Please select 1 option"
],
"input": {
"method": "keyboard",
"type": "radio",
"data": [
{
"id": "yes",
"label": "Yes"
},
{
"id": "no",
"label": "No"
}
]
},
"metadata": {
"media": "map",
"data": "{ \"lat\": {{ variables.session.location.latitude }}, \"lng\": {{ variables.session.location.longitude }}, \"address\": \"\", \"name\": \"\" }"
}
},
"msg_with_delay_one": {
"type": "send_message",
"vars": {},
"messages": [
"Message and delay 1"
],
"delay": 3
},
"msg_with_delay_two": {
"type": "send_message",
"vars": {},
"messages": [
"Message and delay 2"
],
"delay": 4
}
},
"functions": {
"get_checkbox_text": {
"runtime": "js",
"version": "5",
"code": "function get_checkbox_text(input) { var output = { 'number': 0, 'text': '' }; var data = []; if (input && ('data' in input) && Array.isArray(input['data'])) { data = input['data']; } var numChoices = data.length; if (numChoices) { output['number'] = numChoices; var choices = []; for (var i = 0; i < numChoices; i++) { if (('id' in data[i]) && ('label' in data[i])) { choices.push(data[i]['id'] + '-' + data[i]['label']); } } output['text'] = choices.join(','); } return output; }",
"input": {
"data": "array"
},
"output": {
"type": "object",
"values": [],
"sample": {
"number": 0,
"text": "text"
}
}
}
}
}
Simple spec that shows how to use all input types, media files and basic radio usage.
Description
This is a simple bot that shows how to use all inputs described before. The functionality just consists of 2 steps:
- Ask an input with the right type and wait until input is provided
- Send a final message
Also, this spec shows how to make the bot even more interactive by sending:
- Images
- Audios
- Videos
- PDF Files
- Map Locations
Define all URLs at files
section and inside send_message
actions, set the metadata
object as
shown in order to let Twnel clients know how to display the information.
Platform APIs
When developing bots or integrating APIs for automation purposes, these APIs are useful:
send_one_message
send media message
curl -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer TOKEN" \
https://api.production.twnel.com/messages \
-d '{"to": "PHONE_NUMBER", "body": "TEXT", "mediaUrl": "PUBLIC_MEDIA_URL" }'
Send a message to an user by specifying a valid E164 phone number, text and media url
put_contact
create or update contact
curl -X PUT \
-H "Content-Type: application/json" \
-H "Authorization: Bearer TOKEN" \
https://api.production.twnel.com/conversations/contacts \
-d '{"phone": "PHONE_NUMBER", "name": "NAME", "tags": ["T1", "T2"] }'
This endpoint will normalize provided tags according to the Tag Spec (ASCII, Uppercase)
delete_contact
delete contact
curl -X DELETE \
-H "Content-Type: application/json" \
-H "Authorization: Bearer TOKEN" \
https://api.production.twnel.com/conversations/contacts/{PHONE}
Delete a contact by specifying the phone number
search_contact
search
curl -X GET \
-H "Content-Type: application/json" \
-H "Authorization: Bearer TOKEN" \
https://api.production.twnel.com/contacts/search\?q\=310
curl -X GET \
-H "Content-Type: application/json" \
-H "Authorization: Bearer TOKEN" \
https://api.production.twnel.com/contacts/search\?q\=andres
curl -X GET \
-H "Content-Type: application/json" \
-H "Authorization: Bearer TOKEN" \
https://api.production.twnel.com/contacts/search\?tags\=STAFF,UI
A q
string parameter is provided to search by phone number or name. To search based on tags, add a new tags
query string with a comma-separated value as shown.
list_superbots
list available superbots
curl -X GET \
-H "Content-Type: application/json" \
-H "Authorization: Bearer TOKEN" \
https://api.production.twnel.com/companies/superbots
Returns active superbots indicating both the chatbot id and name. The id
is useful in order to trigger a bot using the trigger_chatbot
endpoint
trigger_chatbot
trigger chatbot
curl -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer TOKEN" \
https://api.production.twnel.com/messages/chatbots \
-d '{ "chatbot_id": "CHATBOT_ID" "phone": "PHONE_NUMBER" }'
By providing a valid chatbot_id, this API activates the chatbot for a contact
start_chat_deep_link
deep link to start a b2c chat with a company
curl -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer TOKEN" \
https://api.production.twnel.com/companies/deeplinks \
-d '{ "action": "start_chat", "message": "STARTING_MESSAGE", "send": true/false,
"phone": "PHONE_NUMBER" }'
Full integration support with third-party applications through deep-linking. Useful for opening conversations and sending messages from other apps. This service extends deep link functionality for installation and redirects a user to a company chatroom with a starting message. A flag is included to control whether to just fill the text field or also send the message automatically.
- action controls which kind of link to generate. Only start_chat is supported
- message to include as starting message for a new chat with a company. Maximum length is 1792 characters
- 1 unique link will be generated. By default no phone is needed given link uniqueness
- send parameter controls whether to just fill the text field or also send the message automatically
- Parameter phone can be specified to tie the link to a particular user (client validates it)
B2C Deep Link Example
{
"link": “https://twnel.link/pGWcKnUkeGqSoMPEA"
}
Mobile Client
The client will use the deep link to send the message to the company on behalf of the user. If the phone parameter arrives, the client checks if there is a match between the deep link phone parameter and the account owner. if send parameter is “false“, client will fill the text field; if “true“, it will fill the text field and send the message automatically.
Integration APIs
Retrieve Records from Google Spreadsheet
Get Record Example
{
"spreadsheetId": "XXX",
"userProfileId": "YYY",
"range": "'Sheet1'!A1:Z",
"labels": [
"nombre",
"celular"
],
"query": {
"celular": "ZZZ",
"nombre": ["[!]WWW"]
},
"returnOnly": "all"
}
https://apis.twnel.io/google/get_records
Method POST
Description Retrieves records from a specified Google Spreadsheet based on the provided query parameters. It supports filtering by multiple criteria and can return all matches, the first match, or the last match.
Parameters
- spreadsheetId (string): The ID of the Google Spreadsheet from which records are to be retrieved.
- userProfileId (string): The profile ID of the user requesting the data.
- range (string): The range of cells to search within the spreadsheet. For example,
'Sheet1'!A1:Z
. - labels (array of strings): The labels corresponding to the columns in the spreadsheet. This defines how the data should be interpreted.
- query (object): The criteria to filter records.
- returnOnly (string): Specifies the type of records to return. Valid values are:
all
: Returns all matching records.firstMatch
: Returns only the first matching record.lastMatch
: Returns only the last matching record.
Notes
- The
range
parameter should be specified according to the sheet name and the cell range to search within. - The
query
object uses arrays for OR logic (multiple matches) within each property and treats each property as an AND condition. - Use
[!]
to denote a negative match for a query value.
Create Records in Google Spreadsheet
Create Record Example
{
"spreadsheetId": "XXX",
"userProfileId": "YYY",
"range": "'Sheet1'!A1:Z",
"labels": [
"documento",
"date"
],
"data": [
{
"documento": "=HYPERLINK(\"google.com\", \"hyperlink\")"
}
],
"addDate": true,
"addDateLabel": "date",
"locales": "es-CO",
"formatDate": {
"timeZone": "America/Bogota"
}
}
https://apis.twnel.io/google/create_records
Method POST
Description Allows for the creation of new records in a specified Google Spreadsheet. It supports the insertion of data including Google Sheets formulas and the automatic addition of timestamps.
Parameters
- spreadsheetId (string): The ID of the Google Spreadsheet where records will be created.
- userProfileId (string): The profile ID of the user creating the data.
- range (string): The range of cells to insert data within the spreadsheet. For example,
'Sheet1'!A1:Z
. - labels (array of strings): The labels corresponding to the columns in the spreadsheet. This defines the structure of the data to be inserted.
- data (array of objects): The data to be inserted. Each object represents a row, and can include Google Sheets formulas.
- addDate (boolean): If
true
, a timestamp will be added to each row. - addDateLabel (string): The label for the column where the timestamp will be inserted. Required if
addDate
istrue
. - locales (string): Specifies the locale for date formatting. Follows the IETF BCP 47 language tag format (e.g.,
es-CO
). - formatDate (object): Options for date formatting.
- timeZone (string): The time zone for the timestamp. For example,
America/Bogota
.
- timeZone (string): The time zone for the timestamp. For example,
Notes
- The
data
parameter should be an array of objects where each object represents a row to be inserted into the spreadsheet. - The
addDate
parameter allows for automatic timestamp insertion. When enabled,addDateLabel
must specify the column label for the timestamp. - The
locales
parameter specifies the locale for the timestamp format. Refer to the Intl.DateTimeFormat documentation for more details on locale and formatting options. - The
formatDate
object allows customization of the timestamp's time zone and other formatting options.
Update Records in Google Spreadsheet
Update Record Example
{
"spreadsheetId": "XXX",
"userProfileId": "YYY",
"range": "'Sheet1'!A1:Z",
"labels": [
"documento"
],
"query": {
"documento": "5"
},
"returnOnly": "lastMatch",
"update": {
"documento": "50"
}
}
https://apis.twnel.io/google/update_records
Method PATCH
Description Updates existing records in a specified Google Spreadsheet based on provided query parameters. It supports partial updates, where only specified fields are changed, and other fields retain their existing values.
Parameters
For other parameters, see the section on the creation of records
- update (object): The data to be updated. Only the specified fields will be updated, and other fields will retain their existing values.
Notes
- The
update
object contains the new data to replace the old values. If some columns are not included inupdate
, their existing values will remain unchanged.
Delete Records in Google Spreadsheet
Delete Record Example
{
"spreadsheetId": "XXX",
"userProfileId": "YYY",
"range": "'Sheet1'!A1:G",
"labels": [
"documento"
],
"query": {
"documento": 1352
}
}
https://apis.twnel.io/google/delete_records
Method DELETE
Description Deletes records from a specified Google Spreadsheet based on provided query parameters.
Parameters
See the section on obtaining records
Notes
- The
query
object uses fields to specify the criteria for matching records to be deleted. - Be careful with using negative queries, you may eliminate desired records.
Create PDF from Google Docs Template
Create PDF Example
{
"userProfileId": "XXX",
"templateId": "YYY",
"folderId": "ZZZ",
"requests": [
{
"type": "text",
"value": "REPLACED TEXT",
"match": "<<TEXT>>"
},
{
"type": "text",
"value": "BOLD TEXT",
"options": {
"bold": true,
"alignment": "CENTER",
"fontSize": {
"magnitude": 16,
"unit": "PT"
}
}
},
{
"type": "image",
"value": "URL",
"options": {
"alignment": "CENTER"
}
},
{
"type": "table",
"value": [
["A", "B", "C"],
["1", "2", "3"]
]
}
]
}
https://apis.twnel.io/google/create_pdf
Method POST
Description Creates a PDF from a specified Google Docs template, allowing for dynamic insertion and replacement of text, images, and tables. It supports text styling and paragraph formatting.
Parameters
- userProfileId (string): The profile ID of the user creating the PDF.
- templateId (string): The ID of the Google Docs template to be used.
- folderId (string): The ID of the folder where the created PDF will be saved.
- requests (array of objects): The main parameter that defines the content to be inserted or replaced in the template.
Request Types
text: Inserts or replaces text in the template.
- value (string): The new text value to be inserted or used for replacement.
- match (string, optional): The placeholder in the template to be replaced. If provided, this text will replace the matching placeholder.
- options (object, optional): Paragraph and text style options for the text.
image: Inserts or replaces an image in the template.
- value (string): The URL of the image to be inserted.
- match (boolean, optional): If set to
true
, replaces the first image in the template, from top to bottom, left to right. - options (object, optional): Paragraph style options for the image.
table: Inserts a table in the template.
- value (array of arrays): The matrix of the table data to be inserted.
- options (object, optional): Paragraph style options for the table.
Notes
- The
userProfileId
,templateId
, andfolderId
are required parameters. - The
requests
array can contain multiple request objects of different types (text, image, table). - The
options
only apply to insertions, not replacements (with any valid value inmatch
). - For more information on paragraph and text styling options, refer to the ParagraphStyle and TextStyle documentation.
OCR Invoice Processing
OCR Invoice Example
{
"userProfileId": "XXX",
"url": "URL",
"schema": {
"type": "object",
"properties": {
"documento": {
"type": "string",
"description": ""
}
}
}
}
https://apis.twnel.io/google/invoice_ocr
Method POST
Description Processes an invoice image using Optical Character Recognition (OCR) and extracts data according to a specified JSON Schema.
Parameters
- userProfileId (string): The profile ID of the user requesting the OCR processing.
- url (string): The URL of the invoice image to be processed.
- schema (object): The JSON Schema that defines the structure of the data to be extracted from the invoice.
Notes
- The
userProfileId
andurl
are required parameters. - The
schema
parameter must be a valid JSON Schema, which defines the structure and data types of the expected extracted data. - For more information on JSON Schema, refer to the JSON Schema documentation.
Send Email
Send Email Example
{
"userProfileId": "XXX",
"text": "<p>Hi, world!</p> <img src=\"cid:image_example\"/>",
"to": ["example@domain.com"],
"subject": "Test",
"attachments": [
{
"path": "URL",
"contentType": "image/png",
"name": "image_example.png"
}
]
}
https://apis.twnel.io/google/send_email
Method POST
Description Sends an email with the specified content, recipients, subject, and attachments.
Parameters
- userProfileId (string): The profile ID of the user requesting the sending of e-mails.
- text (string): The HTML content of the email body. This can include HTML tags such as
<p>
,<img>
, etc. - to (array of strings): A list of recipient email addresses.
- subject (string): The subject of the email.
- attachments (array of objects, optional): A list of attachments to be included in the email.
- path (string): The URL or path to the attachment file.
- contentType (string): The MIME type of the attachment (e.g.,
image/png
). - name (string): The name of the attachment file as it will appear in the email.
Notes
- The
text
parameter should contain the HTML body of the email. Inline images can be included using thecid
scheme. - The
to
parameter must be an array of valid email addresses. - The
attachments
parameter is optional and should contain valid URLs or paths to the attachment files, along with their corresponding MIME types and filenames.