openapi: 3.1.0
info:
  title: GreenCIO Data API
  version: 2.1.0
  license:
    name: Proprietary
    identifier: LicenseRef-Proprietary
  description: |
    Customer-facing contract for the external GreenCIO Data API.

    This specification describes the `/v1` API implemented by
    `app/api/v1/*` adapters. Internal `/api/*` application routes are not part
    of this contract unless they are wrapped by a v1 adapter and covered by
    the OpenAPI parity test.

    All responses are JSON. Timestamps are ISO-8601 UTC. Collections use
    cursor pagination and a `{ data, meta }` envelope. Error responses use
    `{ error: { code, message, field, request_id } }`.

    News-derived intelligence returns metadata and GreenCIO-authored summaries
    only. It never returns full third-party article bodies.

    Route presence is not a production-readiness claim. Seed/static products
    expose source mode, freshness, evidence, and source-right metadata. Uncleared
    fab and capacity-seed fixtures are quarantined from live keys and
    customer-serving runtimes.

    CORS is intentionally not enabled for v1. Treat this as a server-to-server
    API; browser clients must call through their own backend so API keys never
    live in frontend code.
servers:
  - url: https://www.greencio.com/api/v1
    description: Current Vercel path adapter; subdomain move should be DNS and Vercel config only.
  - url: https://api.greencio.com/v1
    description: Production target
  - url: https://api-staging.greencio.com/v1
    description: Staging target

security:
  - bearerAuth: []

tags:
  - name: CapacitySignals
    description: Quarantined seed references plus gated persisted source intelligence.
  - name: Facilities
    description: Quarantined facility CSV fixture pending canonical sources, source date, and rights review.
  - name: ComputeIndex
    description: GreenCIO Compute Index settlements, history, basis, and methodology.
  - name: InferenceEconomics
    description: Unit economics, token prices, and LCOE calculations.
  - name: AiInfrastructureReview
    description: Repository-derived AI infrastructure facts with optional submitted economics and power assumptions.
  - name: Fabs
    description: Quarantined, unverified semiconductor UI/API fixture pending provenance and rights review.
  - name: PowerForecast
    description: Deferred v1.1 power forecast curves and scenarios.
  - name: CarbonTrail
    description: Deferred v1.2 compute-load carbon attribution.
  - name: Webhooks
    description: Deferred v1.1 signed event delivery.

paths:
  /capacity/signals:
    get:
      operationId: listCapacitySignals
      summary: List source-linked capacity-signal seed references in an explicit demo/test runtime
      tags: [CapacitySignals]
      x-greencio-tier: insights
      x-greencio-release: preview-demo
      x-greencio-status: quarantined-from-customer-serving-runtimes
      x-greencio-current-adapter: app/api/v1/capacity/signals/route.ts
      parameters:
        - $ref: "#/components/parameters/Since"
        - $ref: "#/components/parameters/Cursor"
        - $ref: "#/components/parameters/Limit"
        - in: query
          name: type
          schema:
            type: string
            enum: [permit, grid_filing, financing, interconnection, policy, supply_chain, market]
        - in: query
          name: impact
          schema:
            type: string
            enum: [high, medium, low]
        - in: query
          name: entity
          schema:
            type: string
      responses:
        "200":
          description: Cursor page of capacity signals
          headers: { X-Request-Id: { $ref: "#/components/headers/RequestId" }, X-RateLimit-Limit: { $ref: "#/components/headers/RateLimitLimit" }, X-RateLimit-Remaining: { $ref: "#/components/headers/RateLimitRemaining" }, X-RateLimit-Reset: { $ref: "#/components/headers/RateLimitReset" }, X-RateLimit-Quota-Limit: { $ref: "#/components/headers/RateLimitQuotaLimit" }, X-RateLimit-Quota-Remaining: { $ref: "#/components/headers/RateLimitQuotaRemaining" }, X-RateLimit-Quota-Reset: { $ref: "#/components/headers/RateLimitQuotaReset" } }
          content:
            application/json:
              schema: { $ref: "#/components/schemas/SignalCollection" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "503": { $ref: "#/components/responses/ServiceUnavailable" }

  /capacity/signals/{id}:
    get:
      operationId: getCapacitySignal
      summary: Get one source-linked capacity-signal seed reference
      tags: [CapacitySignals]
      x-greencio-tier: insights
      x-greencio-release: preview-demo
      x-greencio-status: quarantined-from-customer-serving-runtimes
      parameters:
        - $ref: "#/components/parameters/Id"
      responses:
        "200":
          description: Capacity signal
          content:
            application/json:
              schema: { $ref: "#/components/schemas/SignalObject" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
        "503": { $ref: "#/components/responses/ServiceUnavailable" }

  /capacity/intelligence:
    get:
      operationId: listCapacityIntelligence
      summary: List fresh persisted news-derived summaries with canonical sources and cleared rights
      tags: [CapacitySignals]
      x-greencio-tier: insights
      x-greencio-release: v1.0
      x-greencio-status: gated
      x-greencio-current-adapter: app/api/v1/capacity/intelligence/route.ts
      parameters:
        - $ref: "#/components/parameters/Since"
        - $ref: "#/components/parameters/Cursor"
        - $ref: "#/components/parameters/Limit"
        - in: query
          name: tags
          schema:
            type: string
            description: Comma-separated tag list.
        - in: query
          name: impact
          schema:
            type: string
            enum: [high, medium, low]
      responses:
        "200":
          description: Cursor page of intelligence items
          content:
            application/json:
              schema: { $ref: "#/components/schemas/IntelligenceCollection" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "503": { $ref: "#/components/responses/ServiceUnavailable" }

  /capacity/intelligence/{id}:
    get:
      operationId: getCapacityIntelligence
      summary: Get one fresh, source-traceable persisted intelligence item
      tags: [CapacitySignals]
      x-greencio-tier: insights
      x-greencio-release: v1.0
      x-greencio-status: gated
      parameters:
        - $ref: "#/components/parameters/Id"
      responses:
        "200":
          description: Intelligence item
          content:
            application/json:
              schema: { $ref: "#/components/schemas/IntelligenceObject" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "503": { $ref: "#/components/responses/ServiceUnavailable" }

  /facilities:
    get:
      operationId: listFacilities
      summary: List an unverified facility CSV fixture in explicit demo/test runtimes only
      tags: [Facilities]
      x-greencio-tier: insights
      x-greencio-release: preview-demo
      x-greencio-status: quarantined-from-customer-serving-runtimes
      parameters:
        - $ref: "#/components/parameters/Cursor"
        - $ref: "#/components/parameters/Limit"
        - in: query
          name: country
          schema: { type: string }
        - in: query
          name: operator
          schema: { type: string }
        - in: query
          name: min_capacity_mw
          schema: { type: number, minimum: 0 }
      responses:
        "200":
          description: Cursor page of facilities
          content:
            application/json:
              schema: { $ref: "#/components/schemas/FacilityCollection" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "503": { $ref: "#/components/responses/ServiceUnavailable" }

  /facilities/{id}:
    get:
      operationId: getFacility
      summary: Get one unverified facility CSV row in explicit demo/test runtimes only
      tags: [Facilities]
      x-greencio-tier: insights
      x-greencio-release: preview-demo
      x-greencio-status: quarantined-from-customer-serving-runtimes
      parameters:
        - $ref: "#/components/parameters/Id"
      responses:
        "200":
          description: Facility
          content:
            application/json:
              schema: { $ref: "#/components/schemas/FacilityObject" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "503": { $ref: "#/components/responses/ServiceUnavailable" }

  /index/compute:
    get:
      operationId: listComputeSettlements
      summary: List current GCI settlements
      tags: [ComputeIndex]
      x-greencio-tier: pro
      x-greencio-release: v1.0
      x-greencio-current-adapter: app/api/v1/index/compute/route.ts
      parameters:
        - in: query
          name: sku
          schema: { type: string }
        - in: query
          name: region
          schema: { type: string }
        - in: query
          name: tenor
          schema: { type: string }
        - $ref: "#/components/parameters/IncludeProvisional"
        - $ref: "#/components/parameters/Cursor"
        - $ref: "#/components/parameters/Limit"
      responses:
        "200":
          description: Current compute-index settlements
          content:
            application/json:
              schema: { $ref: "#/components/schemas/ComputeSettlementCollection" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "503": { $ref: "#/components/responses/ServiceUnavailable" }

  /index/compute/{index_name}:
    get:
      operationId: getComputeSettlement
      summary: Get one GCI settlement
      tags: [ComputeIndex]
      x-greencio-tier: pro
      x-greencio-release: v1.0
      x-greencio-current-adapter: app/api/v1/index/compute/[index_name]/route.ts
      parameters:
        - $ref: "#/components/parameters/IndexName"
        - $ref: "#/components/parameters/IncludeProvisional"
      responses:
        "200":
          description: Compute-index settlement detail
          content:
            application/json:
              schema: { $ref: "#/components/schemas/ComputeSettlementObject" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "503": { $ref: "#/components/responses/ServiceUnavailable" }
        "404": { $ref: "#/components/responses/NotFound" }

  /index/compute/history:
    get:
      operationId: listComputeSettlementHistory
      summary: List historical GCI settlements
      tags: [ComputeIndex]
      x-greencio-tier: pro
      x-greencio-release: v1.0
      x-greencio-current-adapter: app/api/v1/index/compute/history/route.ts
      parameters:
        - in: query
          name: index_name
          schema: { type: string }
        - $ref: "#/components/parameters/IncludeProvisional"
        - $ref: "#/components/parameters/Since"
        - $ref: "#/components/parameters/Cursor"
        - $ref: "#/components/parameters/Limit"
      responses:
        "200":
          description: Historical settlement page
          content:
            application/json:
              schema: { $ref: "#/components/schemas/ComputeSettlementCollection" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "503": { $ref: "#/components/responses/ServiceUnavailable" }

  /index/compute/basis:
    get:
      operationId: listComputeBasisSpreads
      summary: List GCI basis spreads
      tags: [ComputeIndex]
      x-greencio-tier: pro
      x-greencio-release: v1.0
      x-greencio-current-adapter: app/api/v1/index/compute/basis/route.ts
      parameters:
        - in: query
          name: spread_index_name
          schema: { type: string }
        - $ref: "#/components/parameters/IncludeProvisional"
        - $ref: "#/components/parameters/Since"
        - $ref: "#/components/parameters/Cursor"
        - $ref: "#/components/parameters/Limit"
      responses:
        "200":
          description: Basis spread page
          content:
            application/json:
              schema: { $ref: "#/components/schemas/BasisSpreadCollection" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "503": { $ref: "#/components/responses/ServiceUnavailable" }

  /index/compute/methodology:
    get:
      operationId: getComputeMethodology
      summary: Get active GCI methodology metadata
      tags: [ComputeIndex]
      x-greencio-tier: pro
      x-greencio-release: v1.0
      x-greencio-current-adapter: app/api/v1/index/compute/methodology/route.ts
      responses:
        "200":
          description: Methodology metadata
          content:
            application/json:
              schema: { $ref: "#/components/schemas/MethodologyObject" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "503": { $ref: "#/components/responses/ServiceUnavailable" }

  /ai-infra/review:
    post:
      operationId: reviewAiInfrastructure
      summary: Review normalized AI infrastructure facts and submitted diligence assumptions
      description: |
        Accepts allowlisted facts from a local repository scan. The route does not
        accept source text or secret values. Economics and power outputs run only
        when the caller submits the required assumption blocks. Repository findings
        are pattern matches and are not proof of deployed infrastructure.
      tags: [AiInfrastructureReview]
      x-greencio-tier: pro
      x-greencio-release: v1.0
      x-greencio-current-adapter: app/api/v1/ai-infra/review/route.ts
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/AiInfraReviewInput" }
      responses:
        "200":
          description: Normalized review with evidence gaps and provenance
          content:
            application/json:
              schema: { $ref: "#/components/schemas/AiInfraReviewObject" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "408": { $ref: "#/components/responses/RequestTimeout" }
        "409": { $ref: "#/components/responses/IdempotencyConflict" }
        "413": { $ref: "#/components/responses/PayloadTooLarge" }
        "422": { $ref: "#/components/responses/ValidationFailed" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "503": { $ref: "#/components/responses/ServiceUnavailable" }

  /inference/unit-economics:
    post:
      operationId: computeUnitEconomics
      summary: Compute inference unit economics
      tags: [InferenceEconomics]
      x-greencio-tier: pro
      x-greencio-release: v1.0
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/UnitEconomicsInput" }
      responses:
        "200":
          description: Unit-economics result
          content:
            application/json:
              schema: { $ref: "#/components/schemas/UnitEconomicsObject" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "409": { $ref: "#/components/responses/IdempotencyConflict" }
        "408": { $ref: "#/components/responses/RequestTimeout" }
        "413": { $ref: "#/components/responses/PayloadTooLarge" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "503": { $ref: "#/components/responses/ServiceUnavailable" }
        "422": { $ref: "#/components/responses/ValidationFailed" }

  /inference/token-prices:
    get:
      operationId: listTokenPrices
      summary: List the latest available token-price reference snapshot by provider and model class
      tags: [InferenceEconomics]
      x-greencio-tier: pro
      x-greencio-release: v1.0
      responses:
        "200":
          description: Token-price snapshot with observation, freshness, methodology, and source-right metadata
          content:
            application/json:
              schema: { $ref: "#/components/schemas/TokenPriceCollection" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "503": { $ref: "#/components/responses/ServiceUnavailable" }

  /inference/token-prices/history:
    get:
      operationId: listTokenPriceHistory
      summary: Filter the one available token-price snapshot; this is not a historical series
      tags: [InferenceEconomics]
      x-greencio-tier: pro
      x-greencio-release: v1.0
      parameters:
        - $ref: "#/components/parameters/Since"
        - $ref: "#/components/parameters/Cursor"
        - $ref: "#/components/parameters/Limit"
      responses:
        "200":
          description: Single snapshot with series_type=single_snapshot_not_history and history_available=false
          content:
            application/json:
              schema: { $ref: "#/components/schemas/TokenPriceCollection" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "503": { $ref: "#/components/responses/ServiceUnavailable" }

  /lcoe:
    post:
      operationId: computeLcoe
      summary: Compute levelized cost of energy
      tags: [InferenceEconomics]
      x-greencio-tier: insights
      x-greencio-release: v1.0
      x-greencio-current-adapter: app/api/v1/lcoe/route.ts
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/LcoeInput" }
      responses:
        "200":
          description: LCOE result
          content:
            application/json:
              schema: { $ref: "#/components/schemas/LcoeObject" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "409": { $ref: "#/components/responses/IdempotencyConflict" }
        "408": { $ref: "#/components/responses/RequestTimeout" }
        "413": { $ref: "#/components/responses/PayloadTooLarge" }
        "429": { $ref: "#/components/responses/RateLimited" }
        "503": { $ref: "#/components/responses/ServiceUnavailable" }

  /fabs:
    get:
      operationId: listFabs
      summary: List an unverified fab fixture in explicit demo/test runtimes only
      tags: [Fabs]
      x-greencio-tier: pro
      x-greencio-release: preview-demo
      x-greencio-status: quarantined-from-customer-serving-runtimes
      parameters:
        - $ref: "#/components/parameters/Cursor"
        - $ref: "#/components/parameters/Limit"
        - in: query
          name: country
          schema: { type: string }
        - in: query
          name: operator
          schema: { type: string }
        - in: query
          name: node
          description: Substring match against a fab process node.
          schema: { type: string }
      responses:
        "200":
          description: Fab page
          content:
            application/json:
              schema: { $ref: "#/components/schemas/FabCollection" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "503": { $ref: "#/components/responses/ServiceUnavailable" }

  /fabs/{id}:
    get:
      operationId: getFab
      summary: Get one unverified fab fixture row in explicit demo/test runtimes only
      tags: [Fabs]
      x-greencio-tier: pro
      x-greencio-release: preview-demo
      x-greencio-status: quarantined-from-customer-serving-runtimes
      parameters:
        - $ref: "#/components/parameters/Id"
      responses:
        "200":
          description: Fab
          content:
            application/json:
              schema: { $ref: "#/components/schemas/FabObject" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "404": { $ref: "#/components/responses/NotFound" }
        "503": { $ref: "#/components/responses/ServiceUnavailable" }

  /fabs/chokepoints:
    get:
      operationId: listFabChokepoints
      summary: List unverified modeled chokepoint fixtures in explicit demo/test runtimes only
      tags: [Fabs]
      x-greencio-tier: pro
      x-greencio-release: preview-demo
      x-greencio-status: quarantined-from-customer-serving-runtimes
      responses:
        "200":
          description: Chokepoint page
          content:
            application/json:
              schema: { $ref: "#/components/schemas/ChokepointCollection" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
        "503": { $ref: "#/components/responses/ServiceUnavailable" }

  /index/power:
    get:
      operationId: listPowerCurves
      summary: List current power forecast curves
      tags: [PowerForecast]
      x-greencio-tier: power_forecast
      x-greencio-release: v1.1
      x-greencio-status: gated-on-live-prediction-market-integration
      responses:
        "200":
          description: Power forecast curves
          content:
            application/json:
              schema: { $ref: "#/components/schemas/PowerCurveCollection" }
        "403": { $ref: "#/components/responses/Forbidden" }

  /index/power/history:
    get:
      operationId: listPowerCurveHistory
      summary: List historical power forecast curves
      tags: [PowerForecast]
      x-greencio-tier: power_forecast
      x-greencio-release: v1.1
      parameters:
        - $ref: "#/components/parameters/Since"
        - $ref: "#/components/parameters/Cursor"
        - $ref: "#/components/parameters/Limit"
      responses:
        "200":
          description: Historical power curves
          content:
            application/json:
              schema: { $ref: "#/components/schemas/PowerCurveCollection" }
        "403": { $ref: "#/components/responses/Forbidden" }

  /index/power/scenarios:
    post:
      operationId: createPowerScenario
      summary: Run a conditional power forecast scenario
      tags: [PowerForecast]
      x-greencio-tier: power_forecast
      x-greencio-release: v1.1
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/PowerScenarioInput" }
      responses:
        "200":
          description: Scenario result
          content:
            application/json:
              schema: { $ref: "#/components/schemas/PowerScenarioObject" }
        "409": { $ref: "#/components/responses/IdempotencyConflict" }

  /index/power/methodology:
    get:
      operationId: getPowerMethodology
      summary: Get power forecast methodology metadata
      tags: [PowerForecast]
      x-greencio-tier: power_forecast
      x-greencio-release: v1.1
      responses:
        "200":
          description: Methodology metadata
          content:
            application/json:
              schema: { $ref: "#/components/schemas/MethodologyObject" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }

  /carbon/attribute:
    post:
      operationId: createCarbonAttribution
      summary: Attribute compute load to grid emissions
      tags: [CarbonTrail]
      x-greencio-tier: carbon_trail
      x-greencio-release: v1.2
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/CarbonAttributeInput" }
      responses:
        "200":
          description: Attribution result
          content:
            application/json:
              schema: { $ref: "#/components/schemas/CarbonRunObject" }
        "409": { $ref: "#/components/responses/IdempotencyConflict" }

  /carbon/grid-intensity:
    get:
      operationId: listGridIntensity
      summary: List grid-intensity factors
      tags: [CarbonTrail]
      x-greencio-tier: carbon_trail
      x-greencio-release: v1.2
      parameters:
        - in: query
          name: region
          required: true
          schema: { type: string }
        - $ref: "#/components/parameters/Since"
        - $ref: "#/components/parameters/Cursor"
        - $ref: "#/components/parameters/Limit"
      responses:
        "200":
          description: Grid intensity page
          content:
            application/json:
              schema: { $ref: "#/components/schemas/GridIntensityCollection" }
        "400": { $ref: "#/components/responses/BadRequest" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }

  /carbon/runs/{id}:
    get:
      operationId: getCarbonRun
      summary: Get a prior carbon attribution run
      tags: [CarbonTrail]
      x-greencio-tier: carbon_trail
      x-greencio-release: v1.2
      parameters:
        - $ref: "#/components/parameters/Id"
      responses:
        "200":
          description: Carbon run
          content:
            application/json:
              schema: { $ref: "#/components/schemas/CarbonRunObject" }
        "404": { $ref: "#/components/responses/NotFound" }

  /carbon/methodology:
    get:
      operationId: getCarbonMethodology
      summary: Get Carbon Trail methodology metadata
      tags: [CarbonTrail]
      x-greencio-tier: carbon_trail
      x-greencio-release: v1.2
      responses:
        "200":
          description: Methodology metadata
          content:
            application/json:
              schema: { $ref: "#/components/schemas/MethodologyObject" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }

  /webhooks:
    get:
      operationId: listWebhooks
      summary: List webhooks
      tags: [Webhooks]
      x-greencio-tier: pro
      x-greencio-release: v1.1
      responses:
        "200":
          description: Webhook page
          content:
            application/json:
              schema: { $ref: "#/components/schemas/WebhookCollection" }
        "401": { $ref: "#/components/responses/Unauthorized" }
        "403": { $ref: "#/components/responses/Forbidden" }
    post:
      operationId: createWebhook
      summary: Create a webhook
      tags: [Webhooks]
      x-greencio-tier: pro
      x-greencio-release: v1.1
      parameters:
        - $ref: "#/components/parameters/IdempotencyKey"
      requestBody:
        required: true
        content:
          application/json:
            schema: { $ref: "#/components/schemas/WebhookCreate" }
      responses:
        "201":
          description: Created webhook
          content:
            application/json:
              schema: { $ref: "#/components/schemas/WebhookCreatedObject" }
        "409": { $ref: "#/components/responses/IdempotencyConflict" }

  /webhooks/{id}:
    delete:
      operationId: deleteWebhook
      summary: Delete a webhook
      tags: [Webhooks]
      x-greencio-tier: pro
      x-greencio-release: v1.1
      parameters:
        - $ref: "#/components/parameters/Id"
      responses:
        "204":
          description: Deleted
        "404": { $ref: "#/components/responses/NotFound" }

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: "gc_live_* or gc_test_* API key"
      description: Data API keys use `gc_live_` and `gc_test_` prefixes. Keys are never accepted in query strings.

  parameters:
    Cursor:
      in: query
      name: cursor
      schema: { type: string }
      description: Opaque cursor from `meta.next_cursor`.
    Limit:
      in: query
      name: limit
      schema: { type: integer, minimum: 1, maximum: 500, default: 50 }
    Since:
      in: query
      name: since
      schema: { type: string, format: date-time }
      description: Inclusive ISO-8601 UTC lower bound.
    Id:
      in: path
      name: id
      required: true
      schema: { type: string }
    IndexName:
      in: path
      name: index_name
      required: true
      schema: { type: string, pattern: "^GCI-[A-Z0-9]+-[A-Z0-9]+-(SPOT|[0-9]+M)$" }
    IncludeProvisional:
      in: query
      name: include_provisional
      schema: { type: boolean, default: false }
      description: Provisional settlements are excluded by default. Test/sandbox keys may set true for review workflows; live keys receive 403.
    IdempotencyKey:
      in: header
      name: Idempotency-Key
      schema: { type: string, minLength: 8, maxLength: 200 }
      description: Required for billing-relevant POST endpoints.

  headers:
    RequestId:
      schema: { type: string }
      description: Request identifier for support and log correlation.
    RateLimitLimit:
      schema: { type: integer }
      description: Per-second burst limit.
    RateLimitRemaining:
      schema: { type: integer }
      description: Requests remaining in the current burst window.
    RateLimitReset:
      schema: { type: string, format: date-time }
      description: Burst window reset time.
    RateLimitQuotaLimit:
      schema: { type: integer }
      description: Monthly quota limit.
    RateLimitQuotaRemaining:
      schema: { type: integer }
      description: Requests or runs remaining in the monthly quota.
    RateLimitQuotaReset:
      schema: { type: string, format: date-time }
      description: Monthly quota reset time.

  responses:
    BadRequest:
      description: Invalid request
      content:
        application/json:
          schema: { $ref: "#/components/schemas/ErrorEnvelope" }
    Unauthorized:
      description: Missing or malformed API key
      content:
        application/json:
          schema: { $ref: "#/components/schemas/ErrorEnvelope" }
    Forbidden:
      description: Key is valid but tier or add-on does not include this resource
      content:
        application/json:
          schema: { $ref: "#/components/schemas/ErrorEnvelope" }
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema: { $ref: "#/components/schemas/ErrorEnvelope" }
    IdempotencyConflict:
      description: Same idempotency key was reused with a different body
      content:
        application/json:
          schema: { $ref: "#/components/schemas/ErrorEnvelope" }
    ValidationFailed:
      description: Body parsed but failed semantic validation
      content:
        application/json:
          schema: { $ref: "#/components/schemas/ErrorEnvelope" }
    RequestTimeout:
      description: Request body or processing exceeded the endpoint deadline
      content:
        application/json:
          schema: { $ref: "#/components/schemas/ErrorEnvelope" }
    PayloadTooLarge:
      description: Request body exceeds the endpoint byte limit
      content:
        application/json:
          schema: { $ref: "#/components/schemas/ErrorEnvelope" }
    RateLimited:
      description: Rate limit exceeded
      headers:
        Retry-After:
          schema: { type: integer }
      content:
        application/json:
          schema: { $ref: "#/components/schemas/ErrorEnvelope" }
    ServiceUnavailable:
      description: Required backing data, freshness, provenance, or publication-right gate is unavailable
      content:
        application/json:
          schema: { $ref: "#/components/schemas/ErrorEnvelope" }

  schemas:
    ResponseMeta:
      type: object
      required: [generated_at, request_id]
      properties:
        generated_at: { type: string, format: date-time }
        request_id: { type: string }
        next_cursor:
          type: [string, "null"]
        source_mode:
          type: string
          enum: [persisted, synthetic_demo, static_reference, seed_reference, submitted_model]
        status: { type: string }
        freshness:
          type: string
          enum: [fresh, warning, breached, disabled, stale, unknown, not_applicable, archival_snapshot]
        last_success_at: { type: [string, "null"], format: date-time }
        age_seconds: { type: [integer, "null"], minimum: 0 }
        max_age_seconds: { type: integer, minimum: 0 }
        dataset_snapshot_at: { type: [string, "null"], format: date-time }
        artifact_sha256: { type: string, pattern: "^[a-f0-9]{64}$" }
        row_count: { type: integer, minimum: 0 }
        evidence_status: { type: string }
        source_rights: { $ref: "#/components/schemas/SourceRights" }
        limitations: { type: string }
        series_type: { type: string }
        history_available: { type: boolean }
    ErrorEnvelope:
      type: object
      required: [error]
      properties:
        error:
          type: object
          required: [code, message, request_id]
          properties:
            code:
              type: string
              enum: [invalid_request, invalid_filter, unauthenticated, unauthorized, not_found, idempotency_conflict, validation_failed, payload_too_large, request_timeout, rate_limited, internal_error, service_unavailable]
            message: { type: string }
            field:
              type: [string, "null"]
            request_id: { type: string }

    Signal:
      type: object
      required: [id, title, type, impact, confidence, source_url, observed_at, summary_kind, confidence_status, source_rights]
      properties:
        id: { type: string, pattern: "^sig_" }
        title: { type: string }
        summary: { type: string, maxLength: 200 }
        type: { type: string }
        impact: { type: string, enum: [high, medium, low] }
        confidence: { type: number, minimum: 0, maximum: 1 }
        source_url: { type: string, format: uri }
        source_name: { type: string }
        observed_at: { type: string, format: date-time }
        summary_kind: { type: string, enum: [derived_summary] }
        confidence_status: { type: string, enum: [legacy_seed_score_unverified] }
        source_rights: { $ref: "#/components/schemas/SourceRights" }
        entities: { type: array, items: { type: string } }
        linked_markets: { type: array, items: { $ref: "#/components/schemas/LinkedMarket" } }
        facility_ids: { type: array, items: { type: string } }
    SignalCollection:
      type: object
      required: [data, meta]
      properties:
        data: { type: array, items: { $ref: "#/components/schemas/Signal" } }
        meta: { $ref: "#/components/schemas/ResponseMeta" }
    SignalObject:
      type: object
      required: [data, meta]
      properties:
        data: { $ref: "#/components/schemas/Signal" }
        meta: { $ref: "#/components/schemas/ResponseMeta" }

    IntelligenceItem:
      type: object
      required: [id, title, source_url, source_name, published_at, impact_level, relevance_score, summary_kind, source_rights]
      properties:
        id: { type: string, pattern: "^int_" }
        title: { type: string, maxLength: 500 }
        summary: { type: string, maxLength: 200 }
        source_url: { type: string, format: uri }
        source_name: { type: string }
        published_at: { type: string, format: date-time }
        discovered_at: { type: string, format: date-time }
        relevance_score: { type: number, minimum: 0, maximum: 1 }
        impact_level: { type: string, enum: [high, medium, low] }
        sentiment: { type: string, enum: [positive, neutral, negative] }
        summary_kind: { type: string, enum: [derived_summary] }
        source_rights: { $ref: "#/components/schemas/SourceRights" }
        tags: { type: array, items: { type: string } }
        entities:
          type: object
          additionalProperties:
            type: array
            items: { type: string }
    IntelligenceCollection:
      type: object
      required: [data, meta]
      properties:
        data: { type: array, items: { $ref: "#/components/schemas/IntelligenceItem" } }
        meta: { $ref: "#/components/schemas/ResponseMeta" }
    IntelligenceObject:
      type: object
      required: [data, meta]
      properties:
        data: { $ref: "#/components/schemas/IntelligenceItem" }
        meta: { $ref: "#/components/schemas/ResponseMeta" }

    Facility:
      type: object
      required: [id, operator, facility_name, country, provenance, evidence_status, source_rights_status, publication_scope]
      properties:
        id: { type: string, pattern: "^fac_" }
        operator: { type: string }
        facility_name: { type: string }
        city: { type: string }
        state_province: { type: string }
        country: { type: string }
        total_it_capacity_mw: { type: number }
        total_facility_power_mw: { type: number }
        pue: { type: number }
        renewable_energy_pct: { type: number }
        primary_power_source: { type: string }
        provenance: { type: array, items: { $ref: "#/components/schemas/SourceRef" } }
        source_notes:
          type: string
          description: Unverified prose carried from the CSV; not a canonical source reference.
        evidence_status: { type: string, enum: [unverified] }
        source_rights_status: { type: string, enum: [not_reviewed] }
        publication_scope: { type: string, enum: [demo_only] }
    FacilityCollection:
      type: object
      required: [data, meta]
      properties:
        data: { type: array, items: { $ref: "#/components/schemas/Facility" } }
        meta: { $ref: "#/components/schemas/ResponseMeta" }
    FacilityObject:
      type: object
      required: [data, meta]
      properties:
        data: { $ref: "#/components/schemas/Facility" }
        meta: { $ref: "#/components/schemas/ResponseMeta" }

    ComputeSettlement:
      type: object
      required: [id, index_name, sku, region, tenor, as_of, currency, value, band_low, band_high, methodology_version, input_hash, source_mode]
      properties:
        id: { type: string, pattern: "^set_" }
        index_name: { type: string }
        sku: { type: string }
        region: { type: string }
        tenor: { type: string }
        as_of: { type: string, format: date-time }
        currency: { type: string, minLength: 3, maxLength: 3 }
        value: { type: number }
        band_low: { type: number }
        band_high: { type: number }
        band_pct: { type: number }
        n_inputs: { type: integer }
        n_venues: { type: integer }
        provisional: { type: boolean }
        methodology_version: { type: string }
        input_hash: { type: string }
        source_mode: { type: string, enum: [persisted, synthetic_demo] }
        audit:
          type: object
          properties:
            run_id: { type: string }
            included_input_ids: { type: array, items: { type: string } }
            rejected_inputs: { type: array, items: { type: object, additionalProperties: true } }
            source_weight_shares: { type: object, additionalProperties: { type: number } }
            supersedes_settlement_id: { type: [string, "null"] }
    ComputeSettlementCollection:
      type: object
      required: [data, meta]
      properties:
        data: { type: array, items: { $ref: "#/components/schemas/ComputeSettlement" } }
        meta: { $ref: "#/components/schemas/ResponseMeta" }
    ComputeSettlementObject:
      type: object
      required: [data, meta]
      properties:
        data: { $ref: "#/components/schemas/ComputeSettlement" }
        meta: { $ref: "#/components/schemas/ResponseMeta" }

    BasisSpread:
      type: object
      required: [id, spread_index_name, as_of, value]
      properties:
        id: { type: string }
        spread_index_name: { type: string }
        as_of: { type: string, format: date-time }
        value: { type: number }
        ratio: { type: number }
        base_settlement_id: { type: string }
        comparison_settlement_id: { type: string }
    BasisSpreadCollection:
      type: object
      required: [data, meta]
      properties:
        data: { type: array, items: { $ref: "#/components/schemas/BasisSpread" } }
        meta: { $ref: "#/components/schemas/ResponseMeta" }

    Methodology:
      type: object
      required: [version, status, effective_from, rulebook_hash]
      properties:
        version: { type: string }
        status: { type: string, enum: [draft, active, retired] }
        effective_from: { type: string, format: date-time }
        effective_to: { type: [string, "null"], format: date-time }
        rulebook_hash: { type: string }
        notes: { type: string }
        disclaimer: { type: string }
    MethodologyObject:
      type: object
      required: [data, meta]
      properties:
        data: { $ref: "#/components/schemas/Methodology" }
        meta: { $ref: "#/components/schemas/ResponseMeta" }

    UnitEconomicsInput:
      type: object
      required: [facility_capacity_mw, gpu_count, gpu_type, capex_usd, power_cost_usd_per_mwh, utilization_rate]
      properties:
        facility_capacity_mw: { type: number, exclusiveMinimum: 0 }
        gpu_count: { type: integer, minimum: 1 }
        gpu_type: { type: string }
        capex_usd: { type: number, exclusiveMinimum: 0 }
        annual_opex_usd: { type: number, minimum: 0 }
        power_cost_usd_per_mwh: { type: number, minimum: 0 }
        pue: { type: number, minimum: 1 }
        utilization_rate: { type: number, minimum: 0, maximum: 1 }
        project_lifetime_years: { type: integer, minimum: 1, maximum: 30 }
        discount_rate: { type: number, minimum: 0, maximum: 0.5 }
        tokens_per_second_per_gpu: { type: number, minimum: 0 }
    UnitEconomicsResult:
      type: object
      required: [lcoi_usd_per_million_tokens, annual_token_capacity, power_cost_share_pct, disclaimer]
      properties:
        lcoi_usd_per_million_tokens: { type: number }
        annual_token_capacity: { type: number }
        effective_tokens_per_watt: { type: number }
        total_lifecycle_cost_usd: { type: number }
        power_cost_share_pct: { type: number }
        gpu_cost_share_pct: { type: number }
        opex_cost_share_pct: { type: number }
        breakeven_utilization_pct: { type: number }
        disclaimer: { type: string }
    UnitEconomicsObject:
      type: object
      required: [data, meta]
      properties:
        data: { $ref: "#/components/schemas/UnitEconomicsResult" }
        meta: { $ref: "#/components/schemas/ResponseMeta" }

    AiInfraFinding:
      type: object
      additionalProperties: false
      required: [path, kind]
      description: An allowlisted fact. At least one detected fact property is required by the runtime schema.
      properties:
        path: { type: string, maxLength: 500 }
        line: { type: integer, minimum: 1 }
        kind:
          type: string
          enum: [terraform, kubernetes, helm, docker, compose, model_config, environment, other]
        provider: { type: string, maxLength: 160 }
        region: { type: string, maxLength: 160 }
        gpu_type: { type: string, maxLength: 160 }
        gpu_count: { type: integer, minimum: 1, maximum: 1000000 }
        replicas: { type: integer, minimum: 1, maximum: 1000000 }
        utilization_rate: { type: number, minimum: 0, maximum: 1 }
        model: { type: string, maxLength: 160 }
    AiInfraScan:
      type: object
      additionalProperties: false
      required: [files_scanned, findings]
      properties:
        repository_name: { type: string, maxLength: 200 }
        revision: { type: string, maxLength: 200 }
        files_scanned: { type: integer, minimum: 0, maximum: 100000 }
        truncated: { type: boolean, default: false }
        findings:
          type: array
          maxItems: 2000
          items: { $ref: "#/components/schemas/AiInfraFinding" }
    ComputePriceBasisInput:
      type: object
      additionalProperties: false
      required: [index_name, sku, region, tenor, value_usd_per_gpu_hour, observed_at, methodology_version, source_mode]
      properties:
        index_name: { type: string }
        sku: { type: string }
        region: { type: string }
        tenor: { type: string }
        value_usd_per_gpu_hour: { type: number, exclusiveMinimum: 0 }
        band_low: { type: number, minimum: 0 }
        band_high: { type: number, minimum: 0 }
        observed_at:
          type: string
          format: date-time
          description: >-
            ISO-8601 instant. An explicit offset is accepted and normalized to
            UTC before the value is hashed, stored, or echoed back.
        methodology_version: { type: string }
        source_mode: { type: string, enum: [persisted, synthetic_demo] }
        input_hash: { type: string, pattern: "^[a-f0-9]{64}$" }
    PowerDiligenceInput:
      type: object
      additionalProperties: false
      required: [region, isoRto, targetPowerMw, targetOnlineDate, workloadType, developmentStage]
      properties:
        siteName: { type: string, maxLength: 140 }
        region: { type: string, maxLength: 120 }
        isoRto: { type: string, enum: [PJM, ERCOT, CAISO, MISO, SPP, NYISO, ISONE, WECC, Southeast, Other] }
        utility: { type: string, maxLength: 140 }
        targetPowerMw: { type: number, exclusiveMinimum: 0, maximum: 5000 }
        targetOnlineDate: { type: string, format: date }
        workloadType: { type: string, enum: [training, inference, mixed, colocation] }
        developmentStage: { type: string, enum: [site_selection, interconnection_requested, utility_study, ppa_negotiation, permitted, construction, energized] }
        knownConstraints:
          type: array
          maxItems: 8
          items: { type: string, enum: [interconnection_queue, substation_upgrade, transmission_upgrade, local_permit, water_or_cooling, gas_bridge, ppa_unsigned, equipment_lead_time, community_opposition, unknown] }
        evidenceUrls:
          type: array
          maxItems: 6
          items: { type: string, format: uri }
        notes: { type: string, maxLength: 1200 }
    AiInfraReviewInput:
      type: object
      additionalProperties: false
      required: [scan]
      properties:
        scan: { $ref: "#/components/schemas/AiInfraScan" }
        compute_price_basis: { $ref: "#/components/schemas/ComputePriceBasisInput" }
        economics: { $ref: "#/components/schemas/UnitEconomicsInput" }
        power: { $ref: "#/components/schemas/PowerDiligenceInput" }
    AiInfraReviewResult:
      type: object
      required: [review_id, input_hash, status, repository, detected, economics, power, missing_evidence, warnings, provenance]
      properties:
        review_id: { type: string, pattern: "^air_[a-f0-9]{26}$" }
        input_hash: { type: string, pattern: "^[a-f0-9]{64}$" }
        status: { type: string, enum: [actionable, facts_only, evidence_required] }
        repository: { type: object, additionalProperties: true }
        detected: { type: object, additionalProperties: true }
        compute_price_basis:
          oneOf:
            - { $ref: "#/components/schemas/ComputePriceBasisInput" }
            - { type: "null" }
        economics: { type: object, additionalProperties: true }
        power: { type: object, additionalProperties: true }
        missing_evidence: { type: array, items: { type: string } }
        warnings: { type: array, items: { type: string } }
        provenance: { type: object, additionalProperties: true }
    AiInfraReviewObject:
      type: object
      required: [data, meta]
      properties:
        data: { $ref: "#/components/schemas/AiInfraReviewResult" }
        meta: { $ref: "#/components/schemas/ResponseMeta" }

    TokenPrice:
      type: object
      required: [id, provider, model, input_usd_per_million_tokens, output_usd_per_million_tokens, observed_at]
      properties:
        id: { type: string }
        provider: { type: string }
        model: { type: string }
        model_class: { type: string }
        input_usd_per_million_tokens: { type: number }
        output_usd_per_million_tokens: { type: number }
        observed_at: { type: string, format: date-time }
        source_url: { type: string, format: uri }
    TokenPriceCollection:
      type: object
      required: [data, meta]
      properties:
        data: { type: array, items: { $ref: "#/components/schemas/TokenPrice" } }
        meta: { $ref: "#/components/schemas/ResponseMeta" }

    LcoeInput:
      type: object
      required: [capex_usd, annual_generation_mwh, project_lifetime_years, discount_rate]
      properties:
        capex_usd: { type: number, exclusiveMinimum: 0 }
        annual_generation_mwh: { type: number, exclusiveMinimum: 0 }
        project_lifetime_years: { type: integer, minimum: 1, maximum: 50 }
        discount_rate: { type: number, minimum: 0, maximum: 0.3 }
        annual_opex_usd: { type: number, minimum: 0 }
        annual_fuel_cost_usd: { type: number, minimum: 0 }
        opex_escalation_rate: { type: number, minimum: 0, maximum: 0.2 }
        fuel_escalation_rate: { type: number, minimum: 0, maximum: 0.2 }
        degradation_rate: { type: number, minimum: 0, maximum: 0.05 }
        technology: { type: string }
        capacity_mw: { type: number }
    LcoeResult:
      type: object
      required: [lcoe_usd_per_mwh, lcoe_usd_per_kwh, disclaimer]
      properties:
        lcoe_usd_per_mwh: { type: number }
        lcoe_usd_per_kwh: { type: number }
        total_lifecycle_cost_usd: { type: number }
        npv_generation_mwh: { type: number }
        disclaimer: { type: string }
    LcoeObject:
      type: object
      required: [data, meta]
      properties:
        data: { $ref: "#/components/schemas/LcoeResult" }
        meta: { $ref: "#/components/schemas/ResponseMeta" }

    Fab:
      type: object
      required: [id, name, operator, country, provenance, evidence_status, source_rights_status, publication_scope]
      properties:
        id: { type: string, pattern: "^fab_" }
        name: { type: string }
        operator: { type: string }
        country: { type: string }
        process_nodes: { type: array, items: { type: string } }
        capacity_kwpm: { type: number }
        power_demand_mw: { type: number }
        geopolitical_risk_score: { type: number, minimum: 0, maximum: 100 }
        refreshed_at: { type: string, format: date-time }
        provenance: { type: array, items: { $ref: "#/components/schemas/SourceRef" } }
        evidence_status: { type: string, enum: [unverified] }
        source_rights_status: { type: string, enum: [not_reviewed] }
        publication_scope: { type: string, enum: [demo_only] }
    FabCollection:
      type: object
      required: [data, meta]
      properties:
        data: { type: array, items: { $ref: "#/components/schemas/Fab" } }
        meta: { $ref: "#/components/schemas/ResponseMeta" }
    FabObject:
      type: object
      required: [data, meta]
      properties:
        data: { $ref: "#/components/schemas/Fab" }
        meta: { $ref: "#/components/schemas/ResponseMeta" }

    Chokepoint:
      type: object
      required: [id, name, category, risk_score, evidence_status, source_rights_status, publication_scope, modeled_score]
      properties:
        id: { type: string }
        name: { type: string }
        category: { type: string }
        country: { type: string }
        risk_score: { type: number, minimum: 0, maximum: 100 }
        affected_nodes: { type: array, items: { type: string } }
        evidence_status: { type: string, enum: [unverified] }
        source_rights_status: { type: string, enum: [not_reviewed] }
        publication_scope: { type: string, enum: [demo_only] }
        modeled_score: { type: boolean, const: true }
    ChokepointCollection:
      type: object
      required: [data, meta]
      properties:
        data: { type: array, items: { $ref: "#/components/schemas/Chokepoint" } }
        meta: { $ref: "#/components/schemas/ResponseMeta" }

    PowerCurve:
      type: object
      required: [id, region, as_of, methodology_version, points]
      properties:
        id: { type: string }
        region: { type: string }
        as_of: { type: string, format: date-time }
        methodology_version: { type: string }
        points:
          type: array
          items:
            type: object
            required: [tenor, value_usd_per_mwh, band_low, band_high]
            properties:
              tenor: { type: string }
              value_usd_per_mwh: { type: number }
              band_low: { type: number }
              band_high: { type: number }
    PowerCurveCollection:
      type: object
      required: [data, meta]
      properties:
        data: { type: array, items: { $ref: "#/components/schemas/PowerCurve" } }
        meta: { $ref: "#/components/schemas/ResponseMeta" }
    PowerScenarioInput:
      type: object
      required: [region, assumptions]
      properties:
        region: { type: string }
        assumptions: { type: object, additionalProperties: true }
    PowerScenarioObject:
      type: object
      required: [data, meta]
      properties:
        data: { $ref: "#/components/schemas/PowerCurve" }
        meta: { $ref: "#/components/schemas/ResponseMeta" }

    CarbonAttributeInput:
      type: object
      required: [region, load_profile]
      properties:
        region: { type: string }
        load_profile:
          type: array
          items:
            type: object
            required: [timestamp, compute_load_mwh]
            properties:
              timestamp: { type: string, format: date-time }
              compute_load_mwh: { type: number, minimum: 0 }
        retention_days: { type: integer, minimum: 0, maximum: 2555, default: 90 }
    CarbonRun:
      type: object
      required: [id, region, total_scope2_tco2e, methodology_version, audit_hash, generated_at]
      properties:
        id: { type: string, pattern: "^car_" }
        region: { type: string }
        total_scope2_tco2e: { type: number }
        methodology_version: { type: string }
        audit_hash: { type: string }
        generated_at: { type: string, format: date-time }
        source_series: { type: array, items: { $ref: "#/components/schemas/SourceRef" } }
        disclaimer: { type: string }
    CarbonRunObject:
      type: object
      required: [data, meta]
      properties:
        data: { $ref: "#/components/schemas/CarbonRun" }
        meta: { $ref: "#/components/schemas/ResponseMeta" }
    GridIntensity:
      type: object
      required: [region, timestamp, kg_co2e_per_mwh, source]
      properties:
        region: { type: string }
        timestamp: { type: string, format: date-time }
        kg_co2e_per_mwh: { type: number }
        source: { $ref: "#/components/schemas/SourceRef" }
    GridIntensityCollection:
      type: object
      required: [data, meta]
      properties:
        data: { type: array, items: { $ref: "#/components/schemas/GridIntensity" } }
        meta: { $ref: "#/components/schemas/ResponseMeta" }

    Webhook:
      type: object
      required: [id, url, events, created_at]
      properties:
        id: { type: string, pattern: "^wh_" }
        url: { type: string, format: uri }
        events:
          type: array
          items:
            type: string
            enum: [signal.created, intelligence.created, facility.updated, compute_settlement.created]
        filters: { type: object, additionalProperties: true }
        created_at: { type: string, format: date-time }
        last_delivered_at: { type: [string, "null"], format: date-time }
    WebhookCollection:
      type: object
      required: [data, meta]
      properties:
        data: { type: array, items: { $ref: "#/components/schemas/Webhook" } }
        meta: { $ref: "#/components/schemas/ResponseMeta" }
    WebhookCreate:
      type: object
      required: [url, events]
      properties:
        url: { type: string, format: uri }
        events:
          type: array
          items:
            type: string
            enum: [signal.created, intelligence.created, facility.updated, compute_settlement.created]
        filters: { type: object, additionalProperties: true }
    WebhookCreated:
      allOf:
        - $ref: "#/components/schemas/Webhook"
        - type: object
          required: [signing_secret]
          properties:
            signing_secret:
              type: string
              description: Shown once. Used to verify HMAC-SHA256 webhook signatures.
    WebhookCreatedObject:
      type: object
      required: [data, meta]
      properties:
        data: { $ref: "#/components/schemas/WebhookCreated" }
        meta: { $ref: "#/components/schemas/ResponseMeta" }

    LinkedMarket:
      type: object
      properties:
        market_id: { type: string }
        venue: { type: string }
        probability: { type: number, minimum: 0, maximum: 1 }
        observed_at: { type: string, format: date-time }
    SourceRef:
      type: object
      required: [name]
      properties:
        name: { type: string }
        url: { type: string, format: uri }
        observed_at: { type: string, format: date-time }
    SourceRights:
      type: object
      required: [policy_id, counsel_status, redistribution_status, publication_scope]
      properties:
        policy_id: { type: string }
        counsel_status: { type: string, enum: [pending_review, approved, rejected] }
        redistribution_status: { type: string, enum: [internal_only, derived_only, contributor_panel, redistributable] }
        publication_scope: { type: string }
