> ## Documentation Index
> Fetch the complete documentation index at: https://developers.closebot.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Topic treemap and per-topic daily series for the active agency.

> A dedicated endpoint rather than another metric on /analytics/query, for three reasons:
AnalyticsQueryPoint.Value is an int and average sentiment is a nullable double; the treemap
and its sparklines must come from one snapshot or a batch landing mid-request can make the
rectangles disagree with the trend lines; and Query's validator hard-requires 15-minute
alignment while topics are day-grained.



## OpenAPI

````yaml https://megastream25-api.closebot.com/swagger/v1/swagger.json post /analytics/topics
openapi: 3.0.4
info:
  title: CloseBot API
  version: v1
servers:
  - url: https://api.closebot.com
    description: CloseBot API Endpoint
security:
  - Key: []
tags:
  - name: Account
  - name: AccountNotification
  - name: Agency
  - name: Analytics
  - name: Billing
  - name: Bot
  - name: BotMetric
  - name: BotSourceVariable
  - name: BotTemplate
  - name: BotTesting
  - name: Dashboard
  - name: Hierarchy
  - name: Lead
  - name: Leaderboard
  - name: Library
  - name: LiveDemo
  - name: Notification
  - name: Persona
  - name: Report
  - name: SmartFAQ
  - name: Source
  - name: SourceChannel
  - name: Webhook
paths:
  /analytics/topics:
    post:
      tags:
        - Analytics
      summary: Topic treemap and per-topic daily series for the active agency.
      description: >-
        A dedicated endpoint rather than another metric on /analytics/query, for
        three reasons:

        AnalyticsQueryPoint.Value is an int and average sentiment is a nullable
        double; the treemap

        and its sparklines must come from one snapshot or a batch landing
        mid-request can make the

        rectangles disagree with the trend lines; and Query's validator
        hard-requires 15-minute

        alignment while topics are day-grained.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TopicsQueryRequest'
          text/json:
            schema:
              $ref: '#/components/schemas/TopicsQueryRequest'
          application/*+json:
            schema:
              $ref: '#/components/schemas/TopicsQueryRequest'
      responses:
        '200':
          description: OK
          content:
            text/plain:
              schema:
                $ref: '#/components/schemas/TopicsQueryResponse'
            application/json:
              schema:
                $ref: '#/components/schemas/TopicsQueryResponse'
            text/json:
              schema:
                $ref: '#/components/schemas/TopicsQueryResponse'
        '403':
          description: Forbidden
          content:
            text/plain:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
            text/json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
components:
  schemas:
    TopicsQueryRequest:
      required:
        - end
        - start
      type: object
      properties:
        start:
          type: string
          description: >-
            Inclusive UTC start. Unlike /analytics/query this is day-grained, so
            no 15-minute alignment.
          format: date-time
        end:
          type: string
          description: Inclusive UTC end.
          format: date-time
        limit:
          type: integer
          description: Largest topics first; the rest are dropped. Capped at 50.
          format: int32
          nullable: true
        filters:
          $ref: '#/components/schemas/AnalyticsQueryFilters'
      additionalProperties: false
    TopicsQueryResponse:
      required:
        - analyzedConversations
        - pendingConversations
        - sentimentCoverage
        - topics
      type: object
      properties:
        topics:
          type: array
          items:
            $ref: '#/components/schemas/TopicsTopic'
          nullable: true
        analyzedConversations:
          type: integer
          description: Distinct conversations with at least one topic in range.
          format: int32
        pendingConversations:
          type: integer
          description: >-
            Conversations on topic-enabled sources that have never been
            classified, agency-wide.
                        
            Deliberately <em>not</em> scoped to the requested range. A
            range-scoped backlog would

            need each unanalysed conversation's start date, which means
            MIN(Message.Time) per lead

            — the scan of the Messages table that this feature's whole design
            avoids. An agency-wide

            number is cheap, and the UI must label it as such rather than
            implying a percentage.
          format: int32
        sentimentCoverage:
          type: number
          description: >-
            Share of
            Closebot.Api.Controllers.AnalyticsController.TopicsQueryResponse.AnalyzedConversations
            carrying a sentiment reading, 0-1.

            Below 1 means some sources have Sentiment switched off.
          format: double
        oldestAnalyzedOn:
          type: string
          description: >-
            Earliest conversation start date with any topic, agency-wide. Lets
            the UI say

            "topics from 3 Mar" so the forward-only cutover reads as a cutover
            and not a data gap.
          format: date
          nullable: true
      additionalProperties: false
    ProblemDetails:
      type: object
      properties:
        type:
          type: string
          nullable: true
        title:
          type: string
          nullable: true
        status:
          type: integer
          format: int32
          nullable: true
        detail:
          type: string
          nullable: true
        instance:
          type: string
          nullable: true
      additionalProperties: {}
    AnalyticsQueryFilters:
      type: object
      properties:
        sourceIds:
          type: array
          items:
            type: string
          description: Optional AgencySource IDs to include.
          nullable: true
        dimensions:
          type: array
          items:
            $ref: '#/components/schemas/DimensionFilter'
          description: >-
            Optional narrowing by the metric's declared dimensions. Each entry
            ANDs with the rest.

            A dimension the metric does not declare is a 400 rather than a
            silent no-op — a chart

            must never quietly show unfiltered totals under a filter the user
            set.
          nullable: true
      additionalProperties: false
    TopicsTopic:
      required:
        - conversationCount
        - name
        - sentimentCount
        - series
        - slug
        - topicId
      type: object
      properties:
        topicId:
          type: string
          nullable: true
        name:
          type: string
          nullable: true
        slug:
          type: string
          nullable: true
        conversationCount:
          type: integer
          description: >-
            Conversations carrying this topic in range. Drives treemap rectangle
            area.
          format: int32
        avgSentiment:
          type: number
          description: >-
            Mean 0-100 sentiment over the conversations that have a reading.
            Drives the colour axis.

            Null when none do — the caller must render that as "no data", never
            as neutral.
          format: double
          nullable: true
        sentimentCount:
          type: integer
          description: >-
            How many conversations the average is built from. Exposed so the UI
            can visually

            discount a rectangle coloured by three readings against one coloured
            by three hundred.
          format: int32
        series:
          type: array
          items:
            $ref: '#/components/schemas/TopicsSeriesPoint'
          description: >-
            Daily buckets across the requested range. Sparse: days with no
            conversations are omitted.
          nullable: true
      additionalProperties: false
    DimensionFilter:
      required:
        - dimension
        - operator
      type: object
      properties:
        dimension:
          type: string
          description: >-
            One of
            Closebot.Api.Controllers.AnalyticsController.MetricDimensions. Must
            be declared by the metric.
          nullable: true
        operator:
          type: string
          description: >-
            One of
            Closebot.Api.Controllers.AnalyticsController.DimensionOperators.
          nullable: true
        values:
          type: array
          items:
            type: string
          description: Required for `is` and `isNot`; ignored otherwise.
          nullable: true
      additionalProperties: false
      description: One filter on one dimension.
    TopicsSeriesPoint:
      required:
        - count
        - date
      type: object
      properties:
        date:
          type: string
          description: >-
            The day conversations in this bucket <em>started</em>. See
            TopicsTopic.
          format: date
        count:
          type: integer
          format: int32
        avgSentiment:
          type: number
          description: >-
            Null when no conversation in this bucket carried a sentiment
            reading.
          format: double
          nullable: true
      additionalProperties: false
  securitySchemes:
    Key:
      type: apiKey
      description: CloseBot API Key Authorization
      name: X-CB-KEY
      in: header

````