openapi: 3.1.0
info:
  title: SignalForge API
  version: 1.2.0
  description: >-
    Versioned financial-intelligence infrastructure API for explainable signals,
    rankings,

    ticker theses, normalized evidence, provider/readiness metadata, and derived
    research context.


    SignalForge exposes derived intelligence for developers, fintech products,
    research

    platforms, and AI agents. It does not expose raw vendor feeds, provide
    brokerage

    functionality, manage portfolios, or make personalized recommendations.


    Output is informational only and is not financial advice.


    The canonical OpenAPI document is served in-app at /api/openapi.yaml. Public
    /api/v1/* responses include an x-request-id header for support correlation.
    Authenticated responses also include plan-aware x-ratelimit-* headers.
servers:
  - url: https://api.bennbots.com
    description: Configured API host
security:
  - ApiKey: []
  - BearerAuth: []
tags:
  - name: System
    description: Liveness, readiness, provider, and ingestion status.
  - name: Signals
    description: Derived signal and news-burst intelligence.
  - name: Rankings
    description: Daily ranked ticker intelligence.
  - name: Research
    description: Ticker theses, normalized evidence, macro context, outcomes, and
      beta research surfaces.
  - name: Watchlists
    description: Organization-scoped watchlists and ticker membership.
  - name: Usage
    description: Organization-scoped API metering.
paths:
  /api/v1/health:
    get:
      tags:
        - System
      summary: Check API health
      security: []
      responses:
        "200":
          description: Service is available.
          headers:
            x-ratelimit-limit:
              $ref: "#/components/headers/RateLimit"
            x-ratelimit-remaining:
              $ref: "#/components/headers/RateLimitRemaining"
            x-ratelimit-reset:
              $ref: "#/components/headers/RateLimitReset"
            x-ratelimit-plan:
              $ref: "#/components/headers/RateLimitPlan"
            x-request-id:
              $ref: "#/components/headers/RequestId"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/HealthEnvelope"
              examples:
                health:
                  $ref: "#/components/examples/HealthResponse"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalServerError"
      operationId: getApiV1Health
      x-signalforge:
        surface: Public customer API
        maturity: Production
        requiredScopes: []
        dataPolicy: Secret-safe service liveness only.
  /api/v1/health/readiness:
    get:
      tags:
        - System
      summary: Check production readiness and data freshness
      description: Combines API liveness, Supabase reachability, provider status,
        latest ingestion status, source mix, provider issues, and data freshness
        into a production-safe readiness verdict.
      security: []
      responses:
        "200":
          description: Production is ready or degraded but still serving.
          headers:
            x-ratelimit-limit:
              $ref: "#/components/headers/RateLimit"
            x-ratelimit-remaining:
              $ref: "#/components/headers/RateLimitRemaining"
            x-ratelimit-reset:
              $ref: "#/components/headers/RateLimitReset"
            x-ratelimit-plan:
              $ref: "#/components/headers/RateLimitPlan"
            x-request-id:
              $ref: "#/components/headers/RequestId"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ReadinessEnvelope"
              examples:
                readiness:
                  $ref: "#/components/examples/ReadinessResponse"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "503":
          description: A critical dependency or freshness invariant is unavailable.
          headers:
            x-ratelimit-limit:
              $ref: "#/components/headers/RateLimit"
            x-ratelimit-remaining:
              $ref: "#/components/headers/RateLimitRemaining"
            x-ratelimit-reset:
              $ref: "#/components/headers/RateLimitReset"
            x-ratelimit-plan:
              $ref: "#/components/headers/RateLimitPlan"
            x-request-id:
              $ref: "#/components/headers/RequestId"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ReadinessEnvelope"
              examples:
                readiness:
                  $ref: "#/components/examples/ReadinessResponse"
      operationId: getApiV1HealthReadiness
      x-signalforge:
        surface: Public customer API
        maturity: Production
        requiredScopes: []
        dataPolicy: Secret-safe readiness and freshness metadata only.
  /api/v1/signals:
    get:
      tags:
        - Signals
      summary: List recent signals
      parameters:
        - name: ticker
          in: query
          schema:
            type: string
            pattern: ^[A-Za-z.-]{1,10}$
        - name: signal_type
          in: query
          schema:
            $ref: "#/components/schemas/SignalType"
        - name: source
          in: query
          schema:
            type: string
        - name: min_score
          in: query
          schema:
            type: number
            minimum: 0
            maximum: 100
        - name: from_date
          in: query
          schema:
            type: string
            format: date
        - name: to_date
          in: query
          schema:
            type: string
            format: date
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 500
            default: 100
      responses:
        "200":
          description: Matching signals
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - meta
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/Signal"
                  meta:
                    $ref: "#/components/schemas/Meta"
              examples:
                signalWithEvidence:
                  $ref: "#/components/examples/SignalWithEvidence"
          headers:
            x-ratelimit-limit:
              $ref: "#/components/headers/RateLimit"
            x-ratelimit-remaining:
              $ref: "#/components/headers/RateLimitRemaining"
            x-ratelimit-reset:
              $ref: "#/components/headers/RateLimitReset"
            x-ratelimit-plan:
              $ref: "#/components/headers/RateLimitPlan"
            x-request-id:
              $ref: "#/components/headers/RequestId"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalServerError"
      operationId: getApiV1Signals
      x-signalforge:
        surface: Public customer API
        maturity: Production
        requiredScopes:
          - signals:read
        dataPolicy: Returns derived intelligence and normalized summaries only; raw
          vendor payloads and secrets are not exposed.
      security:
        - ApiKey: []
        - BearerAuth: []
  /api/v1/signals/{id}:
    get:
      tags:
        - Signals
      summary: Get a signal
      parameters:
        - $ref: "#/components/parameters/SignalId"
      responses:
        "200":
          description: Signal details.
          headers:
            x-ratelimit-limit:
              $ref: "#/components/headers/RateLimit"
            x-ratelimit-remaining:
              $ref: "#/components/headers/RateLimitRemaining"
            x-ratelimit-reset:
              $ref: "#/components/headers/RateLimitReset"
            x-ratelimit-plan:
              $ref: "#/components/headers/RateLimitPlan"
            x-request-id:
              $ref: "#/components/headers/RequestId"
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - meta
                properties:
                  data:
                    $ref: "#/components/schemas/Signal"
                  meta:
                    $ref: "#/components/schemas/Meta"
              examples:
                signal:
                  $ref: "#/components/examples/SignalDetailResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          description: Signal lookup miss. This route currently returns an API envelope
            with a 404 status.
          headers:
            x-ratelimit-limit:
              $ref: "#/components/headers/RateLimit"
            x-ratelimit-remaining:
              $ref: "#/components/headers/RateLimitRemaining"
            x-ratelimit-reset:
              $ref: "#/components/headers/RateLimitReset"
            x-ratelimit-plan:
              $ref: "#/components/headers/RateLimitPlan"
            x-request-id:
              $ref: "#/components/headers/RequestId"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LookupNotFoundEnvelope"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalServerError"
      operationId: getApiV1SignalsId
      x-signalforge:
        surface: Public customer API
        maturity: Production
        requiredScopes:
          - signals:read
        dataPolicy: Returns derived intelligence and normalized summaries only; raw
          vendor payloads and secrets are not exposed.
      security:
        - ApiKey: []
        - BearerAuth: []
  /api/v1/evidence/{id}:
    get:
      tags:
        - Research
      summary: Get normalized evidence for a signal input
      description: Returns a safe, normalized evidence object with provider/source
        references and a human-readable summary. Raw vendor payloads remain
        server-side and are not redistributed.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: Normalized evidence details.
          headers:
            x-ratelimit-limit:
              $ref: "#/components/headers/RateLimit"
            x-ratelimit-remaining:
              $ref: "#/components/headers/RateLimitRemaining"
            x-ratelimit-reset:
              $ref: "#/components/headers/RateLimitReset"
            x-ratelimit-plan:
              $ref: "#/components/headers/RateLimitPlan"
            x-request-id:
              $ref: "#/components/headers/RequestId"
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - meta
                properties:
                  data:
                    $ref: "#/components/schemas/Evidence"
                  meta:
                    $ref: "#/components/schemas/Meta"
              examples:
                normalizedEvidence:
                  $ref: "#/components/examples/NormalizedEvidence"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          description: Evidence lookup miss. This route currently returns an API envelope
            with a 404 status.
          headers:
            x-ratelimit-limit:
              $ref: "#/components/headers/RateLimit"
            x-ratelimit-remaining:
              $ref: "#/components/headers/RateLimitRemaining"
            x-ratelimit-reset:
              $ref: "#/components/headers/RateLimitReset"
            x-ratelimit-plan:
              $ref: "#/components/headers/RateLimitPlan"
            x-request-id:
              $ref: "#/components/headers/RequestId"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/LookupNotFoundEnvelope"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalServerError"
      operationId: getApiV1EvidenceId
      x-signalforge:
        surface: Public customer API
        maturity: Production
        requiredScopes:
          - signals:read
        dataPolicy: Returns normalized evidence summaries and references, never raw
          vendor payloads.
      security:
        - ApiKey: []
        - BearerAuth: []
  /api/v1/signals/{id}/outcomes:
    get:
      tags:
        - Research
      summary: List forward outcome windows for a signal
      description: Returns 1, 5, 20, and 60 day signal outcome windows with pending,
        measured, or failed measurement status.
      parameters:
        - $ref: "#/components/parameters/SignalId"
      responses:
        "200":
          description: Signal outcome status.
          headers:
            x-ratelimit-limit:
              $ref: "#/components/headers/RateLimit"
            x-ratelimit-remaining:
              $ref: "#/components/headers/RateLimitRemaining"
            x-ratelimit-reset:
              $ref: "#/components/headers/RateLimitReset"
            x-ratelimit-plan:
              $ref: "#/components/headers/RateLimitPlan"
            x-request-id:
              $ref: "#/components/headers/RequestId"
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - meta
                properties:
                  data:
                    type: object
                    properties:
                      signal_id:
                        type: string
                      ticker:
                        type: string
                      status:
                        type: string
                        enum:
                          - pending
                          - measured
                          - failed
                      supported_horizons_days:
                        type: array
                        items:
                          type: integer
                      outcomes:
                        type: array
                        items:
                          $ref: "#/components/schemas/SignalOutcome"
                  meta:
                    $ref: "#/components/schemas/Meta"
              examples:
                outcomes:
                  $ref: "#/components/examples/SignalOutcomesResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalServerError"
      operationId: getApiV1SignalsIdOutcomes
      x-signalforge:
        surface: Public customer API
        maturity: Beta
        requiredScopes:
          - signals:read
        dataPolicy: Outcome windows are returned when measurement data exists; pending
          windows are explicit.
      security:
        - ApiKey: []
        - BearerAuth: []
  /api/v1/rankings/daily:
    get:
      tags:
        - Rankings
      summary: List current daily ticker rankings
      responses:
        "200":
          description: Daily rankings.
          headers:
            x-ratelimit-limit:
              $ref: "#/components/headers/RateLimit"
            x-ratelimit-remaining:
              $ref: "#/components/headers/RateLimitRemaining"
            x-ratelimit-reset:
              $ref: "#/components/headers/RateLimitReset"
            x-ratelimit-plan:
              $ref: "#/components/headers/RateLimitPlan"
            x-request-id:
              $ref: "#/components/headers/RequestId"
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - meta
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/DailyRanking"
                  meta:
                    $ref: "#/components/schemas/Meta"
              examples:
                rankings:
                  $ref: "#/components/examples/RankingsResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalServerError"
      operationId: getApiV1RankingsDaily
      x-signalforge:
        surface: Public customer API
        maturity: Production
        requiredScopes:
          - signals:read
        dataPolicy: Returns derived intelligence and normalized summaries only; raw
          vendor payloads and secrets are not exposed.
      security:
        - ApiKey: []
        - BearerAuth: []
  /api/v1/tickers/{ticker}/signals:
    get:
      tags:
        - Signals
      summary: List signals for a ticker
      parameters:
        - $ref: "#/components/parameters/Ticker"
      responses:
        "200":
          description: Ticker signals.
          headers:
            x-ratelimit-limit:
              $ref: "#/components/headers/RateLimit"
            x-ratelimit-remaining:
              $ref: "#/components/headers/RateLimitRemaining"
            x-ratelimit-reset:
              $ref: "#/components/headers/RateLimitReset"
            x-ratelimit-plan:
              $ref: "#/components/headers/RateLimitPlan"
            x-request-id:
              $ref: "#/components/headers/RequestId"
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - meta
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/Signal"
                  meta:
                    $ref: "#/components/schemas/Meta"
              examples:
                signals:
                  $ref: "#/components/examples/SignalWithEvidence"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalServerError"
      operationId: getApiV1TickersTickerSignals
      x-signalforge:
        surface: Public customer API
        maturity: Production
        requiredScopes:
          - signals:read
        dataPolicy: Returns derived intelligence and normalized summaries only; raw
          vendor payloads and secrets are not exposed.
      security:
        - ApiKey: []
        - BearerAuth: []
  /api/v1/providers/status:
    get:
      tags:
        - System
      summary: List configured normalized data providers
      responses:
        "200":
          description: Provider names, selected adapters, missing config variable names,
            mock status, and capabilities; never secret values.
          headers:
            x-ratelimit-limit:
              $ref: "#/components/headers/RateLimit"
            x-ratelimit-remaining:
              $ref: "#/components/headers/RateLimitRemaining"
            x-ratelimit-reset:
              $ref: "#/components/headers/RateLimitReset"
            x-ratelimit-plan:
              $ref: "#/components/headers/RateLimitPlan"
            x-request-id:
              $ref: "#/components/headers/RequestId"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProviderStatusEnvelope"
              examples:
                providers:
                  $ref: "#/components/examples/ProviderStatusResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalServerError"
      operationId: getApiV1ProvidersStatus
      x-signalforge:
        surface: Public customer API
        maturity: Production
        requiredScopes:
          - signals:read
        dataPolicy: Shows provider names, selected adapters, capabilities, and missing
          configuration names only; never secret values.
      security:
        - ApiKey: []
        - BearerAuth: []
  /api/v1/ingestion/status:
    get:
      tags:
        - System
      summary: Inspect ingestion readiness and recent provider issues
      description: Returns storage mode, persisted intelligence counts, latest
        ingestion run metadata, and sanitized provider fallback messages.
      responses:
        "200":
          description: Ingestion and provider readiness status.
          headers:
            x-ratelimit-limit:
              $ref: "#/components/headers/RateLimit"
            x-ratelimit-remaining:
              $ref: "#/components/headers/RateLimitRemaining"
            x-ratelimit-reset:
              $ref: "#/components/headers/RateLimitReset"
            x-ratelimit-plan:
              $ref: "#/components/headers/RateLimitPlan"
            x-request-id:
              $ref: "#/components/headers/RequestId"
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - meta
                properties:
                  data:
                    $ref: "#/components/schemas/IngestionStatus"
                  meta:
                    $ref: "#/components/schemas/Meta"
              examples:
                ingestion:
                  $ref: "#/components/examples/IngestionStatusResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalServerError"
      operationId: getApiV1IngestionStatus
      x-signalforge:
        surface: Public customer API
        maturity: Beta
        requiredScopes:
          - signals:read
        dataPolicy: Operational status is sanitized and does not include provider
          secrets or raw payloads.
      security:
        - ApiKey: []
        - BearerAuth: []
  /api/v1/macro/context:
    get:
      tags:
        - Research
      summary: Get the latest interpreted macro regime
      responses:
        "200":
          description: Derived macro context.
          headers:
            x-ratelimit-limit:
              $ref: "#/components/headers/RateLimit"
            x-ratelimit-remaining:
              $ref: "#/components/headers/RateLimitRemaining"
            x-ratelimit-reset:
              $ref: "#/components/headers/RateLimitReset"
            x-ratelimit-plan:
              $ref: "#/components/headers/RateLimitPlan"
            x-request-id:
              $ref: "#/components/headers/RequestId"
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - meta
                properties:
                  data:
                    $ref: "#/components/schemas/MacroSnapshot"
                  meta:
                    $ref: "#/components/schemas/Meta"
              examples:
                macro:
                  $ref: "#/components/examples/MacroContextResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalServerError"
      operationId: getApiV1MacroContext
      x-signalforge:
        surface: Public customer API
        maturity: Beta
        requiredScopes:
          - signals:read
        dataPolicy: Returns derived intelligence and normalized summaries only; raw
          vendor payloads and secrets are not exposed.
      security:
        - ApiKey: []
        - BearerAuth: []
  /api/v1/news-bursts:
    get:
      tags:
        - Signals
      summary: List derived abnormal news-coverage signals
      responses:
        "200":
          description: Recent news burst signals.
          headers:
            x-ratelimit-limit:
              $ref: "#/components/headers/RateLimit"
            x-ratelimit-remaining:
              $ref: "#/components/headers/RateLimitRemaining"
            x-ratelimit-reset:
              $ref: "#/components/headers/RateLimitReset"
            x-ratelimit-plan:
              $ref: "#/components/headers/RateLimitPlan"
            x-request-id:
              $ref: "#/components/headers/RequestId"
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - meta
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/Signal"
                  meta:
                    $ref: "#/components/schemas/Meta"
              examples:
                newsBursts:
                  $ref: "#/components/examples/NewsBurstsResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalServerError"
      operationId: getApiV1NewsBursts
      x-signalforge:
        surface: Public customer API
        maturity: Beta
        requiredScopes:
          - signals:read
        dataPolicy: Returns derived intelligence and normalized summaries only; raw
          vendor payloads and secrets are not exposed.
      security:
        - ApiKey: []
        - BearerAuth: []
      parameters:
        - name: limit
          in: query
          schema:
            type: integer
            minimum: 1
            maximum: 500
            default: 100
          description: Maximum number of news-burst signals to return.
  /api/v1/companies/{ticker}/agent-brief:
    get:
      tags:
        - Research
      summary: Get an agent-ready structured company brief
      description: Returns derived company intelligence for AI agents and developer
        workflows without redistributing raw vendor payloads.
      parameters:
        - $ref: "#/components/parameters/Ticker"
      responses:
        "200":
          description: Structured company agent brief.
          headers:
            x-ratelimit-limit:
              $ref: "#/components/headers/RateLimit"
            x-ratelimit-remaining:
              $ref: "#/components/headers/RateLimitRemaining"
            x-ratelimit-reset:
              $ref: "#/components/headers/RateLimitReset"
            x-ratelimit-plan:
              $ref: "#/components/headers/RateLimitPlan"
            x-request-id:
              $ref: "#/components/headers/RequestId"
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - meta
                properties:
                  data:
                    $ref: "#/components/schemas/CompanyAgentBrief"
                  meta:
                    $ref: "#/components/schemas/Meta"
              examples:
                companyAgentBrief:
                  $ref: "#/components/examples/CompanyAgentBriefResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalServerError"
      operationId: getApiV1CompaniesTickerAgentBrief
      x-signalforge:
        surface: Public customer API
        maturity: Beta
        requiredScopes:
          - signals:read
        dataPolicy: Agent Briefs are derived summaries for workflow automation and
          remain beta.
      security:
        - ApiKey: []
        - BearerAuth: []
  /api/v1/tickers/{ticker}/thesis:
    get:
      tags:
        - Research
      summary: Get an explainable ticker thesis
      parameters:
        - $ref: "#/components/parameters/Ticker"
      responses:
        "200":
          description: Ticker thesis.
          headers:
            x-ratelimit-limit:
              $ref: "#/components/headers/RateLimit"
            x-ratelimit-remaining:
              $ref: "#/components/headers/RateLimitRemaining"
            x-ratelimit-reset:
              $ref: "#/components/headers/RateLimitReset"
            x-ratelimit-plan:
              $ref: "#/components/headers/RateLimitPlan"
            x-request-id:
              $ref: "#/components/headers/RequestId"
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - meta
                properties:
                  data:
                    $ref: "#/components/schemas/TickerThesis"
                  meta:
                    $ref: "#/components/schemas/Meta"
              examples:
                tickerThesis:
                  $ref: "#/components/examples/AgentBriefResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalServerError"
      operationId: getApiV1TickersTickerThesis
      x-signalforge:
        surface: Public customer API
        maturity: Beta
        requiredScopes:
          - signals:read
        dataPolicy: Returns derived intelligence and normalized summaries only; raw
          vendor payloads and secrets are not exposed.
      security:
        - ApiKey: []
        - BearerAuth: []
  /api/v1/backtests:
    get:
      tags:
        - Research
      summary: Get backtest capabilities and runs
      responses:
        "200":
          description: Placeholder capabilities and an empty run list.
          headers:
            x-ratelimit-limit:
              $ref: "#/components/headers/RateLimit"
            x-ratelimit-remaining:
              $ref: "#/components/headers/RateLimitRemaining"
            x-ratelimit-reset:
              $ref: "#/components/headers/RateLimitReset"
            x-ratelimit-plan:
              $ref: "#/components/headers/RateLimitPlan"
            x-request-id:
              $ref: "#/components/headers/RequestId"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BacktestRunsEnvelope"
              examples:
                backtests:
                  $ref: "#/components/examples/BacktestCapabilitiesResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalServerError"
      operationId: getApiV1Backtests
      x-signalforge:
        surface: Public customer API
        maturity: Placeholder
        requiredScopes:
          - signals:read
        dataPolicy: Backtest execution is not enabled; this returns reserved
          capabilities and an empty run list.
      security:
        - ApiKey: []
        - BearerAuth: []
    post:
      tags:
        - Research
      summary: Submit a backtest
      description: Reserved endpoint; execution is not enabled in the beta.
      responses:
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalServerError"
        "501":
          $ref: "#/components/responses/NotImplemented"
      operationId: postApiV1Backtests
      x-signalforge:
        surface: Public customer API
        maturity: Placeholder
        requiredScopes:
          - signals:read
        dataPolicy: Reserved endpoint. Current beta returns 501 until historical
          execution jobs are enabled.
      security:
        - ApiKey: []
        - BearerAuth: []
  /api/v1/backtests/{id}:
    get:
      tags:
        - Research
      summary: Get a backtest run
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalServerError"
      operationId: getApiV1BacktestsId
      x-signalforge:
        surface: Public customer API
        maturity: Placeholder
        requiredScopes:
          - signals:read
        dataPolicy: Backtest run lookup is reserved and currently returns a not-found
          placeholder.
      security:
        - ApiKey: []
        - BearerAuth: []
  /api/v1/backtests/signal-types:
    get:
      tags:
        - Research
      summary: List signal definitions and outcome availability
      responses:
        "200":
          description: Backtest signal research status.
          headers:
            x-ratelimit-limit:
              $ref: "#/components/headers/RateLimit"
            x-ratelimit-remaining:
              $ref: "#/components/headers/RateLimitRemaining"
            x-ratelimit-reset:
              $ref: "#/components/headers/RateLimitReset"
            x-ratelimit-plan:
              $ref: "#/components/headers/RateLimitPlan"
            x-request-id:
              $ref: "#/components/headers/RequestId"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BacktestSignalTypeSummaryEnvelope"
              examples:
                signalTypes:
                  $ref: "#/components/examples/BacktestSignalTypesResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalServerError"
      operationId: getApiV1BacktestsSignalTypes
      x-signalforge:
        surface: Public customer API
        maturity: Placeholder
        requiredScopes:
          - signals:read
        dataPolicy: Signal type summaries expose readiness status, not performance claims.
      security:
        - ApiKey: []
        - BearerAuth: []
  /api/v1/watchlists:
    get:
      tags:
        - Watchlists
      summary: List organization watchlists
      description: Requires the watchlists:read scope. Results are always scoped to
        the API key organization.
      responses:
        "200":
          description: Organization watchlists
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - meta
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/Watchlist"
                  meta:
                    $ref: "#/components/schemas/Meta"
              examples:
                watchlists:
                  $ref: "#/components/examples/WatchlistsResponse"
          headers:
            x-ratelimit-limit:
              $ref: "#/components/headers/RateLimit"
            x-ratelimit-remaining:
              $ref: "#/components/headers/RateLimitRemaining"
            x-ratelimit-reset:
              $ref: "#/components/headers/RateLimitReset"
            x-ratelimit-plan:
              $ref: "#/components/headers/RateLimitPlan"
            x-request-id:
              $ref: "#/components/headers/RequestId"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalServerError"
      operationId: getApiV1Watchlists
      x-signalforge:
        surface: Public customer API
        maturity: Beta
        requiredScopes:
          - watchlists:read
        dataPolicy: Organization-scoped watchlist resources only.
      security:
        - ApiKey: []
        - BearerAuth: []
    post:
      tags:
        - Watchlists
      summary: Create an organization watchlist
      description: Requires the watchlists:write scope. The created watchlist belongs
        to the API key organization.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WatchlistCreateRequest"
      responses:
        "201":
          description: Watchlist created
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - meta
                properties:
                  data:
                    $ref: "#/components/schemas/Watchlist"
                  meta:
                    $ref: "#/components/schemas/Meta"
          headers:
            x-ratelimit-limit:
              $ref: "#/components/headers/RateLimit"
            x-ratelimit-remaining:
              $ref: "#/components/headers/RateLimitRemaining"
            x-ratelimit-reset:
              $ref: "#/components/headers/RateLimitReset"
            x-ratelimit-plan:
              $ref: "#/components/headers/RateLimitPlan"
            x-request-id:
              $ref: "#/components/headers/RequestId"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "409":
          description: Watchlist name already exists for the organization
        "422":
          $ref: "#/components/responses/ValidationFailed"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalServerError"
      operationId: postApiV1Watchlists
      x-signalforge:
        surface: Public customer API
        maturity: Beta
        requiredScopes:
          - watchlists:write
        dataPolicy: Organization-scoped watchlist resources only.
      security:
        - ApiKey: []
        - BearerAuth: []
  /api/v1/watchlists/{id}:
    get:
      tags:
        - Watchlists
      summary: Get an organization watchlist
      description: Requires the watchlists:read scope.
      parameters:
        - $ref: "#/components/parameters/WatchlistId"
      responses:
        "200":
          description: Watchlist detail
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - meta
                properties:
                  data:
                    $ref: "#/components/schemas/Watchlist"
                  meta:
                    $ref: "#/components/schemas/Meta"
          headers:
            x-ratelimit-limit:
              $ref: "#/components/headers/RateLimit"
            x-ratelimit-remaining:
              $ref: "#/components/headers/RateLimitRemaining"
            x-ratelimit-reset:
              $ref: "#/components/headers/RateLimitReset"
            x-ratelimit-plan:
              $ref: "#/components/headers/RateLimitPlan"
            x-request-id:
              $ref: "#/components/headers/RequestId"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalServerError"
      operationId: getApiV1WatchlistsId
      x-signalforge:
        surface: Public customer API
        maturity: Beta
        requiredScopes:
          - watchlists:read
        dataPolicy: Organization-scoped watchlist resources only.
      security:
        - ApiKey: []
        - BearerAuth: []
  /api/v1/watchlists/{id}/tickers:
    post:
      tags:
        - Watchlists
      summary: Add a ticker to a watchlist
      description: Requires the watchlists:write scope. Adding an existing ticker is
        idempotent.
      parameters:
        - $ref: "#/components/parameters/WatchlistId"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/WatchlistTickerRequest"
      responses:
        "200":
          description: Updated watchlist
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - meta
                properties:
                  data:
                    $ref: "#/components/schemas/Watchlist"
                  meta:
                    $ref: "#/components/schemas/Meta"
          headers:
            x-ratelimit-limit:
              $ref: "#/components/headers/RateLimit"
            x-ratelimit-remaining:
              $ref: "#/components/headers/RateLimitRemaining"
            x-ratelimit-reset:
              $ref: "#/components/headers/RateLimitReset"
            x-ratelimit-plan:
              $ref: "#/components/headers/RateLimitPlan"
            x-request-id:
              $ref: "#/components/headers/RequestId"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "422":
          $ref: "#/components/responses/ValidationFailed"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalServerError"
      operationId: postApiV1WatchlistsIdTickers
      x-signalforge:
        surface: Public customer API
        maturity: Beta
        requiredScopes:
          - watchlists:write
        dataPolicy: Organization-scoped watchlist resources only.
      security:
        - ApiKey: []
        - BearerAuth: []
  /api/v1/watchlists/{id}/tickers/{ticker}:
    delete:
      tags:
        - Watchlists
      summary: Remove a ticker from a watchlist
      description: Requires the watchlists:write scope. Removing a ticker that is not
        present is idempotent.
      parameters:
        - $ref: "#/components/parameters/WatchlistId"
        - name: ticker
          in: path
          required: true
          schema:
            type: string
            pattern: ^[A-Za-z][A-Za-z0-9.-]{0,9}$
      responses:
        "200":
          description: Updated watchlist
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - meta
                properties:
                  data:
                    $ref: "#/components/schemas/Watchlist"
                  meta:
                    $ref: "#/components/schemas/Meta"
          headers:
            x-ratelimit-limit:
              $ref: "#/components/headers/RateLimit"
            x-ratelimit-remaining:
              $ref: "#/components/headers/RateLimitRemaining"
            x-ratelimit-reset:
              $ref: "#/components/headers/RateLimitReset"
            x-ratelimit-plan:
              $ref: "#/components/headers/RateLimitPlan"
            x-request-id:
              $ref: "#/components/headers/RequestId"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalServerError"
      operationId: deleteApiV1WatchlistsIdTickersTicker
      x-signalforge:
        surface: Public customer API
        maturity: Beta
        requiredScopes:
          - watchlists:write
        dataPolicy: Organization-scoped watchlist resources only.
      security:
        - ApiKey: []
        - BearerAuth: []
  /api/v1/usage:
    get:
      tags:
        - Usage
      summary: List recent usage for the API key organization
      description: Requires the usage:read scope.
      responses:
        "200":
          description: Recent usage log entries.
          headers:
            x-ratelimit-limit:
              $ref: "#/components/headers/RateLimit"
            x-ratelimit-remaining:
              $ref: "#/components/headers/RateLimitRemaining"
            x-ratelimit-reset:
              $ref: "#/components/headers/RateLimitReset"
            x-ratelimit-plan:
              $ref: "#/components/headers/RateLimitPlan"
            x-request-id:
              $ref: "#/components/headers/RequestId"
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - meta
                properties:
                  data:
                    type: array
                    items:
                      $ref: "#/components/schemas/UsageLog"
                  meta:
                    $ref: "#/components/schemas/Meta"
              examples:
                usage:
                  $ref: "#/components/examples/UsageResponse"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/RateLimited"
        "500":
          $ref: "#/components/responses/InternalServerError"
      operationId: getApiV1Usage
      x-signalforge:
        surface: Public customer API
        maturity: Production
        requiredScopes:
          - usage:read
        dataPolicy: Organization-scoped usage metadata only.
      security:
        - ApiKey: []
        - BearerAuth: []
components:
  securitySchemes:
    ApiKey:
      type: apiKey
      in: header
      name: x-api-key
    BearerAuth:
      type: http
      scheme: bearer
  parameters:
    SignalId:
      name: id
      in: path
      required: true
      schema:
        type: string
    Ticker:
      name: ticker
      in: path
      required: true
      schema:
        type: string
        pattern: ^[A-Za-z.-]{1,10}$
    WatchlistId:
      name: id
      in: path
      required: true
      schema:
        type: string
  headers:
    RequestId:
      description: Correlation ID for support and logs
      schema:
        type: string
        format: uuid
    RateLimit:
      description: Requests allowed in the current plan window
      schema:
        type: integer
    RateLimitRemaining:
      description: Requests remaining in the current window
      schema:
        type: integer
    RateLimitReset:
      description: Unix timestamp when the current window resets
      schema:
        type: integer
    RateLimitPlan:
      description: Plan key applied to the current request window
      schema:
        type: string
  responses:
    Unauthorized:
      description: Missing, expired, revoked, or invalid API key.
      headers:
        x-request-id:
          $ref: "#/components/headers/RequestId"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorEnvelope"
    Forbidden:
      description: API key lacks the required scope.
      headers:
        x-request-id:
          $ref: "#/components/headers/RequestId"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorEnvelope"
    NotFound:
      description: Resource not found.
      headers:
        x-request-id:
          $ref: "#/components/headers/RequestId"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorEnvelope"
    RateLimited:
      description: Plan rate limit exceeded.
      headers:
        x-ratelimit-limit:
          $ref: "#/components/headers/RateLimit"
        x-ratelimit-remaining:
          $ref: "#/components/headers/RateLimitRemaining"
        x-ratelimit-reset:
          $ref: "#/components/headers/RateLimitReset"
        x-ratelimit-plan:
          $ref: "#/components/headers/RateLimitPlan"
        x-request-id:
          $ref: "#/components/headers/RequestId"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorEnvelope"
    NotImplemented:
      description: Endpoint contract is reserved but execution is unavailable.
      headers:
        x-request-id:
          $ref: "#/components/headers/RequestId"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorEnvelope"
    BadRequest:
      description: Malformed request, invalid JSON, or invalid query parameter.
      headers:
        x-request-id:
          $ref: "#/components/headers/RequestId"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorEnvelope"
    Conflict:
      description: Resource conflict, such as a duplicate watchlist name.
      headers:
        x-request-id:
          $ref: "#/components/headers/RequestId"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorEnvelope"
    ValidationFailed:
      description: Request body was valid JSON but failed domain validation.
      headers:
        x-request-id:
          $ref: "#/components/headers/RequestId"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorEnvelope"
    InternalServerError:
      description: Unexpected server-side failure. The x-request-id header can be used
        for support correlation.
      headers:
        x-request-id:
          $ref: "#/components/headers/RequestId"
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorEnvelope"
  schemas:
    Meta:
      type: object
      required:
        - api_version
        - request_id
        - disclaimer
      properties:
        environment:
          type: string
          description: Runtime environment label, such as production, preview,
            development, or test.
        data_mode:
          type: string
          enum:
            - live
            - mock
          description: Effective SignalForge data mode for the response.
        mock_data:
          type: boolean
          description: Whether mock data is enabled for this runtime.
        api_version:
          type: string
          const: v1
        request_id:
          type: string
          format: uuid
        disclaimer:
          type: string
        count:
          type: integer
    ErrorEnvelope:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          required:
            - code
            - message
          properties:
            code:
              type: string
            message:
              type: string
    HealthEnvelope:
      type: object
      properties:
        data:
          type: object
          properties:
            ok:
              type: boolean
            service:
              type: string
            version:
              type: string
            checked_at:
              type: string
              format: date-time
            environment:
              type: string
            data_mode:
              type: string
              enum:
                - live
                - mock
            mock_data_enabled:
              type: boolean
            live_sources_configured:
              type: boolean
            missing_required_config:
              type: array
              items:
                type: string
        meta:
          $ref: "#/components/schemas/Meta"
    ReadinessEnvelope:
      type: object
      required:
        - data
        - meta
      properties:
        data:
          $ref: "#/components/schemas/ReadinessReport"
        meta:
          $ref: "#/components/schemas/Meta"
    ReadinessReport:
      type: object
      required:
        - status
        - checked_at
        - environment
        - supabase_configured
        - components
        - counts
        - latest_ingestion_run
        - latest_ingestion_run_health
        - latest_source_mix
        - freshness
        - provider_status
        - recent_provider_issues
        - thresholds
      properties:
        status:
          type: string
          enum:
            - ready
            - degraded
            - unavailable
        checked_at:
          type: string
          format: date-time
        environment:
          type: string
        supabase_configured:
          type: boolean
        components:
          type: array
          items:
            $ref: "#/components/schemas/ReadinessComponent"
        counts:
          anyOf:
            - $ref: "#/components/schemas/ReadinessCounts"
            - type: "null"
        latest_ingestion_run:
          anyOf:
            - $ref: "#/components/schemas/IngestionRun"
            - type: "null"
        latest_ingestion_run_health:
          anyOf:
            - $ref: "#/components/schemas/IngestionRunHealth"
            - type: "null"
        latest_source_mix:
          anyOf:
            - $ref: "#/components/schemas/IngestionSourceMix"
            - type: "null"
        freshness:
          type: array
          items:
            $ref: "#/components/schemas/DataFreshnessCheck"
        provider_status:
          type: array
          items:
            $ref: "#/components/schemas/ProviderStatus"
        recent_provider_issues:
          type: array
          items:
            $ref: "#/components/schemas/ProviderIssue"
        thresholds:
          $ref: "#/components/schemas/ReadinessThresholds"
    ReadinessComponent:
      type: object
      required:
        - name
        - status
        - message
      properties:
        name:
          type: string
          enum:
            - api
            - database
            - providers
            - ingestion
            - data_freshness
        status:
          type: string
          enum:
            - ok
            - warning
            - critical
        message:
          type: string
        details:
          type: object
          additionalProperties: true
    ReadinessCounts:
      type: object
      required:
        - daily_rankings
        - ingestion_runs
        - macro_snapshots
        - news_mentions
        - signals
        - source_records
      properties:
        daily_rankings:
          type: integer
        ingestion_runs:
          type: integer
        macro_snapshots:
          type: integer
        news_mentions:
          type: integer
        signals:
          type: integer
        source_records:
          type: integer
    DataFreshnessCheck:
      type: object
      required:
        - name
        - status
        - latest_at
        - age_minutes
        - threshold_minutes
        - message
      properties:
        name:
          type: string
          enum:
            - ingestion_run
            - signals
            - daily_rankings
            - macro_snapshot
            - news_mentions
        status:
          type: string
          enum:
            - ok
            - warning
            - critical
        latest_at:
          type:
            - string
            - "null"
          format: date-time
        age_minutes:
          type:
            - integer
            - "null"
        threshold_minutes:
          type: integer
        message:
          type: string
    ReadinessThresholds:
      type: object
      required:
        - ingestion_run_minutes
        - running_ingestion_minutes
        - signals_minutes
        - daily_rankings_minutes
        - macro_snapshot_minutes
        - news_mentions_minutes
      properties:
        ingestion_run_minutes:
          type: integer
        running_ingestion_minutes:
          type: integer
        signals_minutes:
          type: integer
        daily_rankings_minutes:
          type: integer
        macro_snapshot_minutes:
          type: integer
        news_mentions_minutes:
          type: integer
    SignalType:
      type: string
      enum:
        - insider_conviction
        - insider_cluster
        - political_activity
        - earnings_momentum
        - price_momentum
        - composite_signal
        - large_insider_buy
        - insider_cluster_buy
        - repeat_insider_buyer
        - congress_buy_activity
        - earnings_catalyst
        - analyst_momentum
        - analyst_upgrade
        - news_burst
    Signal:
      type: object
      description: Derived signal object with explainable scoring and current beta
        provenance pointers. Raw source records are retained server-side and are
        not redistributed through the public API.
      required:
        - id
        - ticker
        - signal_type
        - score
        - confidence
        - reason
        - event_date
        - generated_at
        - scoring_version
        - components
        - score_components
        - score_explanation
      properties:
        id:
          type: string
        ticker:
          type: string
        signal_type:
          $ref: "#/components/schemas/SignalType"
        score:
          type: number
          minimum: 0
          maximum: 100
        confidence:
          type: number
          minimum: 0
          maximum: 1
        reason:
          type: string
        event_date:
          type: string
          format: date
        generated_at:
          type: string
          format: date-time
        effective_at:
          type: string
          format: date-time
        as_of:
          type: string
          format: date
        scoring_version:
          type: string
        input_hash:
          type: string
        coverage:
          type: number
          minimum: 0
          maximum: 1
        components:
          type: array
          items:
            $ref: "#/components/schemas/SignalComponent"
        score_components:
          type: object
          description: Machine-readable component contribution map. Values are normalized
            contributions to the final 0-100 score and generally sum to the
            final score after rounding.
          additionalProperties:
            type: number
        score_explanation:
          type: string
          description: Deterministic explanation of how the bounded score was derived from
            available components and input coverage.
        source_event_ids:
          type: array
          description: Current beta evidence pointers to normalized/source event IDs that
            contributed to the signal.
          items:
            type: string
        evidence_ids:
          type: array
          description: Fetchable normalized evidence object IDs for source inputs that
            contributed to the signal.
          items:
            type: string
        evidence_status:
          type: string
          enum:
            - available
            - partial
            - unavailable
          description: Whether normalized evidence summaries are fully available,
            partially available, or unavailable for this signal.
        source_lineage:
          type: array
          description: Safe source/evidence summaries for the records that contributed to
            the signal. Raw payloads are not included.
          items:
            $ref: "#/components/schemas/SourceLineageItem"
        source_names:
          type: array
          description: Normalized provider/source names that contributed to the derived
            signal. Values can include mock providers in local or fallback mode.
          items:
            type: string
        metadata:
          type: object
          additionalProperties: true
    SourceLineageItem:
      type: object
      required:
        - evidence_id
        - provider
        - source_type
        - external_id
        - observed_at
        - summary
        - confidence
        - source_quality
      properties:
        evidence_id:
          type: string
        provider:
          type: string
        source_type:
          type: string
        source_url:
          type:
            - string
            - "null"
          format: uri
        external_id:
          type: string
        observed_at:
          type: string
          format: date-time
        event_date:
          type:
            - string
            - "null"
          format: date
        ticker:
          type:
            - string
            - "null"
        company_name:
          type:
            - string
            - "null"
        summary:
          type: string
        confidence:
          type: number
          minimum: 0
          maximum: 1
        source_quality:
          type: string
          enum:
            - official
            - vendor
            - mock
            - unknown
    Evidence:
      type: object
      description: Safe, normalized evidence derived from source records. It includes
        traceability references and summaries, not raw vendor payloads.
      required:
        - id
        - provider
        - source_type
        - external_id
        - observed_at
        - raw_payload_ref
        - summary
        - confidence
        - source_quality
        - metadata
      properties:
        id:
          type: string
        provider:
          type: string
        source_type:
          type: string
        source_url:
          type:
            - string
            - "null"
          format: uri
        external_id:
          type: string
        observed_at:
          type: string
          format: date-time
        event_date:
          type:
            - string
            - "null"
          format: date
        ticker:
          type:
            - string
            - "null"
        company_name:
          type:
            - string
            - "null"
        raw_payload_ref:
          type: object
          required:
            - source_record_id
            - raw_event_id
            - content_hash
          properties:
            source_record_id:
              type: string
            raw_event_id:
              type:
                - string
                - "null"
            content_hash:
              type:
                - string
                - "null"
        summary:
          type: string
        confidence:
          type: number
          minimum: 0
          maximum: 1
        source_quality:
          type: string
          enum:
            - official
            - vendor
            - mock
            - unknown
        metadata:
          type: object
          additionalProperties: true
    SignalComponent:
      type: object
      required:
        - key
        - label
        - normalized_score
        - weight
        - contribution
        - reason_code
      properties:
        key:
          type: string
        label:
          type: string
        raw_value:
          type:
            - number
            - "null"
        normalized_score:
          type: number
          minimum: 0
          maximum: 100
        weight:
          type: number
          minimum: 0
          maximum: 1
        contribution:
          type: number
        reason_code:
          type: string
    DailyRanking:
      type: object
      properties:
        id:
          type: string
        ranking_date:
          type: string
          format: date
        rank:
          type: integer
        ticker:
          type: string
        score:
          type: number
          minimum: 0
          maximum: 100
        signal_count:
          type: integer
        primary_signal_types:
          type: array
          items:
            $ref: "#/components/schemas/SignalType"
        reason_summary:
          type: string
        generated_at:
          type: string
          format: date-time
    TickerThesis:
      type: object
      description: Concise ticker thesis response. For structured agent workflows,
        prefer CompanyAgentBrief.
      properties:
        ticker:
          type: string
        score:
          type: number
        thesis:
          type: string
        supporting_signals:
          type: array
          items:
            $ref: "#/components/schemas/Signal"
        supporting_evidence:
          type: array
          items:
            $ref: "#/components/schemas/SourceLineageItem"
        risks:
          type: array
          items:
            type: string
        generated_at:
          type: string
          format: date-time
    CompanyAgentBrief:
      type: object
      description: Structured, agent-ready company intelligence built from derived
        signals, normalized evidence, and source-lineage summaries.
      required:
        - ticker
        - company
        - as_of
        - machine_summary
        - human_summary
        - active_signals
        - why_now
        - supporting_evidence
        - risk_flags
        - source_lineage
        - confidence
        - freshness
        - suggested_agent_tasks
        - qa
        - outcome_status
        - generated_at
      properties:
        ticker:
          type: string
        company:
          type: object
          properties:
            ticker:
              type: string
            company_name:
              type:
                - string
                - "null"
            company_id:
              type:
                - string
                - "null"
            security_id:
              type:
                - string
                - "null"
            exchange:
              type:
                - string
                - "null"
            sector:
              type:
                - string
                - "null"
            industry:
              type:
                - string
                - "null"
        as_of:
          type: string
          format: date-time
        machine_summary:
          type: object
          additionalProperties: true
        human_summary:
          type: string
        active_signals:
          type: array
          items:
            $ref: "#/components/schemas/Signal"
        why_now:
          type: array
          items:
            type: string
        supporting_evidence:
          type: array
          items:
            $ref: "#/components/schemas/SourceLineageItem"
        contradicting_evidence:
          type: array
          items:
            $ref: "#/components/schemas/SourceLineageItem"
        risk_flags:
          type: array
          items:
            type: string
        source_lineage:
          type: array
          items:
            $ref: "#/components/schemas/SourceLineageItem"
        confidence:
          type: number
          minimum: 0
          maximum: 1
        freshness:
          type: object
          properties:
            generated_at:
              type: string
              format: date-time
            as_of:
              type:
                - string
                - "null"
            latest_signal_at:
              type:
                - string
                - "null"
              format: date-time
            latest_event_date:
              type:
                - string
                - "null"
              format: date
            signal_count:
              type: integer
            evidence_count:
              type: integer
        suggested_agent_tasks:
          type: array
          items:
            type: string
        qa:
          type: object
          properties:
            qa_status:
              type: string
              enum:
                - passed
                - flagged
                - skipped
                - failed
            qa_flags:
              type: array
              items:
                type: string
            confidence_notes:
              type: string
            reviewed_at:
              type:
                - string
                - "null"
              format: date-time
        outcome_status:
          type: object
          properties:
            status:
              type: string
              enum:
                - pending_data
                - measured
                - unavailable
            measured_signal_count:
              type: integer
            pending_signal_count:
              type: integer
            summary:
              type: string
        generated_at:
          type: string
          format: date-time
    SignalOutcome:
      type: object
      properties:
        id:
          type: string
        signal_id:
          type: string
          format: uuid
        ticker:
          type: string
        measured_at:
          type:
            - string
            - "null"
          format: date-time
        horizon_days:
          type: integer
        measurement_status:
          type: string
          enum:
            - pending
            - measured
            - failed
        baseline_price:
          type:
            - number
            - "null"
        outcome_price:
          type:
            - number
            - "null"
        return_pct:
          type:
            - number
            - "null"
        benchmark_ticker:
          type:
            - string
            - "null"
        benchmark_return_pct:
          type:
            - number
            - "null"
        excess_return_pct:
          type:
            - number
            - "null"
        direction_correct:
          type:
            - boolean
            - "null"
        error_message:
          type:
            - string
            - "null"
        metadata:
          type: object
    MacroSnapshot:
      type: object
      properties:
        snapshot_date:
          type: string
          format: date
        fed_funds_rate:
          type:
            - number
            - "null"
        treasury_2y:
          type:
            - number
            - "null"
        treasury_10y:
          type:
            - number
            - "null"
        yield_curve_10y_2y:
          type:
            - number
            - "null"
        inflation_rate:
          type:
            - number
            - "null"
        unemployment_rate:
          type:
            - number
            - "null"
        regime_label:
          type: string
        summary:
          type: string
        source_names:
          type: array
          items:
            type: string
    IngestionStatus:
      type: object
      required:
        - serving_mode
        - supabase_configured
        - using_fallback
        - counts
        - latest_ingestion_run
        - latest_ingestion_run_health
        - latest_source_mix
        - recent_provider_issues
        - runtime_config
      properties:
        serving_mode:
          type: string
          enum:
            - supabase
            - bundled_fallback
            - local_files
        supabase_configured:
          type: boolean
        using_fallback:
          type: boolean
        fallback_reason:
          type:
            - string
            - "null"
        counts:
          type: object
          required:
            - daily_rankings
            - ingestion_runs
            - macro_observations
            - macro_snapshots
            - news_mentions
            - signals
            - source_records
          properties:
            daily_rankings:
              type: integer
            ingestion_runs:
              type: integer
            macro_observations:
              type: integer
            macro_snapshots:
              type: integer
            news_mentions:
              type: integer
            signals:
              type: integer
            source_records:
              type: integer
        latest_ingestion_run:
          anyOf:
            - $ref: "#/components/schemas/IngestionRun"
            - type: "null"
        latest_ingestion_run_health:
          anyOf:
            - $ref: "#/components/schemas/IngestionRunHealth"
            - type: "null"
        latest_source_mix:
          anyOf:
            - $ref: "#/components/schemas/IngestionSourceMix"
            - type: "null"
        recent_provider_issues:
          type: array
          items:
            $ref: "#/components/schemas/ProviderIssue"
        runtime_config:
          $ref: "#/components/schemas/RuntimeConfigStatus"
    IngestionRun:
      type: object
      properties:
        id:
          type: string
          format: uuid
        provider_name:
          type:
            - string
            - "null"
        job_name:
          type: string
        status:
          type: string
          enum:
            - running
            - completed
            - failed
        started_at:
          type: string
          format: date-time
        completed_at:
          type:
            - string
            - "null"
          format: date-time
        records_seen:
          type: integer
        records_inserted:
          type: integer
        records_updated:
          type: integer
        error_message:
          type:
            - string
            - "null"
        metadata:
          type: object
          additionalProperties: true
    IngestionRunHealth:
      type: object
      required:
        - effective_status
        - status
        - is_stale
        - age_minutes
        - running_age_minutes
        - running_threshold_minutes
        - message
      properties:
        effective_status:
          type: string
          enum:
            - running
            - completed
            - failed
            - stale
            - unknown
        status:
          type: string
          enum:
            - ok
            - warning
            - critical
        is_stale:
          type: boolean
        age_minutes:
          type:
            - integer
            - "null"
        running_age_minutes:
          type:
            - integer
            - "null"
        running_threshold_minutes:
          type: integer
        message:
          type: string
    IngestionSourceMix:
      type: object
      required:
        - source_counts
        - source_names
        - mock_source_count
        - fallback_count
        - provider_skip_count
      properties:
        source_counts:
          type: object
          description: Count of latest-run source records by normalized provider/source
            name.
          additionalProperties:
            type: integer
        source_names:
          type: array
          items:
            type: string
        mock_source_count:
          type: integer
          description: Number of latest-run source records that came from mock providers.
        fallback_count:
          type: integer
          description: Number of provider fallbacks recorded on the latest ingestion run.
        provider_skip_count:
          type: integer
          description: Number of optional provider endpoints skipped after known provider
            plan/access limits.
    ProviderStatus:
      type: object
      required:
        - category
        - provider
        - configured
        - using_mock
        - selected_provider
        - missing_config
        - capabilities
      properties:
        category:
          type: string
          enum:
            - regulatory
            - market
            - political
            - news
            - macro
        provider:
          type: string
        configured:
          type: boolean
        using_mock:
          type: boolean
        selected_provider:
          type: string
        missing_config:
          type: array
          description: Missing environment variable names only; never secret values.
          items:
            type: string
        capabilities:
          type: array
          items:
            type: string
    ProviderIssue:
      type: object
      required:
        - label
        - provider
        - issue_type
        - severity
        - message
        - recommended_action
        - job_name
        - status
        - occurred_at
      properties:
        label:
          type: string
        provider:
          type: string
        issue_type:
          type: string
          enum:
            - authentication
            - configuration
            - rate_limit
            - subscription
            - provider_unavailable
            - unknown
        severity:
          type: string
          enum:
            - info
            - warning
            - action_required
        message:
          type: string
          description: Sanitized provider or ingestion failure detail. Secret-like query
            parameters are redacted.
        recommended_action:
          type: string
        job_name:
          type: string
        status:
          type: string
        occurred_at:
          type: string
          format: date-time
    RuntimeConfigStatus:
      type: object
      required:
        - environment
        - is_production
        - data_mode
        - mock_data_enabled
        - mock_fallback_enabled
        - production_mock_data_blocked
        - invalid_mock_flags
        - dev_tools_enabled
        - secret_status
        - cron
        - gdelt
        - sec
        - safe_config_hints
      description: Production-safe runtime configuration state. Secret values are
        never returned.
      properties:
        environment:
          type: string
        is_production:
          type: boolean
        data_mode:
          type: string
          enum:
            - live
            - mock
        mock_data_enabled:
          type: boolean
        mock_fallback_enabled:
          type: boolean
        production_mock_data_blocked:
          type: boolean
        invalid_mock_flags:
          type: array
          items:
            type: string
        dev_tools_enabled:
          type: boolean
        secret_status:
          type: object
          required:
            - cron_secret_configured
            - dashboard_api_key_configured
            - supabase_service_key_configured
          properties:
            cron_secret_configured:
              type: boolean
            dashboard_api_key_configured:
              type: boolean
            supabase_service_key_configured:
              type: boolean
        cron:
          type: object
          required:
            - configured
            - jobs
          properties:
            configured:
              type: boolean
            jobs:
              type: array
              items:
                type: object
                required:
                  - path
                  - schedule
                  - job
                  - ticker
                  - limit
                properties:
                  path:
                    type: string
                  schedule:
                    type: string
                  job:
                    type: string
                  ticker:
                    type:
                      - string
                      - "null"
                  limit:
                    type:
                      - integer
                      - "null"
        gdelt:
          type: object
          required:
            - base_url_host
            - request_delay_ms
            - max_retries
            - retry_delay_ms
            - news_burst_limit
            - cooldown_minutes
            - request_limit_posture
          properties:
            base_url_host:
              type: string
            request_delay_ms:
              $ref: "#/components/schemas/SafeNumberConfigValue"
            max_retries:
              $ref: "#/components/schemas/SafeNumberConfigValue"
            retry_delay_ms:
              $ref: "#/components/schemas/SafeNumberConfigValue"
            news_burst_limit:
              $ref: "#/components/schemas/SafeNumberConfigValue"
            cooldown_minutes:
              $ref: "#/components/schemas/SafeNumberConfigValue"
            request_limit_posture:
              type: string
              enum:
                - gentle
                - moderate
                - aggressive
        sec:
          type: object
          required:
            - request_delay_ms
            - max_retries
            - retry_delay_ms
            - cooldown_minutes
            - request_limit_posture
          properties:
            request_delay_ms:
              $ref: "#/components/schemas/SafeNumberConfigValue"
            max_retries:
              $ref: "#/components/schemas/SafeNumberConfigValue"
            retry_delay_ms:
              $ref: "#/components/schemas/SafeNumberConfigValue"
            cooldown_minutes:
              $ref: "#/components/schemas/SafeNumberConfigValue"
            request_limit_posture:
              type: string
              enum:
                - gentle
                - moderate
                - aggressive
        safe_config_hints:
          type: array
          items:
            type: object
            required:
              - name
              - configured
              - value
              - description
            properties:
              name:
                type: string
              configured:
                type: boolean
              value:
                type: string
              description:
                type: string
    SafeNumberConfigValue:
      type: object
      required:
        - value
        - source
      properties:
        value:
          type: number
        source:
          type: string
          enum:
            - environment
            - default
            - vercel_json
    UsageLog:
      type: object
      properties:
        request_id:
          type:
            - string
            - "null"
          format: uuid
        path:
          type: string
        method:
          type: string
        status_code:
          type: integer
        duration_ms:
          type: integer
        plan_key:
          type:
            - string
            - "null"
        occurred_at:
          type: string
          format: date-time
    WatchlistTicker:
      type: object
      required:
        - id
        - watchlist_id
        - organization_id
        - ticker
        - added_at
        - metadata
      properties:
        id:
          type: string
        watchlist_id:
          type: string
        organization_id:
          type: string
        ticker:
          type: string
        added_at:
          type: string
          format: date-time
        metadata:
          type: object
          additionalProperties: true
    Watchlist:
      type: object
      required:
        - id
        - organization_id
        - name
        - description
        - created_at
        - updated_at
        - tickers
        - metadata
      properties:
        id:
          type: string
        organization_id:
          type: string
        name:
          type: string
        description:
          type:
            - string
            - "null"
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        tickers:
          type: array
          items:
            $ref: "#/components/schemas/WatchlistTicker"
        metadata:
          type: object
          additionalProperties: true
    WatchlistCreateRequest:
      type: object
      required:
        - name
      properties:
        name:
          type: string
          minLength: 2
          maxLength: 120
          example: Core research
        description:
          type: string
          maxLength: 500
          example: High-priority tickers for daily signal review.
    WatchlistTickerRequest:
      type: object
      required:
        - ticker
      properties:
        ticker:
          type: string
          pattern: ^[A-Za-z][A-Za-z0-9.-]{0,9}$
          example: MSFT
    WebhookSignalPayload:
      type: object
      description: Signed webhook payload emitted for configured active webhook
        endpoints when a generated signal matches an organization watchlist.
        Requests include `x-signalforge-signature`, `x-signalforge-timestamp`,
        `x-signalforge-event`, and `x-signalforge-delivery` headers.
      required:
        - id
        - event
        - organization_id
        - watchlist_id
        - ticker
        - signal
        - evidence_refs
        - score_explanation
        - timestamp
        - signature
      properties:
        id:
          type: string
          example: wh_9f6b79a7-1c5c-4cd2-8778-9d67bbcb2b47
        event:
          type: string
          enum:
            - signal.created
            - signal.high_confidence
            - evidence.created
            - company_brief.updated
            - watchlist.signal.created
          example: watchlist.signal.created
        organization_id:
          type: string
        watchlist_id:
          type:
            - string
            - "null"
        ticker:
          type:
            - string
            - "null"
        signal:
          anyOf:
            - $ref: "#/components/schemas/Signal"
            - type: "null"
        evidence_refs:
          type: array
          items:
            type: string
        score_explanation:
          type:
            - string
            - "null"
        timestamp:
          type: string
          format: date-time
        signature:
          type: object
          required:
            - algorithm
            - header
            - timestamp_header
          properties:
            algorithm:
              type: string
              const: HMAC-SHA256
            header:
              type: string
              const: x-signalforge-signature
            timestamp_header:
              type: string
              const: x-signalforge-timestamp
    LookupNotFoundEnvelope:
      type: object
      required:
        - data
        - meta
      properties:
        data:
          type: object
          required:
            - id
            - message
          properties:
            id:
              type: string
            message:
              type: string
        meta:
          $ref: "#/components/schemas/Meta"
    ProviderStatusEnvelope:
      type: object
      required:
        - data
        - meta
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/ProviderStatus"
        meta:
          $ref: "#/components/schemas/Meta"
    BacktestCapabilities:
      type: object
      required:
        - status
        - can_submit
        - message
        - supported_horizons_days
        - outcome_fields
      properties:
        status:
          type: string
          enum:
            - stubbed
        can_submit:
          type: boolean
          const: false
        message:
          type: string
        supported_horizons_days:
          type: array
          items:
            type: integer
        outcome_fields:
          type: array
          items:
            type: string
    BacktestRun:
      type: object
      description: Reserved beta object for future historical backtest runs. The
        current beta returns an empty array.
      additionalProperties: true
    BacktestRunsEnvelope:
      type: object
      required:
        - data
        - meta
      properties:
        data:
          type: object
          required:
            - capabilities
            - runs
          properties:
            capabilities:
              $ref: "#/components/schemas/BacktestCapabilities"
            runs:
              type: array
              items:
                $ref: "#/components/schemas/BacktestRun"
        meta:
          $ref: "#/components/schemas/Meta"
    BacktestSignalTypeSummary:
      type: object
      required:
        - signal_type
        - description
        - sample_size
        - win_rate
        - average_return
        - status
      properties:
        signal_type:
          $ref: "#/components/schemas/SignalType"
        description:
          type: string
        sample_size:
          type: integer
        win_rate:
          type:
            - number
            - "null"
        average_return:
          type:
            - number
            - "null"
        status:
          type: string
          enum:
            - not_enough_data
            - stubbed
    BacktestSignalTypeSummaryEnvelope:
      type: object
      required:
        - data
        - meta
      properties:
        data:
          type: array
          items:
            $ref: "#/components/schemas/BacktestSignalTypeSummary"
        meta:
          $ref: "#/components/schemas/Meta"
  examples:
    WatchlistsResponse:
      summary: Organization-scoped watchlists
      value:
        data:
          - id: wl_core_research
            organization_id: org_123
            name: Core research
            description: High-priority tickers for daily signal review.
            created_at: 2026-06-26T20:05:15.000Z
            updated_at: 2026-06-26T20:05:15.000Z
            tickers:
              - id: wlt_msft
                watchlist_id: wl_core_research
                organization_id: org_123
                ticker: MSFT
                added_at: 2026-06-26T20:05:15.000Z
                metadata: {}
            metadata: {}
        meta:
          count: 1
          api_version: v1
          request_id: 7e2f27ef-7848-47a4-8dd3-c6f5bcab0035
          disclaimer: Market intelligence for informational purposes only; not financial
            advice.
    SignalWithEvidence:
      summary: Current signal response with explanation and evidence pointers
      description: Implemented shape from `GET /api/v1/signals`. Evidence is embedded
        as components, reason codes, input hash, source names, and source event
        IDs.
      value:
        data:
          - id: sig_429498d6dba2ffc6
            ticker: AMD
            signal_type: earnings_momentum
            score: 80
            confidence: 0.4
            reason: AMD reports FY2026 Q2 earnings in 6 days; fundamental momentum inputs
              are pending.
            event_date: 2026-07-01
            generated_at: 2026-06-25T16:35:16.302Z
            effective_at: 2026-06-25T23:59:59.999Z
            as_of: 2026-06-25
            scoring_version: 2026-06-20.v1
            input_hash: 9b0502e5ef6ffe3f
            coverage: 0.1
            score_components:
              eps_surprise: 0
              revenue_surprise: 0
              estimate_revisions: 0
              guidance: 0
              event_recency: 80
            score_explanation: Score 80 is a bounded 0-100 weighted blend led by event
              recency, with 10% input coverage. 4 components lacked enough data
              and contributed 0.
            components:
              - key: eps_surprise
                label: EPS surprise
                raw_value: null
                normalized_score: 0
                weight: 0.3
                contribution: 0
                reason_code: EPS_SURPRISE_UNAVAILABLE
              - key: event_recency
                label: Event recency
                raw_value: 6
                normalized_score: 80
                weight: 0.1
                contribution: 8
                reason_code: EARNINGS_RECENCY
            source_names:
              - mock_market
            source_event_ids:
              - raw_earnings_amd_1
            evidence_ids:
              - ev_bed10e9ca24a8eda
            evidence_status: available
            source_lineage:
              - evidence_id: ev_bed10e9ca24a8eda
                provider: mock_market
                source_type: earnings
                source_url: null
                external_id: raw_earnings_amd_1
                observed_at: 2026-06-25T16:35:16.301Z
                event_date: 2026-07-01
                ticker: AMD
                company_name: null
                summary: AMD earnings event for FY2026 Q2 expected 2026-07-01.
                confidence: 0.55
                source_quality: mock
            metadata:
              fiscal_period: FY2026 Q2
              data_status: scheduled
        meta:
          count: 1
          api_version: v1
          request_id: 16b263fb-0be4-4239-a732-0dfec5b3dd40
          disclaimer: Market intelligence for informational purposes only; not financial
            advice.
    CompanyAgentBriefResponse:
      summary: Structured company brief for agent workflows
      description: Returned by `GET /api/v1/companies/{ticker}/agent-brief`.
      value:
        data:
          ticker: MSFT
          company:
            ticker: MSFT
            company_name: Microsoft Corporation
            company_id: company_msft
            security_id: security_msft
            exchange: NASDAQ
            sector: Technology
            industry: Software Infrastructure
          as_of: 2026-06-26T18:30:00.000Z
          machine_summary:
            ticker: MSFT
            score: 36
            confidence: 0.76
            active_signal_count: 5
            primary_signal_types:
              - price_momentum
              - composite_signal
              - analyst_momentum
            evidence_status: available
            qa_status: passed
            outcome_status: pending_data
          human_summary: MSFT has 5 active derived signal(s), led by price_momentum,
            composite_signal, analyst_momentum.
          active_signals:
            - id: sig_977db43b55ef57e1
              ticker: MSFT
              signal_type: price_momentum
              score: 74
              confidence: 0.76
              reason: MSFT has 3.7% recent price momentum with 100% positive sessions.
              scoring_version: 2026-06-20.v1
              score_explanation: Score 74 is a bounded 0-100 weighted blend led by trend
                consistency, drawdown control, return 20d, with 75% input
                coverage.
              evidence_ids:
                - ev_d4db7f1d420ba224
              evidence_status: available
          why_now:
            - MSFT has 3.7% recent price momentum with 100% positive sessions.
          supporting_evidence:
            - evidence_id: ev_d4db7f1d420ba224
              provider: mock_market
              source_type: price_bar
              source_url: null
              external_id: price_8638f0279b38c5e1
              observed_at: 2026-06-25T16:35:16.301Z
              event_date: 2026-06-25
              ticker: MSFT
              company_name: null
              summary: MSFT price observation on 2026-06-25.
              confidence: 0.55
              source_quality: mock
          contradicting_evidence: []
          risk_flags:
            - Market intelligence is informational only and not investment
              advice.
            - Signals can decay quickly as new filings, prices, earnings, or
              news arrive.
          source_lineage:
            - evidence_id: ev_d4db7f1d420ba224
              provider: mock_market
              source_type: price_bar
              source_url: null
              external_id: price_8638f0279b38c5e1
              observed_at: 2026-06-25T16:35:16.301Z
              event_date: 2026-06-25
              ticker: MSFT
              company_name: null
              summary: MSFT price observation on 2026-06-25.
              confidence: 0.55
              source_quality: mock
          confidence: 0.76
          freshness:
            generated_at: 2026-06-26T18:30:00.000Z
            as_of: 2026-06-26
            latest_signal_at: 2026-06-26T18:18:21.351Z
            latest_event_date: 2026-06-26
            signal_count: 5
            evidence_count: 5
          suggested_agent_tasks:
            - Review source_lineage evidence summaries before using this brief
              downstream.
          qa:
            qa_status: passed
            qa_flags: []
            confidence_notes: Deterministic QA found active signals with normalized
              supporting evidence; no AI review was run.
            reviewed_at: 2026-06-26T18:30:00.000Z
          outcome_status:
            status: pending_data
            measured_signal_count: 0
            pending_signal_count: 5
            summary: Forward outcome measurement is pending until outcome jobs are enabled.
          generated_at: 2026-06-26T18:30:00.000Z
        meta:
          ticker: MSFT
          active_signal_count: 5
          evidence_count: 5
          api_version: v1
          request_id: d8d9dfba-e545-4d1f-a3f3-29dbf4eabddc
          disclaimer: Market intelligence for informational purposes only; not financial
            advice.
    AgentBriefResponse:
      summary: Current ticker thesis response for agent/company brief use cases
      description: Implemented shape from `GET /api/v1/tickers/{ticker}/thesis`; there
        is no separate Agent Briefs API in the current beta.
      value:
        data:
          ticker: MSFT
          score: 36
          thesis: MSFT is currently supported by 5 derived signal(s), led by price
            momentum, composite signal, analyst momentum. The strongest evidence
            is derived from supporting signals.
          supporting_signals:
            - id: sig_977db43b55ef57e1
              ticker: MSFT
              signal_type: price_momentum
              score: 74
              confidence: 0.76
              reason: MSFT has 3.7% recent price momentum with 100% positive sessions.
              event_date: 2026-06-25
              generated_at: 2026-06-25T16:35:16.303Z
              effective_at: 2026-06-25T23:59:59.999Z
              as_of: 2026-06-25
              scoring_version: 2026-06-20.v1
              input_hash: 24cb5e733feb8036
              coverage: 0.75
              score_components:
                return_20d: 15.67
                relative_strength_60d: 0
                trend_consistency: 26.67
                volume_confirmation: 15.3
                drawdown_control: 16
              score_explanation: Score 74 is a bounded 0-100 weighted blend led by trend
                consistency, drawdown control, return 20d, with 75% input
                coverage. 1 component lacked enough data and contributed 0.
              components:
                - key: return_20d
                  label: Recent return
                  raw_value: 3.749600893997439
                  normalized_score: 47
                  weight: 0.25
                  contribution: 11.75
                  reason_code: PRICE_RETURN
              source_names:
                - mock_market
              source_event_ids:
                - price_8638f0279b38c5e1
              evidence_ids:
                - ev_d4db7f1d420ba224
              evidence_status: available
              source_lineage:
                - evidence_id: ev_d4db7f1d420ba224
                  provider: mock_market
                  source_type: price_bar
                  source_url: null
                  external_id: price_8638f0279b38c5e1
                  observed_at: 2026-06-25T16:35:16.301Z
                  event_date: 2026-06-25
                  ticker: MSFT
                  company_name: null
                  summary: MSFT price observation on 2026-06-25.
                  confidence: 0.55
                  source_quality: mock
              metadata:
                observation_count: 10
                return_pct: 3.75
          supporting_evidence:
            - evidence_id: ev_d4db7f1d420ba224
              provider: mock_market
              source_type: price_bar
              source_url: null
              external_id: price_8638f0279b38c5e1
              observed_at: 2026-06-25T16:35:16.301Z
              event_date: 2026-06-25
              ticker: MSFT
              company_name: null
              summary: MSFT price observation on 2026-06-25.
              confidence: 0.55
              source_quality: mock
          risks:
            - Signals are derived from public event data and can decay quickly.
            - Mock data is for local development only until live providers are
              connected.
            - Backtest metrics are stubbed in this initial build.
          generated_at: 2026-06-26T16:49:26.153Z
        meta:
          api_version: v1
          request_id: d8d9dfba-e545-4d1f-a3f3-29dbf4eabddc
          disclaimer: Market intelligence for informational purposes only; not financial
            advice.
    EvidencePointer:
      summary: Current beta evidence/provenance pointer shape
      description: Signals include source pointers and fetchable normalized evidence IDs.
      value:
        scoring_version: 2026-06-20.v1
        input_hash: 24cb5e733feb8036
        coverage: 0.75
        score_components:
          return_20d: 15.67
          relative_strength_60d: 0
          trend_consistency: 26.67
          volume_confirmation: 15.3
          drawdown_control: 16
        score_explanation: Score 74 is a bounded 0-100 weighted blend led by trend
          consistency, drawdown control, return 20d, with 75% input coverage. 1
          component lacked enough data and contributed 0.
        source_names:
          - mock_market
        source_event_ids:
          - price_8638f0279b38c5e1
        evidence_ids:
          - ev_d4db7f1d420ba224
        evidence_status: available
        source_lineage:
          - evidence_id: ev_d4db7f1d420ba224
            provider: mock_market
            source_type: price_bar
            source_url: null
            external_id: price_8638f0279b38c5e1
            observed_at: 2026-06-25T16:35:16.301Z
            event_date: 2026-06-25
            ticker: MSFT
            company_name: null
            summary: MSFT price observation on 2026-06-25.
            confidence: 0.55
            source_quality: mock
        components:
          - key: return_20d
            reason_code: PRICE_RETURN
            normalized_score: 47
            weight: 0.25
            contribution: 11.75
    NormalizedEvidence:
      summary: Normalized evidence response
      description: Returned by `GET /api/v1/evidence/{id}`. Raw payload fields are
        referenced, not redistributed.
      value:
        data:
          id: ev_bed10e9ca24a8eda
          provider: mock_market
          source_type: earnings
          source_url: null
          external_id: raw_earnings_amd_1
          observed_at: 2026-06-25T16:35:16.301Z
          event_date: 2026-07-01
          ticker: AMD
          company_name: null
          raw_payload_ref:
            source_record_id: source_82bb7dff15edc7ad
            raw_event_id: raw_earnings_amd_1
            content_hash: c1c6a9f7254d9b4e
          summary: AMD earnings event for FY2026 Q2 expected 2026-07-01.
          confidence: 0.55
          source_quality: mock
          metadata:
            event_type: earnings_event
            source_record_type: earnings
            is_mock: true
        meta:
          api_version: v1
          request_id: 9e89ce30-1ecf-4da6-867c-2f7475636c62
          disclaimer: Market intelligence for informational purposes only; not financial
            advice.
    SignedWatchlistWebhookPayload:
      summary: Signed watchlist signal webhook payload
      description: Delivered to active organization webhook endpoints when a generated
        signal matches a watchlist ticker. Verify `x-signalforge-signature` by
        computing HMAC-SHA256 over `{timestamp}.{rawBody}` with the shared
        webhook signing secret.
      value:
        headers:
          x-signalforge-delivery: wh_9f6b79a7-1c5c-4cd2-8778-9d67bbcb2b47
          x-signalforge-event: watchlist.signal.created
          x-signalforge-timestamp: "1782504000"
          x-signalforge-signature: t=1782504000,v1=1f2b...redacted
        body:
          id: wh_9f6b79a7-1c5c-4cd2-8778-9d67bbcb2b47
          event: watchlist.signal.created
          organization_id: org_123
          watchlist_id: wl_core_research
          ticker: MSFT
          evidence_refs:
            - ev_d4db7f1d420ba224
          score_explanation: Score 74 is a bounded 0-100 weighted blend led by recent
            return, trend consistency, volume confirmation, with 75% input
            coverage.
          timestamp: 2026-06-26T20:00:00.000Z
          signature:
            algorithm: HMAC-SHA256
            header: x-signalforge-signature
            timestamp_header: x-signalforge-timestamp
          signal:
            id: sig_977db43b55ef57e1
            ticker: MSFT
            signal_type: price_momentum
            score: 74
            confidence: 0.76
            reason: MSFT has 3.7% recent price momentum with 100% positive sessions.
            event_date: 2026-06-25
            generated_at: 2026-06-25T16:35:16.303Z
            source_names:
              - mock_market
            source_event_ids:
              - price_8638f0279b38c5e1
    HealthResponse:
      summary: Service liveness response
      value:
        data:
          ok: true
          service: SignalForge API
          version: v1
          checked_at: 2026-06-27T19:30:00.000Z
          environment: production
          data_mode: live
          mock_data_enabled: false
          live_sources_configured: true
          missing_required_config: []
        meta:
          environment: production
          data_mode: live
          mock_data: false
          api_version: v1
          request_id: 7e2f27ef-7848-47a4-8dd3-c6f5bcab0035
          disclaimer: Market intelligence for informational purposes only; not financial
            advice.
    ReadinessResponse:
      summary: Production readiness response
      value:
        data:
          status: ready
          checked_at: 2026-06-27T19:30:00.000Z
          environment: production
          supabase_configured: true
          components:
            - name: api
              status: ok
              message: API is serving requests.
          counts:
            daily_rankings: 25
            ingestion_runs: 14
            macro_snapshots: 1
            news_mentions: 240
            signals: 125
            source_records: 620
          latest_ingestion_run: null
          latest_source_mix: null
          freshness: []
          provider_status: []
          recent_provider_issues: []
          thresholds:
            ingestion_run_minutes: 180
            running_ingestion_minutes: 30
            signals_minutes: 1440
            daily_rankings_minutes: 1440
            macro_snapshot_minutes: 2880
            news_mentions_minutes: 720
        meta:
          api_version: v1
          request_id: 7e2f27ef-7848-47a4-8dd3-c6f5bcab0035
          disclaimer: Market intelligence for informational purposes only; not financial
            advice.
    SignalDetailResponse:
      summary: Signal detail response
      value:
        data:
          id: sig_429498d6dba2ffc6
          ticker: AMD
          signal_type: earnings_momentum
          score: 80
          confidence: 0.4
          reason: AMD reports FY2026 Q2 earnings in 6 days; fundamental momentum inputs
            are pending.
          event_date: 2026-07-01
          generated_at: 2026-06-25T16:35:16.302Z
          effective_at: 2026-06-25T23:59:59.999Z
          as_of: 2026-06-25
          scoring_version: 2026-06-20.v1
          input_hash: 9b0502e5ef6ffe3f
          coverage: 0.1
          score_components:
            eps_surprise: 0
            revenue_surprise: 0
            estimate_revisions: 0
            guidance: 0
            event_recency: 80
          score_explanation: Score 80 is a bounded 0-100 weighted blend led by event
            recency, with 10% input coverage. 4 components lacked enough data
            and contributed 0.
          components:
            - key: eps_surprise
              label: EPS surprise
              raw_value: null
              normalized_score: 0
              weight: 0.3
              contribution: 0
              reason_code: EPS_SURPRISE_UNAVAILABLE
            - key: event_recency
              label: Event recency
              raw_value: 6
              normalized_score: 80
              weight: 0.1
              contribution: 8
              reason_code: EARNINGS_RECENCY
          source_names:
            - mock_market
          source_event_ids:
            - raw_earnings_amd_1
          evidence_ids:
            - ev_bed10e9ca24a8eda
          evidence_status: available
          source_lineage:
            - evidence_id: ev_bed10e9ca24a8eda
              provider: mock_market
              source_type: earnings
              source_url: null
              external_id: raw_earnings_amd_1
              observed_at: 2026-06-25T16:35:16.301Z
              event_date: 2026-07-01
              ticker: AMD
              company_name: null
              summary: AMD earnings event for FY2026 Q2 expected 2026-07-01.
              confidence: 0.55
              source_quality: mock
          metadata:
            fiscal_period: FY2026 Q2
            data_status: scheduled
        meta:
          api_version: v1
          request_id: 7e2f27ef-7848-47a4-8dd3-c6f5bcab0035
          disclaimer: Market intelligence for informational purposes only; not financial
            advice.
    SignalOutcomesResponse:
      summary: Forward outcome windows
      value:
        data:
          signal_id: sig_429498d6dba2ffc6
          ticker: AMD
          status: pending
          supported_horizons_days:
            - 1
            - 5
            - 20
            - 60
          outcomes:
            - id: out_1d
              signal_id: sig_429498d6dba2ffc6
              ticker: AMD
              measured_at: null
              horizon_days: 1
              measurement_status: pending
              baseline_price: null
              outcome_price: null
              return_pct: null
              benchmark_ticker: SPY
              benchmark_return_pct: null
              excess_return_pct: null
              direction_correct: null
              error_message: null
              metadata: {}
        meta:
          count: 4
          measured_count: 0
          pending_count: 4
          failed_count: 0
          api_version: v1
          request_id: 7e2f27ef-7848-47a4-8dd3-c6f5bcab0035
          disclaimer: Market intelligence for informational purposes only; not financial
            advice.
    RankingsResponse:
      summary: Daily ranking response
      value:
        data:
          - id: rank_20260627_msft
            ranking_date: 2026-06-27
            rank: 1
            ticker: MSFT
            score: 82
            signal_count: 5
            primary_signal_types:
              - price_momentum
              - analyst_momentum
            reason_summary: MSFT leads current rankings on price momentum and analyst
              revisions.
            generated_at: 2026-06-27T12:00:00.000Z
        meta:
          count: 1
          generated_at: 2026-06-27T12:00:00.000Z
          api_version: v1
          request_id: 7e2f27ef-7848-47a4-8dd3-c6f5bcab0035
          disclaimer: Market intelligence for informational purposes only; not financial
            advice.
    ProviderStatusResponse:
      summary: Sanitized provider status response
      value:
        data:
          - category: market
            provider: mock_market
            configured: false
            using_mock: true
            selected_provider: fmp
            missing_config:
              - FMP_API_KEY
            capabilities:
              - profiles
              - prices
              - earnings
              - analysts
              - financials
        meta:
          api_version: v1
          request_id: 7e2f27ef-7848-47a4-8dd3-c6f5bcab0035
          disclaimer: Market intelligence for informational purposes only; not financial
            advice.
    IngestionStatusResponse:
      summary: Ingestion status response
      value:
        data:
          serving_mode: bundled_fallback
          supabase_configured: false
          using_fallback: true
          fallback_reason: local_development_files
          counts:
            daily_rankings: 25
            ingestion_runs: 4
            macro_observations: 12
            macro_snapshots: 1
            news_mentions: 90
            signals: 120
            source_records: 400
          latest_ingestion_run: null
          latest_source_mix: null
          recent_provider_issues: []
          runtime_config:
            environment: development
            mock_fallback_enabled: true
            dev_tools_enabled: false
            secret_status:
              cron_secret_configured: false
              dashboard_api_key_configured: false
              supabase_service_key_configured: false
            cron:
              configured: false
              jobs: []
            gdelt:
              base_url_host: api.gdeltproject.org/api/v2
              request_delay_ms:
                value: 10000
                source: default
              max_retries:
                value: 0
                source: default
              retry_delay_ms:
                value: 5500
                source: default
              news_burst_limit:
                value: 25
                source: default
              cooldown_minutes:
                value: 360
                source: default
              request_limit_posture: gentle
            sec:
              request_delay_ms:
                value: 100
                source: default
              max_retries:
                value: 2
                source: default
              retry_delay_ms:
                value: 500
                source: default
              cooldown_minutes:
                value: 10
                source: default
              request_limit_posture: gentle
            safe_config_hints: []
        meta:
          api_version: v1
          request_id: 7e2f27ef-7848-47a4-8dd3-c6f5bcab0035
          disclaimer: Market intelligence for informational purposes only; not financial
            advice.
    MacroContextResponse:
      summary: Macro context response
      value:
        data:
          snapshot_date: 2026-06-27
          fed_funds_rate: 5.25
          treasury_2y: 4.7
          treasury_10y: 4.25
          yield_curve_10y_2y: -0.45
          inflation_rate: 3.1
          unemployment_rate: 4
          regime_label: restrictive_policy
          summary: Rates remain restrictive while labor data is stable.
          source_names:
            - mock_macro
        meta:
          api_version: v1
          request_id: 7e2f27ef-7848-47a4-8dd3-c6f5bcab0035
          disclaimer: Market intelligence for informational purposes only; not financial
            advice.
    NewsBurstsResponse:
      summary: News-burst signal response
      value:
        data:
          - id: sig_news_burst_msft
            ticker: AMD
            signal_type: news_burst
            score: 80
            confidence: 0.4
            reason: MSFT coverage is elevated relative to its recent baseline.
            event_date: 2026-07-01
            generated_at: 2026-06-25T16:35:16.302Z
            effective_at: 2026-06-25T23:59:59.999Z
            as_of: 2026-06-25
            scoring_version: 2026-06-20.v1
            input_hash: 9b0502e5ef6ffe3f
            coverage: 0.1
            score_components:
              eps_surprise: 0
              revenue_surprise: 0
              estimate_revisions: 0
              guidance: 0
              event_recency: 80
            score_explanation: Score 80 is a bounded 0-100 weighted blend led by event
              recency, with 10% input coverage. 4 components lacked enough data
              and contributed 0.
            components:
              - key: eps_surprise
                label: EPS surprise
                raw_value: null
                normalized_score: 0
                weight: 0.3
                contribution: 0
                reason_code: EPS_SURPRISE_UNAVAILABLE
              - key: event_recency
                label: Event recency
                raw_value: 6
                normalized_score: 80
                weight: 0.1
                contribution: 8
                reason_code: EARNINGS_RECENCY
            source_names:
              - mock_market
            source_event_ids:
              - raw_earnings_amd_1
            evidence_ids:
              - ev_bed10e9ca24a8eda
            evidence_status: available
            source_lineage:
              - evidence_id: ev_bed10e9ca24a8eda
                provider: mock_market
                source_type: earnings
                source_url: null
                external_id: raw_earnings_amd_1
                observed_at: 2026-06-25T16:35:16.301Z
                event_date: 2026-07-01
                ticker: AMD
                company_name: null
                summary: AMD earnings event for FY2026 Q2 expected 2026-07-01.
                confidence: 0.55
                source_quality: mock
            metadata:
              fiscal_period: FY2026 Q2
              data_status: scheduled
        meta:
          count: 1
          api_version: v1
          request_id: 7e2f27ef-7848-47a4-8dd3-c6f5bcab0035
          disclaimer: Market intelligence for informational purposes only; not financial
            advice.
    BacktestCapabilitiesResponse:
      summary: Backtest placeholder capabilities
      value:
        data:
          capabilities:
            status: stubbed
            can_submit: false
            message: Forward outcome windows are recorded for signals when price bars exist.
              Full historical backtest execution is reserved for beta.
            supported_horizons_days:
              - 1
              - 5
              - 20
              - 60
            outcome_fields:
              - return_pct
              - benchmark_return_pct
              - excess_return_pct
              - direction_correct
          runs: []
        meta:
          count: 0
          api_version: v1
          request_id: 7e2f27ef-7848-47a4-8dd3-c6f5bcab0035
          disclaimer: Market intelligence for informational purposes only; not financial
            advice.
    BacktestSignalTypesResponse:
      summary: Backtest signal type catalog
      value:
        data:
          - signal_type: price_momentum
            description: Price momentum scored from recent return, trend consistency, volume
              confirmation, and drawdown control.
            sample_size: 0
            win_rate: null
            average_return: null
            status: not_enough_data
        meta:
          count: 1
          api_version: v1
          request_id: 7e2f27ef-7848-47a4-8dd3-c6f5bcab0035
          disclaimer: Market intelligence for informational purposes only; not financial
            advice.
    UsageResponse:
      summary: Usage log response
      value:
        data:
          - request_id: 16b263fb-0be4-4239-a732-0dfec5b3dd40
            path: /api/v1/tickers/MSFT/thesis
            method: GET
            status_code: 200
            duration_ms: 42
            plan_key: beta
            occurred_at: 2026-06-27T19:30:00.000Z
        meta:
          count: 1
          api_version: v1
          request_id: 7e2f27ef-7848-47a4-8dd3-c6f5bcab0035
          disclaimer: Market intelligence for informational purposes only; not financial
            advice.
