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

# Check evidence coverage

> Classifies whether evidence in a product or area scope is informative, too thin,
or predates team coverage. The current density threshold is 50 distinct insights.
`policy_version` makes future policy changes explicit.

`scope.feedback_since` and `scope.all_time_insight_count` ignore the requested date
window but keep the product and area filters. Windowed counts apply every filter.




## OpenAPI

````yaml /api-reference/openapi.yaml get /coverage
openapi: 3.0.3
info:
  title: ClosedLoop AI Public API
  version: 1.2.0
  x-logo:
    url: https://app.closedloop.sh/favicon.svg
    altText: ClosedLoop AI
  description: >
    # ClosedLoop AI Public API


    Programmatic access to your team's **product insights**, the structured
    intelligence

    ClosedLoop AI extracts from customer conversations (Gong, Fireflies, Slack,
    …) and

    structured feedback (surveys, webhooks).


    Pull insights, themes, features, customer context and analytics into your
    own tools.


    ## Two ways to integrate


    | | **This REST API** | **MCP** (live today) |

    |---|---|---|

    | Best for | server-to-server, dashboards, ETL, scripts | AI assistants
    (Claude, Cursor, …), agents |

    | Shape | resource endpoints, JSON | tool calls over the Model Context
    Protocol |

    | Auth | `X-API-Key` header | OAuth |


    Most agentic use cases are already covered by MCP. Reach for this REST API
    when you

    want plain HTTP resources.


    ## Base URLs (region-scoped)


    Your API key belongs to a **team**, and each team lives in exactly one
    region. Use the

    base URL for **your** region. A key issued in the EU only works against the
    EU host.


    | Region | Base URL |

    |---|---|

    | United States | `https://api.closedloop.sh/v1` |

    | European Union | `https://eu.api.closedloop.sh/v1` |


    ## Authentication


    Create a key in **Settings → API Keys** in the app, then send it on every
    request:


    ```

    X-API-Key: clai_live_xxxxxxxxxxxxxxxxxxxx

    ```


    Keys are **team-scoped**: every response is automatically limited to your
    team's data.

    Never expose a key in client-side code.


    ## Conventions


    - **Read-only**: every v1 endpoint is a `GET`; nothing is mutated. Feedback
    is ingested
      through your connected integrations, not this API.
    - **Pagination**: list endpoints accept `limit` (default 50, max 200) and
    `offset`.
      `/insights` also supports fixed-watermark cursor export with `updated_since`.
    - **Errors**: non-2xx responses return a JSON `{ error, code, hint? }` body.
    User-safe
      messages only; never raw internals.
    - **Rate limits**: the public API currently has no fixed published per-key
    quota.

    - **Linking insights and context**: both carry the same resolved
    `customer_id`, so you
      can line up an insight with the context (churn, competitor, satisfaction) for the same
      customer. Filter either list by `customer_id`.
    - **Time anchors**: timestamps are ISO 8601 (UTC).
  contact:
    name: ClosedLoop AI Support
    email: support@closedloop.sh
    url: https://closedloop.sh
  license:
    name: Proprietary (ClosedLoop Labs LLC)
    url: https://closedloop.sh/terms
servers:
  - url: https://api.closedloop.sh/v1
    description: United States (production)
  - url: https://eu.api.closedloop.sh/v1
    description: European Union (production)
security:
  - ApiKeyAuth: []
tags:
  - name: Insights
    description: |
      Individual **product insights**: structured, AI-processed feedback items
      (pain point, severity, workaround, competitor gap, evidence).
  - name: Products
    description: >-
      The current product, product-area, and feature-area vocabulary for the
      team.
  - name: Themes
    description: >
      **Themes**: top-level problem clusters of related insights, RIC-scored.
      The

      "what should we build?" surface. Each theme rolls up one or more buildable
      features.
  - name: Features
    description: >
      **Features**: the buildable, shippable children of a theme. Filter by
      `theme_id`.

      Same theme → features hierarchy you see on the roadmap.
  - name: Customers
    description: The people and accounts who gave feedback, with CRM context.
  - name: Context
    description: >
      **Customer context**, the strategic intelligence behind your customers:
      churn

      reasons, competitive mentions, satisfaction, pricing perception and more,
      surfaced

      from conversations. This is the `/context` surface in the app. Each record
      resolves

      to a customer (`customer_id`), so it lines up with insights for the same
      customer.
  - name: Competitors
    description: >
      Competitive pressure derived from your customers' own words. Competitors
      with a

      **mention trend over time** (not a static total), and a searchable feed of
      the exact

      mentions, each showing the customer behind it.
  - name: Analytics
    description: Trends, cohorts and facet counts over the full dataset.
  - name: Integrations
    description: Connected data sources for your team.
  - name: Usage
    description: Credit consumption and usage records for billing transparency.
  - name: Meta
    description: Service metadata.
paths:
  /coverage:
    get:
      tags:
        - Analytics
      summary: Check evidence coverage
      description: >
        Classifies whether evidence in a product or area scope is informative,
        too thin,

        or predates team coverage. The current density threshold is 50 distinct
        insights.

        `policy_version` makes future policy changes explicit.


        `scope.feedback_since` and `scope.all_time_insight_count` ignore the
        requested date

        window but keep the product and area filters. Windowed counts apply
        every filter.
      operationId: getCoverage
      parameters:
        - $ref: '#/components/parameters/ProductId'
        - $ref: '#/components/parameters/ProductFeatureId'
        - $ref: '#/components/parameters/ProductAreaId'
        - $ref: '#/components/parameters/FeatureAreaId'
        - $ref: '#/components/parameters/DateFrom'
        - $ref: '#/components/parameters/DateTo'
      responses:
        '200':
          description: Coverage verdict and supporting totals
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Coverage'
              example:
                verdict: informative
                reason: density_threshold_met
                reads_as: >-
                  84 insights across 6 themes cover the requested scope, so a
                  weak match is worth noticing — but it still is not proof that
                  nobody wants it.
                policy_version: '1'
                density_threshold: 50
                filters:
                  product_id: null
                  product_feature_id: null
                  product_area_id: 223e4567-e89b-42d3-a456-426614174000
                  feature_area_id: null
                date_range:
                  from: null
                  to: null
                scope:
                  insight_count: 84
                  all_time_insight_count: 84
                  theme_count: 6
                  customer_count: 31
                  deal_blocker_count: 4
                  feedback_since: '2026-01-12T09:00:00Z'
                team:
                  insight_count: 508
                  feedback_since: '2025-11-03T12:00:00Z'
        '400':
          $ref: '#/components/responses/ValidationError'
        '401':
          $ref: '#/components/responses/Unauthorized'
components:
  parameters:
    ProductId:
      name: product_id
      in: query
      schema:
        type: string
        format: uuid
      description: Filter by one product ID from `/products`.
    ProductFeatureId:
      name: product_feature_id
      in: query
      schema:
        type: string
        format: uuid
      description: Filter by one buildable product feature ID from `/features`.
    ProductAreaId:
      name: product_area_id
      in: query
      schema:
        type: string
        format: uuid
      description: Filter by one parent product-area ID from `/areas`.
    FeatureAreaId:
      name: feature_area_id
      in: query
      schema:
        type: string
        format: uuid
      description: Filter by one child feature-area ID from `/areas`.
    DateFrom:
      name: date_from
      in: query
      schema:
        type: string
        format: date
      description: Inclusive start date (ISO 8601).
    DateTo:
      name: date_to
      in: query
      schema:
        type: string
        format: date
      description: Inclusive end date (ISO 8601).
  schemas:
    Coverage:
      type: object
      required:
        - verdict
        - reason
        - reads_as
        - policy_version
        - density_threshold
        - filters
        - date_range
        - scope
        - team
      properties:
        verdict:
          type: string
          enum:
            - informative
            - too_thin
            - pre_coverage
          description: >-
            Whether absence or weak evidence in the requested scope can be
            interpreted meaningfully.
        reason:
          type: string
          enum:
            - no_team_feedback
            - window_before_team_coverage
            - empty_scope_window
            - below_density_threshold
            - density_threshold_met
        reads_as:
          type: string
          description: Human-readable interpretation of the verdict.
        policy_version:
          type: string
          example: '1'
        density_threshold:
          type: integer
          example: 50
        filters:
          type: object
          required:
            - product_id
            - product_feature_id
            - product_area_id
            - feature_area_id
          properties:
            product_id:
              type: string
              format: uuid
              nullable: true
            product_feature_id:
              type: string
              format: uuid
              nullable: true
            product_area_id:
              type: string
              format: uuid
              nullable: true
            feature_area_id:
              type: string
              format: uuid
              nullable: true
        date_range:
          type: object
          required:
            - from
            - to
          properties:
            from:
              type: string
              nullable: true
            to:
              type: string
              nullable: true
        scope:
          type: object
          required:
            - insight_count
            - all_time_insight_count
            - theme_count
            - customer_count
            - deal_blocker_count
            - feedback_since
          properties:
            insight_count:
              type: integer
              description: Distinct insights in the filtered date window.
            all_time_insight_count:
              type: integer
              description: Distinct insights for the product/area scope across all time.
            theme_count:
              type: integer
              description: >-
                Distinct active top-level themes reached by the filtered
                insights.
            customer_count:
              type: integer
              description: Distinct customers represented by the filtered insights.
            deal_blocker_count:
              type: integer
              description: Filtered insights marked as deal blockers.
            feedback_since:
              type: string
              format: date-time
              nullable: true
              description: Earliest feedback for the product/area scope across all time.
        team:
          type: object
          required:
            - insight_count
            - feedback_since
          properties:
            insight_count:
              type: integer
              description: All insights for the authenticated team.
            feedback_since:
              type: string
              format: date-time
              nullable: true
              description: Earliest feedback for the authenticated team.
    Error:
      type: object
      required:
        - error
        - code
      properties:
        error:
          type: string
          description: User-safe message.
        code:
          type: string
          example: INVALID_API_KEY
        hint:
          type: string
          description: Optional next step.
  responses:
    ValidationError:
      description: Invalid query parameter, filter combination, or cursor
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            uuid:
              summary: Invalid filter ID
              value:
                error: product_id must be a UUID.
                code: VALIDATION_ERROR
            cursor:
              summary: Invalid cursor
              value:
                error: cursor is invalid or expired
                code: VALIDATION_ERROR
    Unauthorized:
      description: |
        Missing or invalid API key. `code` is `NO_API_KEY` when the `X-API-Key`
        header is absent, or `INVALID_API_KEY` when a key is present but invalid
        or inactive.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            missing:
              summary: No key provided
              value:
                error: API key required.
                code: NO_API_KEY
                hint: Send your key in the X-API-Key header.
            invalid:
              summary: Key invalid or inactive
              value:
                error: Invalid or inactive API key.
                code: INVALID_API_KEY
                hint: Create a key in Settings → API Keys.
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: |
        Team-scoped API key created in the app (Settings → API Keys).
        Sent as `X-API-Key: <key>` on every request. The header name `apikey`
        is also accepted as an alias.

````