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

# Visibility Change Decomposition

> Explains why the brand's visibility score changed between the selected period and the previous period of equal length. The comparison window is derived automatically and echoed back as `comparison_start_date` / `comparison_end_date`.

The change is decomposed across three dimensions - services, countries and topics - each of which partitions the analysed response set. Within a dimension, every segment carries a `contribution`: the visibility-point effect (on the 0-1 scale) of that segment's own mention-rate movement, weighted by the segment's share of responses. The dimension's `mix_contribution` captures change caused by a shift in where responses were collected (added platforms, new prompts, different response volumes) rather than by mention-rate movement. For every dimension, the segment contributions plus `mix_contribution` sum exactly to `after_visibility - before_visibility`.

`within_noise: true` marks segments whose sample is too small (fewer than 30 responses in either period) or whose movement sits inside the 95% confidence interval for the rate difference; treat those movements as inconclusive.

Segment `id`s use the same public identifiers as the filter parameters (service providers, country codes, topic UUIDs or `no_topic`), so drilling down is iterative: pass a segment's `id` back as the matching filter (`services`, `countries`, `topics`) and call the endpoint again to decompose that slice across the remaining dimensions. Use `GET /api/v1/projects/{project}/mentions` with the same filters to retrieve concrete example responses.

When every partitioning dimension has been pinned to a single segment, pass `include_prompts=true` to reach the final level: the individual prompts that moved the slice.



## OpenAPI

````yaml /openapi.json get /api/v1/projects/{project}/visibility-decomposition
openapi: 3.1.0
info:
  title: Surfacd Public API
  version: 1.0.0
  description: >-
    Public API for Surfacd. Team-scoped operations live under
    `/api/v1/team/...`; project collections live under `/api/v1/projects`;
    project-scoped operations live under `/api/v1/projects/{project}/...`. All
    endpoints require a Bearer team API key created in Team Settings. The API
    key selects the team for team-level and collection endpoints.
servers:
  - url: https://app.surfacd.com
    description: Production
security:
  - bearerAuth: []
tags:
  - name: Projects
    description: Project management endpoints. Reads only in this version.
  - name: Prompts
    description: Prompt management endpoints. Reads only in this version.
  - name: Team
    description: Team settings and plan details for the current team.
  - name: Reporting
    description: >-
      Read-only reporting endpoints for projects (brand rankings, mentions,
      visibility, matrix analysis, source data).
  - name: Reference
    description: Reference data used by create, update, and reporting filters.
  - name: Tags
    description: Project tag management endpoints.
  - name: Reputation
    description: >-
      Read-only reputation analytics for a project: sentiment overview and
      trends, themes, narratives, the AI responses behind them, and the sources
      those responses cite. Requires the Reputation capability on the project's
      team; without it these endpoints return `403`.
paths:
  /api/v1/projects/{project}/visibility-decomposition:
    parameters:
      - $ref: '#/components/parameters/ProjectUuid'
    get:
      tags:
        - Reporting
      summary: Visibility Change Decomposition
      description: >-
        Explains why the brand's visibility score changed between the selected
        period and the previous period of equal length. The comparison window is
        derived automatically and echoed back as `comparison_start_date` /
        `comparison_end_date`.


        The change is decomposed across three dimensions - services, countries
        and topics - each of which partitions the analysed response set. Within
        a dimension, every segment carries a `contribution`: the
        visibility-point effect (on the 0-1 scale) of that segment's own
        mention-rate movement, weighted by the segment's share of responses. The
        dimension's `mix_contribution` captures change caused by a shift in
        where responses were collected (added platforms, new prompts, different
        response volumes) rather than by mention-rate movement. For every
        dimension, the segment contributions plus `mix_contribution` sum exactly
        to `after_visibility - before_visibility`.


        `within_noise: true` marks segments whose sample is too small (fewer
        than 30 responses in either period) or whose movement sits inside the
        95% confidence interval for the rate difference; treat those movements
        as inconclusive.


        Segment `id`s use the same public identifiers as the filter parameters
        (service providers, country codes, topic UUIDs or `no_topic`), so
        drilling down is iterative: pass a segment's `id` back as the matching
        filter (`services`, `countries`, `topics`) and call the endpoint again
        to decompose that slice across the remaining dimensions. Use `GET
        /api/v1/projects/{project}/mentions` with the same filters to retrieve
        concrete example responses.


        When every partitioning dimension has been pinned to a single segment,
        pass `include_prompts=true` to reach the final level: the individual
        prompts that moved the slice.
      operationId: v1ProjectsVisibilityDecomposition
      parameters:
        - name: start_date
          in: query
          required: true
          schema:
            type: string
            format: date
          description: >-
            Start of the current period. The previous period of equal length,
            ending the day before `start_date`, is compared automatically.
        - name: end_date
          in: query
          required: true
          schema:
            type: string
            format: date
          description: End of the current period. Must be on or after `start_date`.
        - name: brand
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: >-
            Brand UUID to analyse. Must belong to the project. Defaults to the
            project's primary brand. Discover brand UUIDs with `GET
            /api/v1/projects/{project}/brands`.
        - name: services
          in: query
          required: false
          schema:
            type: array
            items:
              type: string
          description: >-
            AI platform provider filters such as `chatgpt` or `perplexity`. Pin
            a single provider (using a segment `id` from a previous response) to
            drill into that platform. Discover providers with `GET
            /api/v1/platforms`.
          style: form
          explode: true
        - name: countries
          in: query
          required: false
          schema:
            type: array
            items:
              type: string
          description: >-
            Country code filters such as `US` or `GB`. Pin a single code (using
            a segment `id` from a previous response) to drill into that country.
            Discover codes with `GET /api/v1/countries`.
          style: form
          explode: true
        - name: topics
          in: query
          required: false
          schema:
            type: array
            items:
              type: string
          description: >-
            Topic UUID or name filters. Use `no_topic` for prompts without a
            topic. Pin a single topic (using a segment `id` from a previous
            response) to drill into it. Discover topics with `GET
            /api/v1/projects/{project}/topics`.
          style: form
          explode: true
        - name: tags
          in: query
          required: false
          schema:
            type: array
            items:
              type: string
          description: >-
            Tag UUID or name filters. Tags restrict the analysed prompt set but
            are not a drill dimension. Discover tags with `GET
            /api/v1/projects/{project}/tags`.
          style: form
          explode: true
        - name: prompt
          in: query
          required: false
          schema:
            type: string
            format: uuid
          description: Prompt UUID filter.
        - name: include_prompts
          in: query
          required: false
          schema:
            type: boolean
          description: >-
            When true, appends a `prompt` dimension listing the individual
            prompts with the largest absolute contributions (capped at 10,
            ranked largest first). Use it at the end of a drill to see which
            questions drove the change. Prompt segment `id`s are prompt UUIDs
            and can be passed back as the `prompt` filter or to the mentions
            endpoint. Unlike the partitioning dimensions, this capped list is a
            ranked shortlist - its contributions do not necessarily sum to the
            overall change.
      responses:
        '200':
          description: Period-over-period visibility change decomposed per dimension.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/VisibilityDecomposition'
        '422':
          $ref: '#/components/responses/ValidationError'
components:
  parameters:
    ProjectUuid:
      name: project
      in: path
      required: true
      description: Project UUID.
      schema:
        type: string
        format: uuid
  schemas:
    VisibilityDecomposition:
      type: object
      properties:
        start_date:
          type: string
          format: date
        end_date:
          type: string
          format: date
        comparison_start_date:
          type: string
          format: date
          description: Start of the automatically derived previous period.
        comparison_end_date:
          type: string
          format: date
          description: End of the automatically derived previous period.
        before_visibility:
          type: number
          description: Overall visibility in the comparison period (0-1 scale).
        after_visibility:
          type: number
          description: Overall visibility in the current period (0-1 scale).
        before_responses:
          type: integer
        after_responses:
          type: integer
        dimensions:
          type: array
          items:
            $ref: '#/components/schemas/DecompositionDimension'
      required:
        - start_date
        - end_date
        - comparison_start_date
        - comparison_end_date
        - before_visibility
        - after_visibility
        - before_responses
        - after_responses
        - dimensions
    DecompositionDimension:
      type: object
      properties:
        dimension:
          type: string
          enum:
            - service
            - country
            - topic
            - prompt
        segments:
          type: array
          items:
            $ref: '#/components/schemas/DecompositionSegment'
          description: >-
            Ordered by absolute contribution, largest first. The `prompt`
            dimension is capped to the 10 largest movers.
        mix_contribution:
          type: number
          description: >-
            Visibility points caused by a shift in where responses were
            collected rather than by mention-rate movement. Segment
            contributions plus this value sum exactly to the overall change.
      required:
        - dimension
        - segments
        - mix_contribution
    ValidationError:
      type: object
      description: Returned when one or more request parameters fail validation.
      properties:
        message:
          type: string
        errors:
          type: object
          additionalProperties:
            type: array
            items:
              type: string
      required:
        - message
        - errors
    DecompositionSegment:
      type: object
      description: >-
        One segment of a decomposition dimension. `id` is a public identifier
        that can be passed straight back as the matching filter parameter to
        drill in.
      properties:
        id:
          type: string
          description: >-
            Public identifier: a service provider, a country code, a topic UUID
            (or `no_topic`), or a prompt UUID.
        name:
          type: string
        metadata:
          type:
            - object
            - 'null'
          additionalProperties: true
        before_visibility:
          type: number
          description: Segment visibility in the comparison period (0-1 scale).
        after_visibility:
          type: number
          description: Segment visibility in the current period (0-1 scale).
        before_responses:
          type: integer
        after_responses:
          type: integer
        before_mentions:
          type: integer
        after_mentions:
          type: integer
        contribution:
          type: number
          description: >-
            Visibility points (0-1 scale) this segment's mention-rate movement
            added to (positive) or removed from (negative) the overall change.
        within_noise:
          type: boolean
          description: >-
            True when the sample is too small or the movement sits inside the
            95% confidence interval; treat as inconclusive.
      required:
        - id
        - name
        - metadata
        - before_visibility
        - after_visibility
        - before_responses
        - after_responses
        - before_mentions
        - after_mentions
        - contribution
        - within_noise
  responses:
    ValidationError:
      description: Validation failed.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ValidationError'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: token
      description: >-
        Team API key created in Team Settings. Use it with URLs for the same
        team.

````