Skip to content

Analytics

Pull the same KPIs and clustered visitor questions your B2B dashboard shows: for a BI tool, a scheduled export, or an AI agent that needs to answer "how are we doing" without opening the dashboard.

Both endpoints here are read-only and delegate to the exact same engines the dashboard uses (the KPI board and the Top Questions page), so numbers you pull over the API never diverge from what you see in the app.

Get KPIs

GET /analytics/kpis returns key metrics for a period, each with its trend against the previous equivalent period.

Required scope: analytics:read

Query parameterTypeDescription
periodstringtoday, 7_days, 30_days, 90_days, year, total, or custom. Defaults to 30_days.
start_dateISO 8601Required when period=custom.
end_dateISO 8601Required when period=custom.
kpisstringComma-separated KPI ids. Defaults to conversations,visitor_messages,add_to_cart,conversion_rate,revenue_generated,csat. An unknown id rejects the whole request; the error lists every valid id.

Available KPI ids

conversations, visitor_messages, add_to_cart, conversion_rate, revenue_generated, csat, csat_ai, csat_human, interaction_rate, support_conversations, website_visits, appointments_booked, new_contacts, tickets_created, tickets_resolved, product_page_views, conversations_offensive. This list can grow; treat error.details.available on a 400 as the source of truth if you hardcode it.

website_visits and interaction_rate on long periods

Up to 90_days (and its previous-period trend), website_visits counts distinct visitors over the whole window. For year, total and custom ranges reaching further back than 180 days, both KPIs (and the trend) are computed from a daily roll-up: a visitor who came back on several days counts once per day, so the value is slightly higher than a whole-window distinct count. Sessions and conversations are unaffected.

Request

bash
curl "https://api.thehumind.com/public/v1/analytics/kpis?period=30_days&kpis=conversations,revenue_generated,csat" \
  -H "Authorization: Bearer hmd_live_..."

Response 200 OK

json
{
  "data": {
    "conversations": { "total": 842, "change_percent": 12.4, "format": "number" },
    "revenue_generated": { "total": 15420.5, "change_percent": -3.1, "format": "currency" },
    "csat": { "total": 87.5, "change_percent": 2.0, "format": "percent", "sample": 96 }
  },
  "period": {
    "id": "30_days",
    "start": "2026-05-08T00:00:00.000Z",
    "end": "2026-06-07T23:59:59.999Z"
  }
}
FieldTypeDescription
data.<kpi_id>.totalnumberThe metric's value for the period.
data.<kpi_id>.change_percentnumber | nullPercent change vs. the previous equivalent period. null when there's no comparable previous period (e.g. period=total).
data.<kpi_id>.formatstringnumber, percent, or currency: how to render total.
data.<kpi_id>.sampleintegerNumber of underlying observations. Present only on sample-based KPIs (csat, csat_ai, csat_human): render an em dash placeholder instead of the value when sample is 0, since a 0% CSAT with zero ratings would be misleading.

A custom window:

bash
curl "https://api.thehumind.com/public/v1/analytics/kpis?period=custom&start_date=2026-01-01&end_date=2026-03-31" \
  -H "Authorization: Bearer hmd_live_..."

Get top questions

GET /analytics/top-questions returns clustered visitor questions over a date range: volume, trend, a sparkline timeline, unanswered rate, and sample questions per group, the "what are my customers asking, and what am I failing to answer" view.

Required scope: analytics:read

Query parameterTypeDescription
start_dateISO 8601Required. Start of the window.
end_dateISO 8601Required. End of the window.
languagesstringComma-separated ISO 639-1 codes (e.g. fr,en).
entry_pointsstringComma-separated: launcher, product_page, gift_generator, shop_banner, quiz.
searchstringFree-text narrowing on the question groups (min 2 chars).
answeredtrue | falsetrue = only answered groups; false = only coverage gaps.
categorystringFilter to one question category.
sort_bystringcount (default), recency, or opportunity: sort by the biggest unanswered gaps first.
pageintegerPage index, 0-based. Defaults to 0.
limitintegerGroups per page (1 to 50). Defaults to 20.
include_statstrue | falseAdd period-level stats to the response (slower). Defaults to false.

Find the coverage gaps worth fixing first

sort_by=opportunity&answered=false surfaces the unanswered question clusters with the highest volume, the fastest way to prioritize what to add to your Knowledge base.

Request

bash
curl "https://api.thehumind.com/public/v1/analytics/top-questions?start_date=2026-05-01&end_date=2026-06-01&sort_by=opportunity&answered=false&limit=10" \
  -H "Authorization: Bearer hmd_live_..."

Response 200 OK

json
{
  "data": [
    {
      "_id": "65f1ab9c8e7d4a2b1c3d4e90",
      "title": "Do you ship to Canada?",
      "label": "shipping_canada",
      "title_is_thematic": false,
      "category": "shipping",
      "count": 34,
      "trend": { "direction": "growing", "change_percent": 41.2 },
      "timeline": [
        { "date": "2026-05-01T00:00:00.000Z", "count": 3 },
        { "date": "2026-05-02T00:00:00.000Z", "count": 5 }
      ],
      "timeline_unit": "day",
      "unanswered_count": 22,
      "actionable_unanswered_count": 20,
      "unanswered_percentage": 64.7,
      "sample_questions": [
        "Do you ship to Canada?",
        "Is international shipping to Canada available?"
      ],
      "conversation_ids": ["65f1ab9c8e7d4a2b1c3d4e5f", "65f1ab9c8e7d4a2b1c3d4e61"]
    }
  ],
  "pagination": { "page": 0, "limit": 10, "total": 27 }
}
FieldTypeDescription
_idstringInternal group id.
titlestringDisplay title: an AI-generated label when available, else the raw representative question text.
labelstring | nullAI canonical label. null until the labeling pass has processed the group.
title_is_thematicbooleantrue when title is an AI-generated thematic reformulation of a diverse cluster rather than a single canonical question.
categorystringQuestion category.
countintegerOccurrences in the window.
languagesobject[]Language distribution: { code, count, percentage }.
entry_pointsobject[]Entry-point distribution: { action, count, percentage }.
trendobject{ direction: "growing" | "stable" | "declining" | "new", change_percent }. change_percent is null when direction is new (no occurrence in the previous period).
timelineobject[]Occurrence counts bucketed over the whole window, zero-filled: { date, count }.
timeline_unitstringday, week, or month: the timeline's bucket size.
unanswered_countintegerOccurrences the quality classifier flagged as a coverage gap.
actionable_unanswered_countintegerSubset of unanswered_count with an actionable gap reason.
unanswered_percentagenumber | nullunanswered_count / evaluated, 0-100. null when nothing was evaluated.
unanswered_reasonsobject[]Why answers failed, most frequent first: { reason, count }.
avg_satisfactionnumber | nullAverage CSAT on threads behind this group, when available.
avg_conversion_intentnumber | nullAverage AI-estimated conversion intent.
cart_ratenumber | nullShare of occurrences that led to an add-to-cart.
handoff_ratenumber | nullShare of occurrences that led to a human handoff.
first_seen_at / last_seen_atISO 8601First and last occurrence in the window.
sample_questionsstring[]Up to 5 distinct sample question texts from this group.
conversation_idsstring[]Sample of up to 10 conversation humind_ids behind this group; pass to GET /conversations/{id}.

With include_stats=true, a stats object is added alongside data:

json
{
  "stats": {
    "total_questions": 512,
    "total_groups": 27,
    "grouped_percentage": 91.2,
    "product_linked_percentage": 38.4,
    "unanswered_count": 96,
    "unanswered_percentage": 18.8,
    "evaluated_count": 512,
    "evaluated_questions": 512,
    "previous_period": {
      "total_questions": 470,
      "total_groups": 24,
      "product_linked_percentage": 35.1,
      "unanswered_percentage": 21.3
    }
  }
}

previous_period is null when the previous-window scan failed or timed out; treat that as unknown, not as a real zero.

Common errors

StatusCodeWhenFix
400validation_failedBad/missing dates, period=custom without start_date/end_date, or an unknown kpis / entry_points value. details lists what's valid.Fix the parameter and resend.
401missing_credentials, invalid_key, revokedAuth header missing, malformed, or the key is no longer active.See Authentication.
403insufficient_scopeKey lacks analytics:read.Create a key with the analytics:read scope.
429rate_limitedToo many requests for this key.Back off using the Retry-After header. See Rate limits.

Next

  • Authentication: generate an analytics:read key.
  • Conversations: pull the full transcript behind a conversation_ids entry.
  • Settings: read and update your store configuration over the API.
  • MCP server: let an AI agent query KPIs and top questions directly.

Released under the proprietary Humind license.