We have created a separate service for statistics about your Kindly chatbots. This service is named Sage and lives at sage.kindly.ai. The statistics you can view in the Kindly platform are provided by Sage. The same API that the platform uses is available for you to collect statistics for your own purposes.
Sage has it's own database which contains only non-personal data. When end users chat with chatbots their messages are stored and processed in Kindly's database, and these can be manually or automatically deleted after some time to protect the end user's privacy. The data in Sage's database will not be deleted, but this data doesn't contain the user's message or any personal metadata, only an indication that someone messaged the bot at some specific time. This way we can keep aggregated statistics such as the number of chats per day, while also allowing the end user to have their privacy protected.
Authentication
To access Sage's API, you need to obtain a JWT from Kindly. This token is signed by Kindly and Sage checks the signature to decide whether to allow or deny your API request. Use this token as your authorization header in your API requests. The tokens have short lifetimes, so you can only use the token for a short while before you need to obtain another one.
Obtaining a JWT programmatically
If you have the required permissions, you can create a permanent API key in the platform. This long-lived key can be used by a programmatic service you create to obtain short-lived JWTs which can then be used for statistics requests.
All time values in the database are stored as UTC. If you want results in your local timezone, which takes into consideration daylight saving time and other irregularities, you can provide a tz argument to your API request with the tzdb name of the timezone you want:
GET https://sage.kindly.ai/api/...&tz=Europe/Oslo
⚠ Timezone gotchas
The timezone parameter is important to consider if you are making a request where the data is aggregated by day. If someone messages the bot at 9 p.m. (summer time) on Monday in California, this is stored in the database as occurring at 4 a.m. on Tuesday. To get the API results scoped for California time, you would send a request with tz=America/Los_Angeles.
About granularity
Some aggregations can use a granularity parameter, for example the aggregation that counts the number of messages per hour, day or week. You can specify this with granularity=hour, etc. These default to granularity=day if not otherwise specified. Note that hour granularity is only allowed if the time between from and to is one week or less.
About sources and languages
The events that are aggregated by Sage are labeled by which source and language the user and bot were chatting with. Your API requests can be filtered by these values. Not adding any filters to your request will include all data in the results, unfiltered.
Sources
Sources are the different locations where your bot may be deployed. For example:
test is source value for conversations by bot builders within the Kindly platform.
web is the source value for Kindly Chat deployed on web sites, excluding the Kindly platform.
If you do not filter by sources, all data will be included in the results, including test data. To select only specific sources, you can send one or more filters like this:
GET https://sage.kindly.ai/api/...&sources[]=web&sources[]=app
You can also select sources to exclude, by prefixing with a minus sign. This means that the API will return data from every source except the ones listed. GET https://sage.kindly.ai/api/...&sources[]=-test
Language codes
The language used to chat between the user and the bot is represented by a two-letter language code. If your bot does not use multiple languages, you can safely ignore this parameter.
If you have multiple languages in your bot and you want to filter by language, you can send one or more filters like this:
GET https://sage.kindly.ai/api/...&language_code[]=en&language_code[]=nb
Statistics APIs
Filtering options
Most of the APIs can be filtered by time, language and source. To see what your options are for these filters, you can send a request to the /meta/options API.
count represents the number of fallback messages in the given time interval, rate represents which fraction of the total number of bot replies in the time interval are fallbacks.
count represents the number of fallback messages in the given time interval, rate represents which fraction of the total number of bot replies in the time interval are fallbacks.
Lists most frequent web pages where interactions with the bot has happened. Returns top 3 pages by default, use limit parameter to request more results.
The number of handover requests (while open), requests while closed, started handovers and ended handovers in the requested time period, as a time series.
If you have enabled the feedback feature in Kindly Chat you can get a summary of the ratings given by users in the period. The API considers the ratings as numbers, with 1 being the lowest rating. Both the count of the number of ratings and the ratio out of the total number of ratings is given.
This API only returns aggregated data. If you want to analyze the feedback texts users have given you can get these from an inbox backup. See also: Daily data dump of Inbox to AWS S3 bucket
An ordered list of the buttons most clicked by users chatting with the bot, and the dialogue to which each button belongs. Has both a count of the number of occurrences, and a ratio out of the total amount of button clicks for the period. Returns top 5 results by default, use the limit parameter to request more data.
This example requires Python 3.6 or higher (uses f-strings) and the package requests.
You will need to provide two values yourself, the relevant bot ID and an API key with "read statistics" permissions that you have created at https://app.kindly.ai/bot/BOT_ID/connect/api-keys. The code exchanges the API key for a JWT, uses the JWT to get data about the number of messages per day for the bot's lifetime, then renders the data as a Unicode box-character bar chart.
from time import sleepfrom urllib.parse import urljoinimport requestsBOT_ID:int= REPLACE MEKINDLY_STATISTICS_API_KEY:str= REPLACE ME # get this from https://app.kindly.ai/bot/BOT_ID/connect/api-keysSAGE_API_ROOT ='https://sage.kindly.ai/api/v1/'JWT =Nonedefget_new_jwt(): url =f'https://api.kindly.ai/api/v2/bot/{BOT_ID}/sage/auth' response = requests.get(url, headers={'Authorization': f'Bearer {KINDLY_STATISTICS_API_KEY}'}) response.raise_for_status()return response.json()['jwt']defsage_request(endpoint,params=None):global JWT url =urljoin(SAGE_API_ROOT, f"stats/bot/{BOT_ID}/{endpoint.lstrip('/')}")whileTrue:if JWT isNone: JWT =get_new_jwt()try: response = requests.get(url, params, headers={'Authorization': f'Bearer {JWT}'}) response.raise_for_status()except requests.HTTPError as e:if e.response.status_code ==401:print('401: fetching new JWT before retrying') JWT =Nonecontinueif e.response.status_code ==429: wait_time = e.response.headers.get('retry-after', 10)print(f'429: rate limited: waiting {wait_time}s before retrying')sleep(wait_time)continueraisereturn response.json()['data']options =sage_request('/meta/options')first = options['first']last = options['last']granularities = options['granularities']language_codes = options['language_codes']sources = options['sources']print(f"""The earliest date with data is: {first}The latest date with data is: {last}Granularity options are: {granularities}Language options are: {language_codes}Source options are: {sources}""")messages =sage_request('/sessions/messages', {'from': first, 'to': last})highest =max(x['count'] for x in messages)if highest ==0:raiseException('No data found')# make a "sideways bar chart" with Unicode box charactersdefbar(label,n,scale=1): width = n // scaleif n ==0: bar =''elif width ==0: bar ='▍'else: bar ='█'* widthreturnf"{label}: {bar}{n}"LINE_WIDTH =80c = highest // LINE_WIDTHprint(f'Number of messages between {first} and {last}')print(*(bar(x['date'].split('T', 2)[0], x['count'], scale=c) for x in messages), sep='\n')