API documentation - Bot Analytics Export

The All Events Dump API returns the raw, event-level data behind Bot Analytics — every user message, the bot's response, whether it was handled, the session it belonged to, and the user's profile fields. Use it to feed your own data warehouse, BI tool or reporting pipeline instead of exporting from the dashboard by hand.

The API is region-specific: you call the authentication and analytics hosts of the region your workspace is hosted in.

📘

Before you start

You need an API credential with the Read Analytics Data scope, created in Admin Console → API credentials. See API credentials.

How it works

  1. Create an API credential in the Admin Console with the Read Analytics Data scope.
  2. Generate an access token from your region's authentication URL.
  3. Call the analytics endpoint for your region with the access token, a date range and pagination parameters.
  4. Page through the results until you have fetched all records.

Step 1: Create an API credential

  1. In the Admin Console, go to API credentials.
  2. Click Add credentials.
  3. Enter an Auth name (up to 50 characters) — name it after the consuming system, for example Snowflake – bot events.
  4. In Allowed scope, select Read Analytics Data.
  5. Click Create credentials.
  6. Copy the Client ID, Client secret, Username and Password, and store them in a secure location such as a secrets manager.
🚧

Secrets are shown only once

The client secret and password cannot be viewed again after you leave the page — not by you and not by Leena AI support. If you lose them, create a new credential and revoke the old one.

👍

Find your region

The How to use this key command shown on the credential screen already contains your region's authentication URL (for example https://us-east-1-acl.leena.ai/...). Use the same region for the analytics URL below.

Step 2: Choose your regional base URLs

Use the Auth URL to generate tokens and the Analytics URL to fetch data. Both must belong to the same region — a token issued in one region is not valid in another.

RegionAuth URLAnalytics URL
US East (us-east-1)https://us-east-1-acl.leena.aihttps://us-east-1-analytics-api.leena.ai
EU West (eu-west-1)https://eu-west-1-acl.leena.aihttps://eu-west-1-analytics-api.leena.ai
EU Central (eu-central-1)https://eu-central-1-acl.leena.aihttps://eu-central-1-analytics-api.leena.ai
Canada Central (canadacentral)https://canadacentral-acl.leena.aihttps://canadacentral-analytics-api.leena.ai
Asia Pacific – Singapore (ap-southeast-1)https://ap-southeast-1-acl.leena.aihttps://ap-southeast-1-analytics-api.leena.ai
Asia Pacific – India (ap-south-1)https://acl.leena.aihttps://analytics-api.leena.ai
Qatar Central (qatarcentral)https://qatarcentral-acl.leena.aihttps://qatarcentral-analytics-api.leena.ai
Middle East Central 2 (me-central2)https://me-central2-acl.leena.aihttps://me-central2-analytics-api.leena.ai
📘

Dedicated instances

If your workspace runs on a dedicated instance, your hosts use your instance name instead of a region code. Contact your Leena AI representative for the exact URLs.

In the examples below, replace <AUTH_URL> and <ANALYTICS_URL> with the values for your region.

Step 3: Generate an access token

Send a POST request to your region's token endpoint. The Authorization header is HTTP Basic auth built from clientId:clientSecret, Base64-encoded. The username and password go in the request body.

Endpoint

POST <AUTH_URL>/api/v1.0/oauth/token

Request

curl --location '<AUTH_URL>/api/v1.0/oauth/token' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Basic <base64(clientId:clientSecret)>' \
  --data '{
    "username": "<username>",
    "password": "<password>",
    "grant_type": "password"
  }'

Response — 200 OK

{
  "token_type": "Bearer",
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expires_in": 1800,
  "refresh_token": "82e2cbfa-4d1a-42fc-857e-7da2faad7b70"
}
🚧

Tokens expire after 30 minutes

expires_in is in seconds. Generate a new token, or use the refresh token with POST <AUTH_URL>/api/v1.0/oauth/refresh-token, before the access token expires. Long exports that page through many results should check token age between requests.

For more on grant types, refresh tokens and credential rotation, see API credentials – token generation.

Step 4: Fetch all events

Endpoint

GET <ANALYTICS_URL>/api/analytics/query/external

Headers

HeaderValue
AuthorizationBearer <access_token>

You don't need to pass a bot ID. The workspace is identified from the access token.

Query parameters

ParameterTypeRequiredDescription
metricIdstringYesFixed value: botAnalytics:allEventsDump (URL-encoded: botAnalytics%3AallEventsDump).
isCustomMetricbooleanYesFixed value: true.
responseTypestringYesFixed value: table.
fromstring (ISO 8601)YesStart of the date range, e.g. 2026-01-01 or 2026-01-01T00:00:00+05:30. A date without a time is read as 00:00 UTC.
tostring (ISO 8601)YesEnd of the date range, in the same format as from. To cover all of January, use from=2026-01-01&to=2026-02-01.
pageintegerNoPage number, starting at 1. Default: 1.
limitintegerNoNumber of records per page. Default: 10.
timezonestring (IANA)NoTimezone used to render time values, e.g. America/New_York. Default: Asia/Calcutta.

Request

curl -G '<ANALYTICS_URL>/api/analytics/query/external' \
  --header 'Authorization: Bearer <access_token>' \
  --data-urlencode 'metricId=botAnalytics:allEventsDump' \
  --data-urlencode 'isCustomMetric=true' \
  --data-urlencode 'responseType=table' \
  --data-urlencode 'from=2026-01-01' \
  --data-urlencode 'to=2026-02-01' \
  --data-urlencode 'page=1' \
  --data-urlencode 'limit=100'

Response — 200 OK

The example below is shortened to one column definition and one event.

{
  "isSuccess": true,
  "result": {
    "current": 1,
    "page": 1,
    "limit": 10,
    "total": 47668,
    "isManageColumnsEnabled": true,
    "columns": [
      {
        "key": "userMessage",
        "label": "User message",
        "type": "number",
        "sortable": true,
        "isDynamicColumn": false,
        "isMandatory": false,
        "isVisible": true,
        "sticky": false
      }
    ],
    "tableData": [
      {
        "employeeId": "EMP1024",
        "userMessage": "How many leaves do I have left?",
        "botResponse": "You have 12 days of annual leave remaining.",
        "timestamp": "Jan 14 2026, 3:08 PM",
        "utcTimestamp": "Jan 14 2026, 9:38 AM",
        "messageType": "plainText",
        "isHandled": "true",
        "isSurveyResponse": "false",
        "sessionId": "69676978e4451ee81cc38d48",
        "isEffective": "true",
        "returningUser": "false",
        "rating": "",
        "gptRequestId": "",
        "handledSubcategory": "",
        "skills": "",
        "articles": "",
        "Channel": "slack",
        "eventId": "6967640ed690eb787a38a218",
        "firstName": "Alex",
        "lastName": "Morgan",
        "email": "[email protected]",
        "displayName": "Alex Morgan"
      }
    ]
  }
}
FieldTypeDescription
isSuccessbooleantrue if the query succeeded.
result.tableDataarrayThe events on this page. Each element is one event, keyed by the column keys listed in Response columns.
result.columnsarrayColumn metadata. Each entry's key is the property name used in tableData, and label is its display name.
result.totalintegerTotal number of events across all pages for the date range.
result.pageintegerPage number returned. result.current holds the same value.
result.limitintegerPage size returned.
result.isManageColumnsEnabledbooleanDashboard display setting. Can be ignored.
📘

Read values as strings

All values in tableData are returned as strings:

  • True/false fields contain "true" or "false".
  • Empty fields contain "".
  • Timestamps are formatted as MMM DD YYYY, h:mm A, e.g. "Jan 14 2026, 3:08 PM".

The type property in result.columns describes how the dashboard renders a column, not the data type of its values. Don't use it to parse the data.

Response columns

Each event in tableData contains the following fields. Use the Key column to read values from each row. Keys are case-sensitive: note that Channel starts with a capital letter.

Event fields

These fields are always present.

KeyLabelDescription
employeeIdEmployee IdEmployee ID of the user. Empty if the user has no employee ID in their profile.
userMessageUser messageThe plain text, or the button title, that the user sent. System values such as INVALID_POSTBACK can appear for non-text events, for example file uploads.
botResponseBot responseThe bot's response to the user message. If the bot sent several messages in reply, they're joined with a semicolon (;).
timestampTimestampTime of the user message in the request timezone (see the timezone parameter; default Asia/Calcutta).
utcTimestampUtc TimestampTime of the user message in UTC.
messageTypeMessage TypeThe type of event, e.g. plainText (typed message), quickReply (button or quick-reply selection), fileUpload. Includes "message seen" events, which are not counted as interactions.
isHandledIs Handled"true" if the bot answered or executed the message or request.
isSurveyResponseIs Survey Response"true" if the message was a response to a survey.
sessionIdSession IdUnique ID of the user's session. A session ends after 30 minutes of inactivity.
isEffectiveIs Effective"true" if the session was effective. A session with no handled request or interaction is ineffective.
returningUserReturning User"true" if the user had two or more sessions in the selected date range.
ratingRatingThumbs up or thumbs down feedback the user gave on the bot response. Empty if no feedback was given.
gptRequestIdGpt Request IdUnique ID of the request. One request can include several interactions, messages or events. Use it to group events that belong to the same request.
handledSubcategoryHandled SubcategoryWhat happened to the request (see values below).
skillsSkillsSkills used to answer the user's query for the request.
articlesArticlesKnowledge articles used to answer the user's query. defaultFallback means the bot didn't understand the message and sent its fallback response.
ChannelChannelThe channel the event came from, e.g. slack, msteams, web.
eventIdEvent IdUnique ID of the event. Use it as the primary key when storing or de-duplicating events.

Handled Subcategory values

ValueMeaning
Handled successfullyThe request was executed completely and successfully.
Handled unsuccessfullyThe request could not be completed because of a validation error — for example, a leave application when the user has no leave balance.
FailedAn error occurred while executing the request.
User AbandonedThe user didn't respond to the bot's follow-up question after the initial query.
User AbortedThe user cancelled the request while the bot was generating the response.

User profile fields

After the event fields, each event includes the user's profile fields. Which fields appear depends on your workspace's user profile configuration, so the set varies between workspaces. For example:

KeyLabelDescription
firstNameFirst NameUser's first name.
lastNameLast NameUser's last name.
displayNameDisplay NameUser's display name.
fullNameFull NameUser's full name.
emailEmailUser's email address.
organizationOrganizationUser's organization.
companyCompanyUser's company.
hrIdHr IdUser's HR system ID.
gradeGradeUser's grade or level.

Read result.columns to get the exact list of fields for your workspace. Fields that aren't filled in for a user are returned as "".

For definitions of sessions, interactions and handled queries, see the Bot Analytics glossary.

Paginating through results

Results are paginated. To fetch all events for a date range:

  1. Request page=1 with your chosen limit.
  2. Read result.total and calculate the number of pages: ceil(total / limit).
  3. Request each following page by incrementing page until you have fetched all pages.
👍

Best practices

  • Pull data in smaller date windows (for example one day or one week at a time) for high-volume workspaces. Large ranges and page sizes increase response time.
  • For scheduled syncs, request the window since your last successful run, e.g. yesterday's data each morning.
  • Responses are not cached, so every call returns the latest processed data. Recent events can take some time to be processed before they appear.
  • Store credentials in a secrets manager. Don't share them in documents, email or chat.

Errors

StatusMessageWhat to do
401You are not authorized to perform this actionThe Authorization header is missing, or the token is invalid or expired. Generate a new token. Check that the token and analytics URLs are from the same region.
401Invalid authorization header. Must be a bearer tokenSend the header as Authorization: Bearer <access_token>.
401You are not authorized to perform this action. Invalid scope.The credential doesn't have the Read Analytics Data scope. Create a credential with that scope.
401 (token endpoint)—Wrong client ID, client secret, username or password, or the credential has been revoked.
500Metric not foundCheck that metricId=botAnalytics:allEventsDump and isCustomMetric=true are both set.

Related pages


Did this page help you?