Scrunch AI Signals API

The Signals API from Scrunch AI — 6 operation(s) exposing Scrunch's nightly detection sweep as a queryable feed of statistically-tested level changes and trends in AI visibility metrics (presence_rate, position_top_rate, cited_domain_rate), each with a confidence tier, a human-readable narrative, a stable fingerprint that survives nightly re-detection, and team reactions. Split from the live OpenAPI served at https://api.scrunchai.com/v1/openapi.json.

OpenAPI Specification

scrunch-ai-signals-api-openapi.yml Raw ↑
openapi: 3.1.0
info:
  title: Scrunch Data Signals API
  version: 0.1.0
  description: 'Signals surface of the Scrunch Data API: the nightly detection sweep of statistically-tested level changes
    and trends in AI visibility metrics, with stable signal fingerprints and team reactions. Split verbatim from the live
    OpenAPI served at https://api.scrunchai.com/v1/openapi.json.'
servers:
- url: https://api.scrunchai.com/v1
tags:
- name: Signals
paths:
  /{brand_id}/signals:
    get:
      tags:
      - Signals
      summary: List Signals
      description: 'List detected signals for a brand — statistically-tested movements (level changes and trends) in AI-visibility
        metrics, produced by the nightly detection sweep.


        ## Population


        By default only user-facing signals are returned: the confidence tiers `high`, `confident`, `worth_a_look`, and `provisional`.
        Pass an explicit `tier` to select a single tier, including the noise-floor tiers. An ongoing signal is re-detected
        daily under the same `fingerprint`; a multi-day date range returns only the latest detection per signal identity,
        so you never see day-by-day duplicates of the same issue.


        ## Sorting


        `sort=score_desc` (default) orders by engine priority, `delta_desc` by largest absolute change, `detected_desc` by
        newest detection date.'
      operationId: listSignals
      security:
      - HTTPBearer:
        - query
      parameters:
      - name: brand_id
        in: path
        required: true
        schema:
          type: integer
          title: Brand Id
      - name: scope
        in: query
        required: false
        schema:
          anyOf:
          - enum:
            - account
            - account_platform
            - topic
            - topic_platform
            type: string
          - type: 'null'
          description: 'Slice granularity the signal was detected on: `account` (whole brand), `account_platform` (brand on
            one AI platform), `topic`, or `topic_platform`.'
          title: Scope
        description: 'Slice granularity the signal was detected on: `account` (whole brand), `account_platform` (brand on
          one AI platform), `topic`, or `topic_platform`.'
      - name: platform
        in: query
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          description: AI platform the signal was detected on (e.g. `OpenAI`). Multi-platform signals carry the `(multi)`
            sentinel; the response `slice.platforms` lists the real platforms.
          title: Platform
        description: AI platform the signal was detected on (e.g. `OpenAI`). Multi-platform signals carry the `(multi)` sentinel;
          the response `slice.platforms` lists the real platforms.
      - name: metric
        in: query
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          description: 'Metric the signal fired on. Known values: `presence_rate`, `position_top_rate`, `cited_domain_rate`
            (new metrics may be added).'
          title: Metric
        description: 'Metric the signal fired on. Known values: `presence_rate`, `position_top_rate`, `cited_domain_rate`
          (new metrics may be added).'
      - name: alert_type
        in: query
        required: false
        schema:
          anyOf:
          - enum:
            - level_change
            - trend
            type: string
          - type: 'null'
          description: 'Detection kind: `level_change` (step shift) or `trend` (sustained drift).'
          title: Alert Type
        description: 'Detection kind: `level_change` (step shift) or `trend` (sustained drift).'
      - name: direction
        in: query
        required: false
        schema:
          anyOf:
          - enum:
            - up
            - down
            - none
            type: string
          - type: 'null'
          description: 'Direction of the movement: `up`, `down`, or `none`.'
          title: Direction
        description: 'Direction of the movement: `up`, `down`, or `none`.'
      - name: tier
        in: query
        required: false
        schema:
          anyOf:
          - enum:
            - high
            - confident
            - worth_a_look
            - provisional
            - low_confidence
            - underpowered
            - untested
            type: string
          - type: 'null'
          description: Confidence tier. When omitted, only the default user-facing tiers are returned (`high`, `confident`,
            `worth_a_look`, `provisional`); pass an explicit tier to see a single tier, including the noise-floor tiers (`low_confidence`,
            `underpowered`, `untested`).
          title: Tier
        description: Confidence tier. When omitted, only the default user-facing tiers are returned (`high`, `confident`,
          `worth_a_look`, `provisional`); pass an explicit tier to see a single tier, including the noise-floor tiers (`low_confidence`,
          `underpowered`, `untested`).
      - name: subject_kind
        in: query
        required: false
        schema:
          anyOf:
          - enum:
            - brand
            - competitor
            type: string
          - type: 'null'
          description: 'Whose movement the signal describes: your `brand` or a `competitor`.'
          title: Subject Kind
        description: 'Whose movement the signal describes: your `brand` or a `competitor`.'
      - name: anchor_from
        in: query
        required: false
        schema:
          anyOf:
          - type: string
            format: date
          - type: 'null'
          description: Earliest `detected_for_date` to include (inclusive, YYYY-MM-DD).
          title: Anchor From
        description: Earliest `detected_for_date` to include (inclusive, YYYY-MM-DD).
      - name: anchor_to
        in: query
        required: false
        schema:
          anyOf:
          - type: string
            format: date
          - type: 'null'
          description: Latest `detected_for_date` to include (inclusive, YYYY-MM-DD).
          title: Anchor To
        description: Latest `detected_for_date` to include (inclusive, YYYY-MM-DD).
      - name: mover_url
        in: query
        required: false
        schema:
          anyOf:
          - type: string
            minLength: 1
            maxLength: 2048
          - type: 'null'
          description: Case-insensitive substring matched against the URLs in the signal's `url_movers` evidence (per-URL
            citation movers, emitted for `cited_domain_rate` signals). Only signals with at least one matching mover URL are
            returned; signals without URL movers never match.
          title: Mover Url
        description: Case-insensitive substring matched against the URLs in the signal's `url_movers` evidence (per-URL citation
          movers, emitted for `cited_domain_rate` signals). Only signals with at least one matching mover URL are returned;
          signals without URL movers never match.
      - name: sort
        in: query
        required: false
        schema:
          enum:
          - score_desc
          - delta_desc
          - detected_desc
          type: string
          description: 'Sort order: `score_desc` (engine priority, default), `delta_desc` (largest absolute change first),
            or `detected_desc` (newest first).'
          default: score_desc
          title: Sort
        description: 'Sort order: `score_desc` (engine priority, default), `delta_desc` (largest absolute change first), or
          `detected_desc` (newest first).'
      - name: limit
        in: query
        required: false
        schema:
          type: integer
          maximum: 200
          minimum: 1
          description: Maximum number of signals to return.
          default: 50
          title: Limit
        description: Maximum number of signals to return.
      - name: offset
        in: query
        required: false
        schema:
          type: integer
          minimum: 0
          description: Number of signals to skip (pagination).
          default: 0
          title: Offset
        description: Number of signals to skip (pagination).
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CollectionResponse_APISignal_'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /{brand_id}/signals/anchors:
    get:
      tags:
      - Signals
      summary: List Signal Anchor Dates
      description: List recent distinct detection dates (`detected_for_date`) that produced user-facing signals, newest first.
        Useful for building date pickers or polling for new detection runs — the population matches `listSignals` with default
        filters, so every returned day has at least one signal.
      operationId: listSignalAnchors
      security:
      - HTTPBearer:
        - query
      parameters:
      - name: brand_id
        in: path
        required: true
        schema:
          type: integer
          title: Brand Id
      - name: limit
        in: query
        required: false
        schema:
          type: integer
          maximum: 365
          minimum: 1
          description: Maximum number of dates to return.
          default: 90
          title: Limit
        description: Maximum number of dates to return.
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/APISignalAnchors'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /{brand_id}/signals/reactions:
    get:
      tags:
      - Signals
      summary: List Signal Reactions
      description: List reactions users have set on this brand's signals, newest first. This is **brand-wide** — it returns
        every user's reactions, not just the caller's — so an integration can export the team's aggregate feedback. Filter
        by `fingerprint` to get reactions for one signal identity, or by `reaction` to get e.g. everything marked `useful`.
      operationId: listSignalReactions
      security:
      - HTTPBearer:
        - query
      parameters:
      - name: brand_id
        in: path
        required: true
        schema:
          type: integer
          title: Brand Id
      - name: fingerprint
        in: query
        required: false
        schema:
          anyOf:
          - type: string
          - type: 'null'
          description: Only reactions on this signal fingerprint.
          title: Fingerprint
        description: Only reactions on this signal fingerprint.
      - name: reaction
        in: query
        required: false
        schema:
          anyOf:
          - enum:
            - useful
            - not_useful
            - dismissed
            - actioned
            type: string
          - type: 'null'
          description: Only reactions with this value.
          title: Reaction
        description: Only reactions with this value.
      - name: limit
        in: query
        required: false
        schema:
          type: integer
          maximum: 200
          minimum: 1
          description: Maximum number of reactions to return.
          default: 50
          title: Limit
        description: Maximum number of reactions to return.
      - name: offset
        in: query
        required: false
        schema:
          type: integer
          minimum: 0
          description: Number of reactions to skip (pagination).
          default: 0
          title: Offset
        description: Number of reactions to skip (pagination).
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CollectionResponse_APISignalReaction_'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /{brand_id}/signals/{signal_id}:
    get:
      tags:
      - Signals
      summary: Get Signal
      description: Fetch one signal by ID, including its full narrative (what happened, why it matters, what to do) when available.
        Returns 404 for IDs that exist but are not user-facing (non-fired or cluster-child rows) — the same population rule
        as `listSignals`.
      operationId: getSignal
      security:
      - HTTPBearer:
        - query
      parameters:
      - name: brand_id
        in: path
        required: true
        schema:
          type: integer
          title: Brand Id
      - name: signal_id
        in: path
        required: true
        schema:
          type: integer
          title: Signal Id
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/APISignal'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /{brand_id}/signals/{signal_id}/reaction:
    put:
      tags:
      - Signals
      summary: Set Signal Reaction
      description: 'Set (or replace) the calling user''s reaction on a signal. Reactions are keyed on the signal''s stable
        `fingerprint`, so a reaction survives the nightly re-detection of the same underlying issue.


        Requires a **user token** (JWT) — API keys have no user identity to attribute the reaction to and receive 403.'
      operationId: setSignalReaction
      security:
      - HTTPBearer:
        - query
      parameters:
      - name: brand_id
        in: path
        required: true
        schema:
          type: integer
          title: Brand Id
      - name: signal_id
        in: path
        required: true
        schema:
          type: integer
          title: Signal Id
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/APISignalReactionUpsert'
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/APISignalReaction'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
    delete:
      tags:
      - Signals
      summary: Clear Signal Reaction
      description: Remove the calling user's reaction on a signal's stable identity. Requires a **user token** (JWT); API
        keys receive 403.
      operationId: clearSignalReaction
      security:
      - HTTPBearer:
        - query
      parameters:
      - name: brand_id
        in: path
        required: true
        schema:
          type: integer
          title: Brand Id
      - name: signal_id
        in: path
        required: true
        schema:
          type: integer
          title: Signal Id
      responses:
        '204':
          description: Successful Response
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
components:
  schemas:
    APISignal:
      properties:
        id:
          type: integer
          title: Id
          description: Signal ID. Stable within one detection; use for `getSignal`.
        detected_for_date:
          type: string
          format: date
          title: Detected For Date
          description: Anchor date the nightly detection ran for (YYYY-MM-DD).
        fingerprint:
          type: string
          title: Fingerprint
          description: Stable identity of the underlying issue across nightly re-detections (hash of the slice + metric).
            An ongoing signal is re-raised daily under the same fingerprint; reactions are keyed on it.
        subject_kind:
          type: string
          enum:
          - brand
          - competitor
          title: Subject Kind
          description: 'Whose movement the signal describes: your `brand` or a `competitor`.'
        alert_type:
          type: string
          enum:
          - level_change
          - trend
          title: Alert Type
          description: 'Detection kind: `level_change` (step shift) or `trend` (sustained drift).'
        scope:
          type: string
          enum:
          - account
          - account_platform
          - topic
          - topic_platform
          title: Scope
          description: 'Slice granularity: `account`, `account_platform`, `topic`, or `topic_platform`.'
        metric:
          type: string
          title: Metric
          description: Metric the signal fired on (e.g. `presence_rate`, `position_top_rate`, `cited_domain_rate`).
        platform:
          type: string
          title: Platform
          description: AI platform the signal was detected on. `(multi)` means the slice spans several platforms — see `slice.platforms`
            for the real list.
        direction:
          type: string
          enum:
          - up
          - down
          - none
          title: Direction
          description: 'Direction of the movement: `up`, `down`, `none`.'
        tier:
          type: string
          enum:
          - high
          - confident
          - worth_a_look
          - provisional
          - low_confidence
          - underpowered
          - untested
          title: Tier
          description: 'Confidence tier, best first: `high`, `confident`, `worth_a_look`, `provisional`, then noise-floor
            tiers `low_confidence`, `underpowered`, `untested` (hidden by default).'
        current_value:
          type: number
          title: Current Value
          description: Metric value over the current window (0-1 rate).
        baseline_value:
          type: number
          title: Baseline Value
          description: Metric value over the baseline window (0-1 rate).
        delta_absolute:
          type: number
          title: Delta Absolute
          description: '`current_value - baseline_value` (positive = up).'
        score:
          anyOf:
          - type: number
          - type: 'null'
          title: Score
          description: Engine priority score used for the default sort; null for legacy rows.
        narrative_what:
          type: string
          title: Narrative What
          description: One-line headline of what changed.
        narrative:
          anyOf:
          - $ref: '#/components/schemas/APISignalNarrative'
          - type: 'null'
          description: Full narrative (what happened / why it matters / what to do), when available.
        slice:
          $ref: '#/components/schemas/APISignalSlice'
          description: The data slice the signal was detected on.
        window_current_start:
          anyOf:
          - type: string
            format: date
          - type: 'null'
          title: Window Current Start
          description: Start of the current comparison window.
        window_current_end:
          anyOf:
          - type: string
            format: date
          - type: 'null'
          title: Window Current End
          description: End of the current comparison window.
        baseline_definition:
          anyOf:
          - type: string
          - type: 'null'
          title: Baseline Definition
          description: Human-readable definition of the baseline window.
        url_movers:
          items:
            $ref: '#/components/schemas/APISignalUrlMover'
          type: array
          title: Url Movers
          description: 'Per-URL citation movers behind the signal: the brand/competitor URLs whose distinct-response citation
            counts moved most between the baseline and current windows. Emitted for `cited_domain_rate` signals; empty for
            other metrics and older signals. Filterable via `mover_url`.'
        created_at:
          type: string
          format: date-time
          title: Created At
          description: When the signal row was created (UTC).
      type: object
      required:
      - id
      - detected_for_date
      - fingerprint
      - subject_kind
      - alert_type
      - scope
      - metric
      - platform
      - direction
      - tier
      - current_value
      - baseline_value
      - delta_absolute
      - narrative_what
      - slice
      - created_at
      title: APISignal
      description: A detected, statistically-tested movement in an AI-visibility metric.
    APISignalAnchors:
      properties:
        dates:
          items:
            type: string
            format: date
          type: array
          title: Dates
          description: Distinct `detected_for_date` days with user-facing signals, newest first. Same population as `listSignals`
            with default filters.
      type: object
      required:
      - dates
      title: APISignalAnchors
      description: Recent distinct detection dates that produced signals.
    APISignalNarrative:
      properties:
        what_happened:
          anyOf:
          - type: string
          - type: 'null'
          title: What Happened
          description: Plain-language description of the detected change.
        why_it_matters:
          anyOf:
          - type: string
          - type: 'null'
          title: Why It Matters
          description: Why the change is significant for the brand.
        what_to_do:
          anyOf:
          - type: string
          - type: 'null'
          title: What To Do
          description: Suggested next step or investigation.
      type: object
      title: APISignalNarrative
      description: Engine-generated narrative for a signal.
    APISignalReaction:
      properties:
        user_id:
          type: integer
          title: User Id
          description: ID of the user the reaction belongs to.
        insight_id:
          type: integer
          title: Insight Id
          description: Signal ID the reaction was last set on.
        fingerprint:
          type: string
          title: Fingerprint
          description: Stable signal identity the reaction follows across re-detections.
        reaction:
          type: string
          enum:
          - useful
          - not_useful
          - dismissed
          - actioned
          title: Reaction
          description: The reaction value.
        reason:
          anyOf:
          - type: string
          - type: 'null'
          title: Reason
          description: Free-text reason, when provided.
        surface:
          type: string
          enum:
          - in_app
          - email
          - slack
          - api
          title: Surface
          description: 'Where the reaction was set: `in_app`, `email`, `slack`, or `api`.'
        created_at:
          type: string
          format: date-time
          title: Created At
          description: When the reaction was first set (UTC).
        updated_at:
          type: string
          format: date-time
          title: Updated At
          description: When the reaction was last changed (UTC).
      type: object
      required:
      - user_id
      - insight_id
      - fingerprint
      - reaction
      - surface
      - created_at
      - updated_at
      title: APISignalReaction
      description: A user's reaction to a signal's stable identity.
    APISignalReactionUpsert:
      properties:
        reaction:
          type: string
          enum:
          - useful
          - not_useful
          - dismissed
          - actioned
          title: Reaction
          description: 'Reaction to set: `useful`, `not_useful`, `dismissed`, or `actioned`.'
        reason:
          anyOf:
          - type: string
            maxLength: 2000
          - type: 'null'
          title: Reason
          description: Optional free-text reason accompanying the reaction.
      type: object
      required:
      - reaction
      title: APISignalReactionUpsert
      description: Request body for `setSignalReaction`.
    APISignalSlice:
      properties:
        platforms:
          items:
            type: string
          type: array
          title: Platforms
          description: AI platforms the slice covers. Empty means all platforms.
        topic_labels:
          items:
            type: string
          type: array
          title: Topic Labels
          description: Topic labels the slice covers. Empty means all topics.
        geo_country:
          anyOf:
          - type: string
          - type: 'null'
          title: Geo Country
          description: Country the slice is restricted to, when geo-scoped.
      type: object
      title: APISignalSlice
      description: The data slice a signal was detected on.
    APISignalUrlMover:
      properties:
        normalized_url:
          type: string
          title: Normalized Url
          description: Normalized cited URL (scheme and tracking params stripped).
        owner:
          type: string
          enum:
          - brand
          - competitor
          title: Owner
          description: 'Whose domain the URL belongs to: `brand` or `competitor`.'
        competitor_id:
          anyOf:
          - type: integer
          - type: 'null'
          title: Competitor Id
          description: Competitor ID for `competitor`-owned URLs.
        competitor_name:
          anyOf:
          - type: string
          - type: 'null'
          title: Competitor Name
          description: Competitor name for `competitor`-owned URLs.
        current_responses:
          type: integer
          title: Current Responses
          description: Distinct responses citing the URL in the current window.
        baseline_responses:
          type: integer
          title: Baseline Responses
          description: Distinct responses citing the URL in the baseline window.
        current_prompts:
          type: integer
          title: Current Prompts
          description: Distinct prompts whose responses cite the URL in the current window.
        baseline_prompts:
          type: integer
          title: Baseline Prompts
          description: Distinct prompts whose responses cite the URL in the baseline window.
        contribution:
          anyOf:
          - type: number
          - type: 'null'
          title: Contribution
          description: Exact share of the signal's metric delta attributed to this URL. Brand-owned URLs only; null on competitor
            entries and older signals.
      type: object
      required:
      - normalized_url
      - owner
      - current_responses
      - baseline_responses
      - current_prompts
      - baseline_prompts
      title: APISignalUrlMover
      description: A per-URL citation mover backing a `cited_domain_rate` signal.
    AggregationGranularity:
      type: string
      enum:
      - daily
      - weekly
      - monthly
    CollectionResponse_APISignalReaction_:
      properties:
        total:
          type: integer
          title: Total
        offset:
          type: integer
          title: Offset
          default: 0
        limit:
          anyOf:
          - type: integer
          - type: 'null'
          title: Limit
        items:
          items:
            $ref: '#/components/schemas/APISignalReaction'
          type: array
          title: Items
        metadata:
          anyOf:
          - $ref: '#/components/schemas/TimeSeriesMetadata'
          - type: 'null'
      type: object
      required:
      - total
      - items
      title: CollectionResponse[APISignalReaction]
    CollectionResponse_APISignal_:
      properties:
        total:
          type: integer
          title: Total
        offset:
          type: integer
          title: Offset
          default: 0
        limit:
          anyOf:
          - type: integer
          - type: 'null'
          title: Limit
        items:
          items:
            $ref: '#/components/schemas/APISignal'
          type: array
          title: Items
        metadata:
          anyOf:
          - $ref: '#/components/schemas/TimeSeriesMetadata'
          - type: 'null'
      type: object
      required:
      - total
      - items
      title: CollectionResponse[APISignal]
    HTTPValidationError:
      properties:
        detail:
          items:
            $ref: '#/components/schemas/ValidationError'
          type: array
          title: Detail
      type: object
      title: HTTPValidationError
    OwnerTimeSeriesPoint:
      properties:
        time_bucket:
          type: string
          title: Time Bucket
          description: Date string for the time bucket (YYYY-MM-DD)
        brand:
          type: integer
          title: Brand
          description: Observation count for brand-owned domains
        competitor:
          type: integer
          title: Competitor
          description: Observation count for competitor-owned domains
        other:
          type: integer
          title: Other
          description: Observation count for third-party domains
      type: object
      required:
      - time_bucket
      - brand
      - competitor
      - other
      title: OwnerTimeSeriesPoint
      description: A single time bucket with observation counts by owner type.
    SegmentTotal:
      properties:
        name:
          type: string
          title: Name
        observation_count:
          type: integer
          title: Observation Count
      type: object
      required:
      - name
      - observation_count
      title: SegmentTotal
      description: Observation count for a citation segment, computed server-side.
    TimeSeriesMetadata:
      properties:
        aggregation_granularity:
          $ref: '#/components/schemas/AggregationGranularity'
          description: 'The aggregation granularity: daily, weekly, or monthly'
        period_count:
          type: integer
          title: Period Count
          description: The number of periods in the time series
        start_date:
          type: string
          format: date-time
          title: Start Date
          description: The start date of the time series
        end_date:
          type: string
          format: date-time
          title: End Date
          description: The end date of the time series
        top_domains:
          anyOf:
          - $ref: '#/components/schemas/TopDomainsMetadata'
          - type: 'null'
          description: Top domains metadata (only populated by sources/domains endpoints)
      type: object
      required:
      - aggregation_granularity
      - period_count
      - start_date
      - end_date
      title: TimeSeriesMetadata
    TopDomainSummary:
      properties:
        domain:
          type: string
          title: Domain
        domain_owner:
          type: string
          title: Domain Owner
        observation_count:
          type: integer
          title: Observation Count
      type: object
      required:
      - domain
      - domain_owner
      - observation_count
      title: TopDomainSummary
    TopDomainsMetadata:
      properties:
        domains:
          items:
            $ref: '#/components/schemas/TopDomainSummary'
          type: array
          title: Domains
          description: Top domains by observation count (always domain-level aggregated)
        grand_total:
          type: integer
          title: Grand Total
          description: Total observation count across all domains (for calculating 'Other')
        owner_totals:
          additionalProperties:
            type: integer
          type: object
          title: Owner Totals
          description: Observation counts by owner type (brand, competitor, other)
        owner_time_series:
          anyOf:
          - items:
              $ref: '#/components/schemas/OwnerTimeSeriesPoint'
            type: array
          - type: 'null'
          title: Owner Time Series
          descripti

# --- truncated at 32 KB (33 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/scrunch-ai/refs/heads/main/openapi/scrunch-ai-signals-api-openapi.yml