openapi: 3.1.0
info:
    title: SupplyChainService API
    version: 1.0.0
paths:
    /api/supply-chain/v1/get-shipping-rates:
        get:
            tags:
                - SupplyChainService
            summary: GetShippingRates
            operationId: GetShippingRates
            responses:
                "200":
                    description: Successful response
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/GetShippingRatesResponse'
                "400":
                    description: Validation error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ValidationError'
                default:
                    description: Error response
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/Error'
    /api/supply-chain/v1/get-chokepoint-status:
        get:
            tags:
                - SupplyChainService
            summary: GetChokepointStatus
            operationId: GetChokepointStatus
            responses:
                "200":
                    description: Successful response
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/GetChokepointStatusResponse'
                "400":
                    description: Validation error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ValidationError'
                default:
                    description: Error response
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/Error'
    /api/supply-chain/v1/get-chokepoint-history:
        get:
            tags:
                - SupplyChainService
            summary: GetChokepointHistory
            description: |-
                GetChokepointHistory returns transit-count history for a single chokepoint,
                 loaded lazily on card expand. Keeps the status RPC compact (no 180-day
                 history per chokepoint on every call).
            operationId: GetChokepointHistory
            parameters:
                - name: chokepointId
                  in: query
                  required: false
                  schema:
                    type: string
            responses:
                "200":
                    description: Successful response
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/GetChokepointHistoryResponse'
                "400":
                    description: Validation error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ValidationError'
                default:
                    description: Error response
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/Error'
    /api/supply-chain/v1/get-critical-minerals:
        get:
            tags:
                - SupplyChainService
            summary: GetCriticalMinerals
            operationId: GetCriticalMinerals
            responses:
                "200":
                    description: Successful response
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/GetCriticalMineralsResponse'
                "400":
                    description: Validation error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ValidationError'
                default:
                    description: Error response
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/Error'
    /api/supply-chain/v1/get-shipping-stress:
        get:
            tags:
                - SupplyChainService
            summary: GetShippingStress
            description: GetShippingStress returns carrier market data and a composite stress index.
            operationId: GetShippingStress
            responses:
                "200":
                    description: Successful response
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/GetShippingStressResponse'
                "400":
                    description: Validation error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ValidationError'
                default:
                    description: Error response
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/Error'
    /api/supply-chain/v1/get-country-chokepoint-index:
        get:
            tags:
                - SupplyChainService
            summary: GetCountryChokepointIndex
            description: GetCountryChokepointIndex returns per-chokepoint exposure scores for a country. PRO-gated.
            operationId: GetCountryChokepointIndex
            parameters:
                - name: iso2
                  in: query
                  description: ISO 3166-1 alpha-2 country code (uppercase).
                  required: false
                  schema:
                    type: string
                - name: hs2
                  in: query
                  description: HS2 chapter (2-digit string). Defaults to "27" (energy/mineral fuels) when absent.
                  required: false
                  schema:
                    type: string
            responses:
                "200":
                    description: Successful response
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/GetCountryChokepointIndexResponse'
                "400":
                    description: Validation error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ValidationError'
                default:
                    description: Error response
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/Error'
    /api/supply-chain/v1/get-bypass-options:
        get:
            tags:
                - SupplyChainService
            summary: GetBypassOptions
            description: GetBypassOptions returns ranked bypass corridors for a chokepoint. PRO-gated.
            operationId: GetBypassOptions
            parameters:
                - name: chokepointId
                  in: query
                  required: false
                  schema:
                    type: string
                - name: cargoType
                  in: query
                  description: 'container | tanker | bulk | roro  (default: "container")'
                  required: false
                  schema:
                    type: string
                - name: closurePct
                  in: query
                  description: '0-100, percent of capacity blocked (default: 100)'
                  required: false
                  schema:
                    type: integer
                    format: int32
            responses:
                "200":
                    description: Successful response
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/GetBypassOptionsResponse'
                "400":
                    description: Validation error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ValidationError'
                default:
                    description: Error response
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/Error'
    /api/supply-chain/v1/get-country-cost-shock:
        get:
            tags:
                - SupplyChainService
            summary: GetCountryCostShock
            description: GetCountryCostShock returns cost shock and war risk data for a country+chokepoint. PRO-gated.
            operationId: GetCountryCostShock
            parameters:
                - name: iso2
                  in: query
                  required: false
                  schema:
                    type: string
                - name: chokepointId
                  in: query
                  required: false
                  schema:
                    type: string
                - name: hs2
                  in: query
                  description: 'HS2 chapter (default: "27")'
                  required: false
                  schema:
                    type: string
            responses:
                "200":
                    description: Successful response
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/GetCountryCostShockResponse'
                "400":
                    description: Validation error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ValidationError'
                default:
                    description: Error response
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/Error'
    /api/supply-chain/v1/get-country-products:
        get:
            tags:
                - SupplyChainService
            summary: GetCountryProducts
            description: GetCountryProducts returns the seeded bilateral-HS4 import basket for a country. PRO-gated.
            operationId: GetCountryProducts
            parameters:
                - name: iso2
                  in: query
                  required: false
                  schema:
                    type: string
            responses:
                "200":
                    description: Successful response
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/GetCountryProductsResponse'
                "400":
                    description: Validation error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ValidationError'
                default:
                    description: Error response
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/Error'
    /api/supply-chain/v1/get-multi-sector-cost-shock:
        get:
            tags:
                - SupplyChainService
            summary: GetMultiSectorCostShock
            description: |-
                GetMultiSectorCostShock returns per-sector cost-shock estimates for a
                 country+chokepoint+closure-window. PRO-gated.
            operationId: GetMultiSectorCostShock
            parameters:
                - name: iso2
                  in: query
                  required: false
                  schema:
                    type: string
                - name: chokepointId
                  in: query
                  required: false
                  schema:
                    type: string
                - name: closureDays
                  in: query
                  description: Closure-window duration in days. Server clamps to [1, 365]. Defaults to 30.
                  required: false
                  schema:
                    type: integer
                    format: int32
            responses:
                "200":
                    description: Successful response
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/GetMultiSectorCostShockResponse'
                "400":
                    description: Validation error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ValidationError'
                default:
                    description: Error response
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/Error'
    /api/supply-chain/v1/get-sector-dependency:
        get:
            tags:
                - SupplyChainService
            summary: GetSectorDependency
            description: GetSectorDependency returns dependency flags and risk profile for a country+HS2 sector. PRO-gated.
            operationId: GetSectorDependency
            parameters:
                - name: iso2
                  in: query
                  required: false
                  schema:
                    type: string
                - name: hs2
                  in: query
                  description: HS2 chapter code, e.g. "27" (mineral fuels), "85" (electronics)
                  required: false
                  schema:
                    type: string
            responses:
                "200":
                    description: Successful response
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/GetSectorDependencyResponse'
                "400":
                    description: Validation error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ValidationError'
                default:
                    description: Error response
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/Error'
    /api/supply-chain/v1/get-route-explorer-lane:
        get:
            tags:
                - SupplyChainService
            summary: GetRouteExplorerLane
            description: |-
                GetRouteExplorerLane returns the primary maritime route, chokepoint exposures,
                 bypass options with geometry, war risk, and static transit/freight estimates for
                 a country pair + HS2 + cargo type. PRO-gated. Wraps the route-intelligence vendor
                 endpoint's compute with browser-callable auth and adds fields needed by the
                 Route Explorer UI.
            operationId: GetRouteExplorerLane
            parameters:
                - name: fromIso2
                  in: query
                  required: false
                  schema:
                    type: string
                - name: toIso2
                  in: query
                  required: false
                  schema:
                    type: string
                - name: hs2
                  in: query
                  description: HS2 chapter code, e.g. "27", "85"
                  required: false
                  schema:
                    type: string
                - name: cargoType
                  in: query
                  description: 'One of: container, tanker, bulk, roro'
                  required: false
                  schema:
                    type: string
            responses:
                "200":
                    description: Successful response
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/GetRouteExplorerLaneResponse'
                "400":
                    description: Validation error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ValidationError'
                default:
                    description: Error response
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/Error'
    /api/supply-chain/v1/get-route-impact:
        get:
            tags:
                - SupplyChainService
            summary: GetRouteImpact
            operationId: GetRouteImpact
            parameters:
                - name: fromIso2
                  in: query
                  required: false
                  schema:
                    type: string
                - name: toIso2
                  in: query
                  required: false
                  schema:
                    type: string
                - name: hs2
                  in: query
                  required: false
                  schema:
                    type: string
            responses:
                "200":
                    description: Successful response
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/GetRouteImpactResponse'
                "400":
                    description: Validation error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ValidationError'
                default:
                    description: Error response
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/Error'
    /api/supply-chain/v1/list-pipelines:
        get:
            tags:
                - SupplyChainService
            summary: ListPipelines
            description: |-
                ListPipelines returns the curated oil & gas pipeline registry for the
                 Energy Atlas PathLayer. Public badges are DERIVED from evidence bundles
                 server-side and versioned (classifier_version). Free-tier; see
                 docs/methodology/pipelines.mdx for data + classifier spec.
            operationId: ListPipelines
            parameters:
                - name: commodityType
                  in: query
                  description: Filter to one commodity. Omit (or pass empty) to receive both.
                  required: false
                  schema:
                    type: string
            responses:
                "200":
                    description: Successful response
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ListPipelinesResponse'
                "400":
                    description: Validation error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ValidationError'
                default:
                    description: Error response
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/Error'
    /api/supply-chain/v1/get-pipeline-detail:
        get:
            tags:
                - SupplyChainService
            summary: GetPipelineDetail
            description: |-
                GetPipelineDetail returns a single pipeline with full evidence bundle
                 + auto-revision-log entries. Loaded lazily on drawer open.
            operationId: GetPipelineDetail
            parameters:
                - name: pipelineId
                  in: query
                  required: false
                  schema:
                    type: string
            responses:
                "200":
                    description: Successful response
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/GetPipelineDetailResponse'
                "400":
                    description: Validation error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ValidationError'
                default:
                    description: Error response
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/Error'
    /api/supply-chain/v1/list-storage-facilities:
        get:
            tags:
                - SupplyChainService
            summary: ListStorageFacilities
            description: |-
                ListStorageFacilities returns the curated strategic storage registry
                 (UGS + SPR + LNG + crude tank farms) for the Energy Atlas DeckGL
                 ScatterplotLayer. Public badges are DERIVED from evidence bundles
                 server-side and versioned (classifier_version). Free-tier; see
                 docs/methodology/storage.mdx.
            operationId: ListStorageFacilities
            parameters:
                - name: facilityType
                  in: query
                  description: |-
                    Filter to one facility type. Accepts:
                       "ugs" | "spr" | "lng_export" | "lng_import" | "crude_tank_farm"
                     Omit (or pass empty) to receive all types.
                  required: false
                  schema:
                    type: string
            responses:
                "200":
                    description: Successful response
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ListStorageFacilitiesResponse'
                "400":
                    description: Validation error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ValidationError'
                default:
                    description: Error response
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/Error'
    /api/supply-chain/v1/get-storage-facility-detail:
        get:
            tags:
                - SupplyChainService
            summary: GetStorageFacilityDetail
            description: |-
                GetStorageFacilityDetail returns a single facility with full evidence
                 bundle + revision log. Loaded lazily on drawer open.
            operationId: GetStorageFacilityDetail
            parameters:
                - name: facilityId
                  in: query
                  required: false
                  schema:
                    type: string
            responses:
                "200":
                    description: Successful response
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/GetStorageFacilityDetailResponse'
                "400":
                    description: Validation error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ValidationError'
                default:
                    description: Error response
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/Error'
    /api/supply-chain/v1/list-fuel-shortages:
        get:
            tags:
                - SupplyChainService
            summary: ListFuelShortages
            description: |-
                ListFuelShortages returns the global fuel-shortage alert registry.
                 Curated-only: severity ("confirmed" | "watch") is a row field authored
                 at curation time, not a client-side derivation. Free tier.
            operationId: ListFuelShortages
            parameters:
                - name: country
                  in: query
                  description: Filter to one ISO 3166-1 alpha-2 country. Omit for global.
                  required: false
                  schema:
                    type: string
                - name: product
                  in: query
                  description: |-
                    Filter to one product. Accepts: "petrol" | "diesel" | "jet" | "heating_oil".
                     Omit for all products.
                  required: false
                  schema:
                    type: string
                - name: severity
                  in: query
                  description: 'Filter to one severity. Accepts: "confirmed" | "watch". Omit for both.'
                  required: false
                  schema:
                    type: string
            responses:
                "200":
                    description: Successful response
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ListFuelShortagesResponse'
                "400":
                    description: Validation error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ValidationError'
                default:
                    description: Error response
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/Error'
    /api/supply-chain/v1/get-fuel-shortage-detail:
        get:
            tags:
                - SupplyChainService
            summary: GetFuelShortageDetail
            description: |-
                GetFuelShortageDetail returns a single shortage with full evidence
                 bundle and citation timeline. Loaded lazily on drawer open.
            operationId: GetFuelShortageDetail
            parameters:
                - name: shortageId
                  in: query
                  required: false
                  schema:
                    type: string
            responses:
                "200":
                    description: Successful response
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/GetFuelShortageDetailResponse'
                "400":
                    description: Validation error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ValidationError'
                default:
                    description: Error response
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/Error'
    /api/supply-chain/v1/list-energy-disruptions:
        get:
            tags:
                - SupplyChainService
            summary: ListEnergyDisruptions
            description: |-
                ListEnergyDisruptions returns the disruption event log for pipelines
                 and storage facilities. Supports per-asset or per-asset-type filtering
                 so panel drawers can fetch a scoped timeline without pulling the
                 full registry.
            operationId: ListEnergyDisruptions
            parameters:
                - name: assetId
                  in: query
                  description: |-
                    Filter to one asset. Omit for all. When set, also narrows to the
                     matching asset_type if provided.
                  required: false
                  schema:
                    type: string
                - name: assetType
                  in: query
                  description: 'Filter to one asset type. Accepts: "pipeline" | "storage".'
                  required: false
                  schema:
                    type: string
                - name: ongoingOnly
                  in: query
                  description: If true, only return events with endAt empty (still ongoing).
                  required: false
                  schema:
                    type: boolean
            responses:
                "200":
                    description: Successful response
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ListEnergyDisruptionsResponse'
                "400":
                    description: Validation error
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/ValidationError'
                default:
                    description: Error response
                    content:
                        application/json:
                            schema:
                                $ref: '#/components/schemas/Error'
components:
    schemas:
        Error:
            type: object
            properties:
                message:
                    type: string
                    description: Error message (e.g., 'user not found', 'database connection failed')
            description: Error is returned when a handler encounters an error. It contains a simple error message that the developer can customize.
        FieldViolation:
            type: object
            properties:
                field:
                    type: string
                    description: The field path that failed validation (e.g., 'user.email' for nested fields). For header validation, this will be the header name (e.g., 'X-API-Key')
                description:
                    type: string
                    description: Human-readable description of the validation violation (e.g., 'must be a valid email address', 'required field missing')
            required:
                - field
                - description
            description: FieldViolation describes a single validation error for a specific field.
        ValidationError:
            type: object
            properties:
                violations:
                    type: array
                    items:
                        $ref: '#/components/schemas/FieldViolation'
                    description: List of validation violations
            required:
                - violations
            description: ValidationError is returned when request validation fails. It contains a list of field violations describing what went wrong.
        GetShippingRatesRequest:
            type: object
        GetShippingRatesResponse:
            type: object
            properties:
                indices:
                    type: array
                    items:
                        $ref: '#/components/schemas/ShippingIndex'
                fetchedAt:
                    type: string
                upstreamUnavailable:
                    type: boolean
        ShippingIndex:
            type: object
            properties:
                indexId:
                    type: string
                name:
                    type: string
                currentValue:
                    type: number
                    format: double
                previousValue:
                    type: number
                    format: double
                changePct:
                    type: number
                    format: double
                unit:
                    type: string
                history:
                    type: array
                    items:
                        $ref: '#/components/schemas/ShippingRatePoint'
                spikeAlert:
                    type: boolean
        ShippingRatePoint:
            type: object
            properties:
                date:
                    type: string
                value:
                    type: number
                    format: double
        GetChokepointStatusRequest:
            type: object
        GetChokepointStatusResponse:
            type: object
            properties:
                chokepoints:
                    type: array
                    items:
                        $ref: '#/components/schemas/ChokepointInfo'
                fetchedAt:
                    type: string
                upstreamUnavailable:
                    type: boolean
        ChokepointInfo:
            type: object
            properties:
                id:
                    type: string
                name:
                    type: string
                lat:
                    type: number
                    format: double
                lon:
                    type: number
                    format: double
                disruptionScore:
                    type: integer
                    format: int32
                status:
                    type: string
                activeWarnings:
                    type: integer
                    format: int32
                congestionLevel:
                    type: string
                affectedRoutes:
                    type: array
                    items:
                        type: string
                description:
                    type: string
                aisDisruptions:
                    type: integer
                    format: int32
                directions:
                    type: array
                    items:
                        type: string
                directionalDwt:
                    type: array
                    items:
                        $ref: '#/components/schemas/DirectionalDwt'
                transitSummary:
                    $ref: '#/components/schemas/TransitSummary'
                flowEstimate:
                    $ref: '#/components/schemas/FlowEstimate'
                warRiskTier:
                    type: string
                    enum:
                        - WAR_RISK_TIER_UNSPECIFIED
                        - WAR_RISK_TIER_NORMAL
                        - WAR_RISK_TIER_ELEVATED
                        - WAR_RISK_TIER_HIGH
                        - WAR_RISK_TIER_CRITICAL
                        - WAR_RISK_TIER_WAR_ZONE
                    description: |-
                        *
                         War risk tier derived from Lloyd's JWC Listed Areas + OSINT threat classification.
                         This is a FREE field (no PRO gate) — it exposes the existing server-internal
                         threatLevel from ChokepointConfig, making it available to clients for badges
                         and bypass corridor scoring.
        DirectionalDwt:
            type: object
            properties:
                direction:
                    type: string
                dwtThousandTonnes:
                    type: number
                    format: double
                wowChangePct:
                    type: number
                    format: double
        TransitSummary:
            type: object
            properties:
                todayTotal:
                    type: integer
                    format: int32
                todayTanker:
                    type: integer
                    format: int32
                todayCargo:
                    type: integer
                    format: int32
                todayOther:
                    type: integer
                    format: int32
                wowChangePct:
                    type: number
                    format: double
                history:
                    type: array
                    items:
                        $ref: '#/components/schemas/TransitDayCount'
                riskLevel:
                    type: string
                incidentCount7d:
                    type: integer
                    format: int32
                disruptionPct:
                    type: number
                    format: double
                riskSummary:
                    type: string
                riskReportAction:
                    type: string
                dataAvailable:
                    type: boolean
                    description: |-
                        False when the upstream portwatch/relay source did not return data for
                         this chokepoint in the current cycle — the summary fields are zero-state
                         fill, not a genuine "zero traffic" reading. Client should render a
                         "transit data unavailable" indicator and skip stat/chart rendering.
        TransitDayCount:
            type: object
            properties:
                date:
                    type: string
                tanker:
                    type: integer
                    format: int32
                cargo:
                    type: integer
                    format: int32
                other:
                    type: integer
                    format: int32
                total:
                    type: integer
                    format: int32
                container:
                    type: integer
                    format: int32
                dryBulk:
                    type: integer
                    format: int32
                generalCargo:
                    type: integer
                    format: int32
                roro:
                    type: integer
                    format: int32
                capContainer:
                    type: number
                    format: double
                capDryBulk:
                    type: number
                    format: double
                capGeneralCargo:
                    type: number
                    format: double
                capRoro:
                    type: number
                    format: double
                capTanker:
                    type: number
                    format: double
        FlowEstimate:
            type: object
            properties:
                currentMbd:
                    type: number
                    format: double
                baselineMbd:
                    type: number
                    format: double
                flowRatio:
                    type: number
                    format: double
                disrupted:
                    type: boolean
                source:
                    type: string
                hazardAlertLevel:
                    type: string
                hazardAlertName:
                    type: string
        GetChokepointHistoryRequest:
            type: object
            properties:
                chokepointId:
                    type: string
            required:
                - chokepointId
            description: |-
                GetChokepointHistory returns the transit-count history for a single
                 chokepoint. Loaded lazily on card expand so the main chokepoint-status
                 response can stay compact (no 180-day history per chokepoint).
        GetChokepointHistoryResponse:
            type: object
            properties:
                chokepointId:
                    type: string
                history:
                    type: array
                    items:
                        $ref: '#/components/schemas/TransitDayCount'
                fetchedAt:
                    type: string
                    format: int64
        GetCriticalMineralsRequest:
            type: object
        GetCriticalMineralsResponse:
            type: object
            properties:
                minerals:
                    type: array
                    items:
                        $ref: '#/components/schemas/CriticalMineral'
                fetchedAt:
                    type: string
                upstreamUnavailable:
                    type: boolean
        CriticalMineral:
            type: object
            properties:
                mineral:
                    type: string
                topProducers:
                    type: array
                    items:
                        $ref: '#/components/schemas/MineralProducer'
                hhi:
                    type: number
                    format: double
                riskRating:
                    type: string
                globalProduction:
                    type: number
                    format: double
                unit:
                    type: string
        MineralProducer:
            type: object
            properties:
                country:
                    type: string
                countryCode:
                    type: string
                productionTonnes:
                    type: number
                    format: double
                sharePct:
                    type: number
                    format: double
        GetShippingStressRequest:
            type: object
        GetShippingStressResponse:
            type: object
            properties:
                carriers:
                    type: array
                    items:
                        $ref: '#/components/schemas/ShippingStressCarrier'
                stressScore:
                    type: number
                    format: double
                    description: Composite stress score 0–100 (higher = more disruption).
                stressLevel:
                    type: string
                    description: '"low" | "moderate" | "elevated" | "critical".'
                fetchedAt:
                    type: integer
                    format: int64
                    description: 'Warning: Values > 2^53 may lose precision in JavaScript'
                upstreamUnavailable:
                    type: boolean
                    description: Set to true when upstream data source is unavailable and cached data is stale.
        ShippingStressCarrier:
            type: object
            properties:
                symbol:
                    type: string
                    description: Ticker or identifier (e.g., "BDRY", "ZIM").
                name:
                    type: string
                    description: Human-readable name.
                price:
                    type: number
                    format: double
                    description: Current price.
                changePct:
                    type: number
                    format: double
                    description: Percentage change from previous close.
                carrierType:
                    type: string
                    description: 'Carrier type: "etf" | "carrier" | "index".'
                sparkline:
                    type: array
                    items:
                        type: number
                        format: double
                        description: 30-day price sparkline.
            description: ShippingStressCarrier represents market stress data for a carrier or shipping index.
        GetCountryChokepointIndexRequest:
            type: object
            properties:
                iso2:
                    type: string
                    pattern: ^[A-Z]{2}$
                    description: ISO 3166-1 alpha-2 country code (uppercase).
                hs2:
                    type: string
                    description: HS2 chapter (2-digit string). Defaults to "27" (energy/mineral fuels) when absent.
            required:
                - iso2
            description: GetCountryChokepointIndexRequest specifies the country and optional HS2 chapter.
        GetCountryChokepointIndexResponse:
            type: object
            properties:
                iso2:
                    type: string
                    description: ISO 3166-1 alpha-2 country code echoed from the request.
                hs2:
                    type: string
                    description: HS2 chapter used for the computation.
                exposures:
                    type: array
                    items:
                        $ref: '#/components/schemas/ChokepointExposureEntry'
                primaryChokepointId:
                    type: string
                    description: Canonical ID of the chokepoint with the highest exposure score.
                vulnerabilityIndex:
                    type: number
                    format: double
                    description: Composite vulnerability index 0–100 (weighted sum of top-3 exposures).
                fetchedAt:
                    type: string
                    description: ISO timestamp of when this data was last seeded.
            description: GetCountryChokepointIndexResponse returns exposure scores for all relevant chokepoints.
        ChokepointExposureEntry:
            type: object
            properties:
                chokepointId:
                    type: string
                    description: Canonical chokepoint ID from the chokepoint registry.
                chokepointName:
                    type: string
                    description: Human-readable chokepoint name.
                exposureScore:
                    type: number
                    format: double
                    description: Exposure score 0–100; higher = more dependent on this chokepoint.
                coastSide:
                    type: string
                    description: Which ocean/basin side the country's ports face (atlantic, pacific, indian, med, multi, landlocked).
                shockSupported:
                    type: boolean
                    description: Whether the shock model is supported for this chokepoint + hs2 combination.
            description: ChokepointExposureEntry holds per-chokepoint exposure data for a country.
        GetBypassOptionsRequest:
            type: object
            properties:
                chokepointId:
                    type: string
                cargoType:
                    type: string
                    description: 'container | tanker | bulk | roro  (default: "container")'
                closurePct:
                    type: integer
                    format: int32
                    description: '0-100, percent of capacity blocked (default: 100)'
            required:
                - chokepointId
        GetBypassOptionsResponse:
            type: object
            properties:
                chokepointId:
                    type: string
                cargoType:
                    type: string
                closurePct:
                    type: integer
                    format: int32
                options:
                    type: array
                    items:
                        $ref: '#/components/schemas/BypassOption'
                fetchedAt:
                    type: string
                primaryChokepointWarRiskTier:
                    type: string
                    enum:
                        - WAR_RISK_TIER_UNSPECIFIED
                        - WAR_RISK_TIER_NORMAL
                        - WAR_RISK_TIER_ELEVATED
                        - WAR_RISK_TIER_HIGH
                        - WAR_RISK_TIER_CRITICAL
                        - WAR_RISK_TIER_WAR_ZONE
                    description: |-
                        *
                         War risk tier derived from Lloyd's JWC Listed Areas + OSINT threat classification.
                         This is a FREE field (no PRO gate) — it exposes the existing server-internal
                         threatLevel from ChokepointConfig, making it available to clients for badges
                         and bypass corridor scoring.
        BypassOption:
            type: object
            properties:
                id:
                    type: string
                name:
                    type: string
                type:
                    type: string
                addedTransitDays:
                    type: integer
                    format: int32
                addedCostMultiplier:
                    type: number
                    format: double
                capacityConstraintTonnage:
                    type: string
                    format: int64
                suitableCargoTypes:
                    type: array
                    items:
                        type: string
                activationThreshold:
                    type: string
                waypointChokepointIds:
                    type: array
                    items:
                        type: string
                liveScore:
                    type: number
                    format: double
                bypassWarRiskTier:
                    type: string
                    enum:
                        - WAR_RISK_TIER_UNSPECIFIED
                        - WAR_RISK_TIER_NORMAL
                        - WAR_RISK_TIER_ELEVATED
                        - WAR_RISK_TIER_HIGH
                        - WAR_RISK_TIER_CRITICAL
                        - WAR_RISK_TIER_WAR_ZONE
                    description: |-
                        *
                         War risk tier derived from Lloyd's JWC Listed Areas + OSINT threat classification.
                         This is a FREE field (no PRO gate) — it exposes the existing server-internal
                         threatLevel from ChokepointConfig, making it available to clients for badges
                         and bypass corridor scoring.
                notes:
                    type: string
        GetCountryCostShockRequest:
            type: object
            properties:
                iso2:
                    type: string
                    pattern: ^[A-Z]{2}$
                chokepointId:
                    type: string
                hs2:
                    type: string
                    description: 'HS2 chapter (default: "27")'
            required:
                - iso2
                - chokepointId
        GetCountryCostShockResponse:
            type: object
            properties:
                iso2:
                    type: string
                chokepointId:
                    type: string
                hs2:
                    type: string
                supplyDeficitPct:
                    type: number
                    format: double
                    description: Average refined-product supply deficit % under full closure (Gasoline/Diesel/Jet fuel/LPG average; HS 27 only)
                coverageDays:
                    type: integer
                    format: int32
                    description: Energy stockpile coverage in days (IEA data, HS 27 only; 0 for non-energy sectors or net exporters)
                warRiskPremiumBps:
                    type: integer
                    format: int32
                    description: War risk insurance premium in basis points for this chokepoint
                warRiskTier:
                    type: string
                    enum:
                        - WAR_RISK_TIER_UNSPECIFIED
                        - WAR_RISK_TIER_NORMAL
                        - WAR_RISK_TIER_ELEVATED
                        - WAR_RISK_TIER_HIGH
                        - WAR_RISK_TIER_CRITICAL
                        - WAR_RISK_TIER_WAR_ZONE
                    description: |-
                        *
                         War risk tier derived from Lloyd's JWC Listed Areas + OSINT threat classification.
                         This is a FREE field (no PRO gate) — it exposes the existing server-internal
                         threatLevel from ChokepointConfig, making it available to clients for badges
                         and bypass corridor scoring.
                hasEnergyModel:
                    type: boolean
                    description: Whether supply_deficit_pct and coverage_days are modelled (true) or unavailable (false)
                unavailableReason:
                    type: string
                    description: Null/unavailable explanation for non-energy sectors
                fetchedAt:
                    type: string
        GetCountryProductsRequest:
            type: object
            properties:
                iso2:
                    type: string
                    pattern: ^[A-Z]{2}$
            required:
                - iso2
        GetCountryProductsResponse:
            type: object
            properties:
                iso2:
                    type: string
                products:
                    type: array
                    items:
                        $ref: '#/components/schemas/CountryProduct'
                fetchedAt:
                    type: string
                    description: ISO timestamp from the seeded payload (empty when no data is cached).
        CountryProduct:
            type: object
            properties:
                hs4:
                    type: string
                description:
                    type: string
                totalValue:
                    type: number
                    format: double
                topExporters:
                    type: array
                    items:
                        $ref: '#/components/schemas/ProductExporter'
                year:
                    type: integer
                    format: int32
        ProductExporter:
            type: object
            properties:
                partnerCode:
                    type: integer
                    format: int32
                partnerIso2:
                    type: string
                value:
                    type: number
                    format: double
                share:
                    type: number
                    format: double
        GetMultiSectorCostShockRequest:
            type: object
            properties:
                iso2:
                    type: string
                    pattern: ^[A-Z]{2}$
                chokepointId:
                    type: string
                closureDays:
                    type: integer
                    format: int32
                    description: Closure-window duration in days. Server clamps to [1, 365]. Defaults to 30.
            required:
                - iso2
                - chokepointId
        GetMultiSectorCostShockResponse:
            type: object
            properties:
                iso2:
                    type: string
                chokepointId:
                    type: string
                closureDays:
                    type: integer
                    format: int32
                    description: Server-clamped closure-window duration in days (1-365).
                warRiskTier:
                    type: string
                    enum:
                        - WAR_RISK_TIER_UNSPECIFIED
                        - WAR_RISK_TIER_NORMAL
                        - WAR_RISK_TIER_ELEVATED
                        - WAR_RISK_TIER_HIGH
                        - WAR_RISK_TIER_CRITICAL
                        - WAR_RISK_TIER_WAR_ZONE
                    description: |-
                        *
                         War risk tier derived from Lloyd's JWC Listed Areas + OSINT threat classification.
                         This is a FREE field (no PRO gate) — it exposes the existing server-internal
                         threatLevel from ChokepointConfig, making it available to clients for badges
                         and bypass corridor scoring.
                sectors:
                    type: array
                    items:
                        $ref: '#/components/schemas/MultiSectorCostShock'
                totalAddedCost:
                    type: number
                    format: double
                    description: Sum of total_cost_shock across all sectors.
                fetchedAt:
                    type: string
                unavailableReason:
                    type: string
                    description: Populated when no seeded import data is available for the country.
        MultiSectorCostShock:
            type: object
            properties:
                hs2:
                    type: string
                    description: HS2 chapter code (e.g. "27" mineral fuels, "85" electronics).
                hs2Label:
                    type: string
                    description: Friendly chapter label (e.g. "Energy", "Electronics").
                importValueAnnual:
                    type: number
                    format: double
                    description: Total annual import value (USD) for this sector.
                freightAddedPctPerTon:
                    type: number
                    format: double
                    description: Bypass-corridor freight uplift fraction (0.10 == +10% per ton).
                warRiskPremiumBps:
                    type: integer
                    format: int32
                    description: War-risk insurance premium (basis points) sourced from the chokepoint tier.
                addedTransitDays:
                    type: integer
                    format: int32
                    description: Bypass-corridor transit penalty (informational).
                totalCostShockPerDay:
                    type: number
                    format: double
                totalCostShock30Days:
                    type: number
                    format: double
                totalCostShock90Days:
                    type: number
                    format: double
                totalCostShock:
                    type: number
                    format: double
                    description: Cost for the requested closure_days window.
                closureDays:
                    type: integer
                    format: int32
                    description: Echoes the clamped closure duration used for total_cost_shock (1-365).
        GetSectorDependencyRequest:
            type: object
            properties:
                iso2:
                    type: string
                    pattern: ^[A-Z]{2}$
                hs2:
                    type: string
                    description: HS2 chapter code, e.g. "27" (mineral fuels), "85" (electronics)
            required:
                - iso2
                - hs2
        GetSectorDependencyResponse:
            type: object
            properties:
                iso2:
                    type: string
                hs2:
                    type: string
                hs2Label:
                    type: string
                    description: Human-readable HS2 chapter name.
                flags:
                    type: array
                    items:
                        type: string
                        enum:
                            - DEPENDENCY_FLAG_UNSPECIFIED
                            - DEPENDENCY_FLAG_SINGLE_SOURCE_CRITICAL
                            - DEPENDENCY_FLAG_SINGLE_CORRIDOR_CRITICAL
                            - DEPENDENCY_FLAG_COMPOUND_RISK
                            - DEPENDENCY_FLAG_DIVERSIFIABLE
                        description: DependencyFlag classifies how a country+sector dependency can fail.
                primaryExporterIso2:
                    type: string
                    description: ISO2 of the country supplying the largest share of this sector's imports.
                primaryExporterShare:
                    type: number
                    format: double
                    description: Share of imports from the primary exporter (0–1). 0 = no Comtrade data available.
                primaryChokepointId:
                    type: string
                    description: Chokepoint ID with the highest exposure score for this country+sector.
                primaryChokepointExposure:
                    type: number
                    format: double
                    description: Exposure score for the primary chokepoint (0–100).
                hasViableBypass:
                    type: boolean
                    description: Whether at least one viable bypass corridor exists for the primary chokepoint.
                fetchedAt:
                    type: string
        GetRouteExplorerLaneRequest:
            type: object
            properties:
                fromIso2:
                    type: string
                    pattern: ^[A-Z]{2}$
                toIso2:
                    type: string
                    pattern: ^[A-Z]{2}$
                hs2:
                    type: string
                    description: HS2 chapter code, e.g. "27", "85"
                cargoType:
                    type: string
                    description: 'One of: container, tanker, bulk, roro'
            required:
                - fromIso2
                - toIso2
                - hs2
                - cargoType
        GetRouteExplorerLaneResponse:
            type: object
            properties:
                fromIso2:
                    type: string
                toIso2:
                    type: string
                hs2:
                    type: string
                cargoType:
                    type: string
                primaryRouteId:
                    type: string
                    description: Primary trade route ID from TRADE_ROUTES config. Empty when no modeled lane.
                primaryRouteGeometry:
                    type: array
                    items:
                        $ref: '#/components/schemas/GeoPoint'
                chokepointExposures:
                    type: array
                    items:
                        $ref: '#/components/schemas/ChokepointExposureSummary'
                bypassOptions:
                    type: array
                    items:
                        $ref: '#/components/schemas/BypassCorridorOption'
                warRiskTier:
                    type: string
                disruptionScore:
                    type: number
                    format: double
                estTransitDaysRange:
                    $ref: '#/components/schemas/NumberRange'
                estFreightUsdPerTeuRange:
                    $ref: '#/components/schemas/NumberRange'
                noModeledLane:
                    type: boolean
                    description: |-
                        True when the wrapper fell back to the origin's first route (no shared route
                         between origin and destination clusters). Signals "no modeled lane" to the UI.
                fetchedAt:
                    type: string
        GeoPoint:
            type: object
            properties:
                lon:
                    type: number
                    format: double
                lat:
                    type: number
                    format: double
            description: GeoPoint is a [longitude, latitude] pair.
        ChokepointExposureSummary:
            type: object
            properties:
                chokepointId:
                    type: string
                chokepointName:
                    type: string
                exposurePct:
                    type: integer
                    format: int32
        BypassCorridorOption:
            type: object
            properties:
                id:
                    type: string
                name:
                    type: string
                type:
                    type: string
                addedTransitDays:
                    type: integer
                    format: int32
                addedCostMultiplier:
                    type: number
                    format: double
                warRiskTier:
                    type: string
                status:
                    type: string
                    enum:
                        - CORRIDOR_STATUS_UNSPECIFIED
                        - CORRIDOR_STATUS_ACTIVE
                        - CORRIDOR_STATUS_PROPOSED
                        - CORRIDOR_STATUS_UNAVAILABLE
                    description: |-
                        Status of a bypass corridor for UI labeling. "active" means usable today;
                         "proposed" means documented but not yet built/operational; "unavailable"
                         means blockaded or otherwise blocked from use.
                fromPort:
                    $ref: '#/components/schemas/GeoPoint'
                toPort:
                    $ref: '#/components/schemas/GeoPoint'
            description: |-
                BypassCorridorOption is a single enriched bypass corridor for the Route Explorer UI.
                 Includes coordinate endpoints so the client can call MapContainer.setBypassRoutes
                 directly without any client-side geometry lookup.
        NumberRange:
            type: object
            properties:
                min:
                    type: integer
                    format: int32
                max:
                    type: integer
                    format: int32
            description: Inclusive integer range for transit days / freight USD estimates.
        GetRouteImpactRequest:
            type: object
            properties:
                fromIso2:
                    type: string
                    pattern: ^[A-Z]{2}$
                toIso2:
                    type: string
                    pattern: ^[A-Z]{2}$
                hs2:
                    type: string
            required:
                - fromIso2
                - toIso2
                - hs2
        GetRouteImpactResponse:
            type: object
            properties:
                laneValueUsd:
                    type: number
                    format: double
                primaryExporterIso2:
                    type: string
                primaryExporterShare:
                    type: number
                    format: double
                topStrategicProducts:
                    type: array
                    items:
                        $ref: '#/components/schemas/StrategicProduct'
                resilienceScore:
                    type: number
                    format: double
                dependencyFlags:
                    type: array
                    items:
                        type: string
                        enum:
                            - DEPENDENCY_FLAG_UNSPECIFIED
                            - DEPENDENCY_FLAG_SINGLE_SOURCE_CRITICAL
                            - DEPENDENCY_FLAG_SINGLE_CORRIDOR_CRITICAL
                            - DEPENDENCY_FLAG_COMPOUND_RISK
                            - DEPENDENCY_FLAG_DIVERSIFIABLE
                        description: DependencyFlag classifies how a country+sector dependency can fail.
                hs2InSeededUniverse:
                    type: boolean
                comtradeSource:
                    type: string
                fetchedAt:
                    type: string
        StrategicProduct:
            type: object
            properties:
                hs4:
                    type: string
                label:
                    type: string
                totalValueUsd:
                    type: number
                    format: double
                topExporterIso2:
                    type: string
                topExporterShare:
                    type: number
                    format: double
                primaryChokepointId:
                    type: string
        ListPipelinesRequest:
            type: object
            properties:
                commodityType:
                    type: string
                    description: Filter to one commodity. Omit (or pass empty) to receive both.
            description: |-
                ListPipelines returns the full oil and/or gas pipeline registry with
                 evidence-based status per asset. Registry is curated (see
                 docs/methodology/pipelines.mdx) and refreshed weekly by
                 scripts/seed-pipelines-{gas,oil}.mjs. Typical consumer: PipelineStatusPanel
                 rendering the DeckGL PathLayer.

                 The public badge is DERIVED from the evidence bundle server-side at
                 read time — callers MUST use `public_badge` for display, not invent
                 their own derivation from `evidence` fields. This keeps the atlas and
                 MCP clients consistent and versioned together.
        ListPipelinesResponse:
            type: object
            properties:
                pipelines:
                    type: array
                    items:
                        $ref: '#/components/schemas/PipelineEntry'
                fetchedAt:
                    type: string
                classifierVersion:
                    type: string
                upstreamUnavailable:
                    type: boolean
        PipelineEntry:
            type: object
            properties:
                id:
                    type: string
                name:
                    type: string
                operator:
                    type: string
                commodityType:
                    type: string
                    description: Either "gas" or "oil".
                fromCountry:
                    type: string
                toCountry:
                    type: string
                transitCountries:
                    type: array
                    items:
                        type: string
                capacityBcmYr:
                    type: number
                    format: double
                capacityMbd:
                    type: number
                    format: double
                lengthKm:
                    type: integer
                    format: int32
                inService:
                    type: integer
                    format: int32
                startPoint:
                    $ref: '#/components/schemas/LatLon'
                endPoint:
                    $ref: '#/components/schemas/LatLon'
                waypoints:
                    type: array
                    items:
                        $ref: '#/components/schemas/LatLon'
                evidence:
                    $ref: '#/components/schemas/PipelineEvidence'
                publicBadge:
                    type: string
                    description: |-
                        Server-derived public badge. One of:
                           "flowing" | "reduced" | "offline" | "disputed"
        LatLon:
            type: object
            properties:
                lat:
                    type: number
                    format: double
                lon:
                    type: number
                    format: double
        PipelineEvidence:
            type: object
            properties:
                physicalState:
                    type: string
                    description: 'One of: "flowing" | "reduced" | "offline" | "unknown"'
                physicalStateSource:
                    type: string
                    description: 'One of: "operator" | "regulator" | "press" | "satellite" | "ais-relay"'
                operatorStatement:
                    $ref: '#/components/schemas/OperatorStatement'
                commercialState:
                    type: string
                    description: 'One of: "under_contract" | "expired" | "suspended" | "unknown"'
                sanctionRefs:
                    type: array
                    items:
                        $ref: '#/components/schemas/SanctionRef'
                lastEvidenceUpdate:
                    type: string
                classifierVersion:
                    type: string
                classifierConfidence:
                    type: number
                    format: double
        OperatorStatement:
            type: object
            properties:
                text:
                    type: string
                url:
                    type: string
                date:
                    type: string
        SanctionRef:
            type: object
            properties:
                authority:
                    type: string
                listId:
                    type: string
                date:
                    type: string
                url:
                    type: string
        GetPipelineDetailRequest:
            type: object
            properties:
                pipelineId:
                    type: string
            required:
                - pipelineId
            description: |-
                GetPipelineDetail returns a single pipeline with its full evidence bundle.
                 Evidence surface here is richer than ListPipelinesResponse — the list view
                 is designed for the map layer's compact shape; detail is designed for the
                 click-through drawer.
        GetPipelineDetailResponse:
            type: object
            properties:
                pipeline:
                    $ref: '#/components/schemas/PipelineEntry'
                revisions:
                    type: array
                    items:
                        $ref: '#/components/schemas/PipelineRevisionEntry'
                fetchedAt:
                    type: string
                unavailable:
                    type: boolean
        PipelineRevisionEntry:
            type: object
            properties:
                date:
                    type: string
                fieldChanged:
                    type: string
                previousValue:
                    type: string
                newValue:
                    type: string
                trigger:
                    type: string
                    description: 'One of: "classifier" | "source" | "decay" | "override"'
                sourcesUsed:
                    type: array
                    items:
                        type: string
                classifierVersion:
                    type: string
        ListStorageFacilitiesRequest:
            type: object
            properties:
                facilityType:
                    type: string
                    description: |-
                        Filter to one facility type. Accepts:
                           "ugs" | "spr" | "lng_export" | "lng_import" | "crude_tank_farm"
                         Omit (or pass empty) to receive all types.
            description: |-
                ListStorageFacilities returns the curated strategic storage registry
                 (underground gas storage, strategic petroleum reserves, LNG terminals,
                 crude tank farms) with evidence-based status per asset. Registry is
                 curated (see docs/methodology/storage.mdx) and refreshed weekly by
                 scripts/seed-storage-facilities.mjs. Typical consumer:
                 StorageFacilityMapPanel rendering the DeckGL ScatterplotLayer.

                 Like pipelines, the public badge is DERIVED from the evidence bundle
                 server-side at read time. Callers MUST use `public_badge` for display
                 rather than inventing their own derivation from `evidence` fields, so
                 the atlas and MCP clients stay consistent and versioned together.
        ListStorageFacilitiesResponse:
            type: object
            properties:
                facilities:
                    type: array
                    items:
                        $ref: '#/components/schemas/StorageFacilityEntry'
                fetchedAt:
                    type: string
                classifierVersion:
                    type: string
                upstreamUnavailable:
                    type: boolean
        StorageFacilityEntry:
            type: object
            properties:
                id:
                    type: string
                name:
                    type: string
                operator:
                    type: string
                facilityType:
                    type: string
                    description: 'One of: "ugs" | "spr" | "lng_export" | "lng_import" | "crude_tank_farm"'
                country:
                    type: string
                location:
                    $ref: '#/components/schemas/StorageLatLon'
                capacityTwh:
                    type: number
                    format: double
                    description: |-
                        Working capacity in the facility's native unit (see working_capacity_unit).
                         Exactly ONE of these is populated per facility, chosen by facility_type:
                           ugs                              → capacity_twh
                           spr, crude_tank_farm             → capacity_mb
                           lng_export, lng_import           → capacity_mtpa
                capacityMb:
                    type: number
                    format: double
                capacityMtpa:
                    type: number
                    format: double
                workingCapacityUnit:
                    type: string
                    description: 'One of: "TWh" | "Mb" | "Mtpa"'
                inService:
                    type: integer
                    format: int32
                evidence:
                    $ref: '#/components/schemas/StorageEvidence'
                publicBadge:
                    type: string
                    description: |-
                        Server-derived public badge. One of:
                           "operational" | "reduced" | "offline" | "disputed"
        StorageLatLon:
            type: object
            properties:
                lat:
                    type: number
                    format: double
                lon:
                    type: number
                    format: double
        StorageEvidence:
            type: object
            properties:
                physicalState:
                    type: string
                    description: 'One of: "operational" | "reduced" | "offline" | "under_construction" | "unknown"'
                physicalStateSource:
                    type: string
                    description: 'One of: "operator" | "regulator" | "press" | "satellite" | "ais-relay"'
                operatorStatement:
                    $ref: '#/components/schemas/StorageOperatorStatement'
                commercialState:
                    type: string
                    description: 'One of: "under_contract" | "expired" | "suspended" | "unknown"'
                sanctionRefs:
                    type: array
                    items:
                        $ref: '#/components/schemas/StorageSanctionRef'
                fillDisclosed:
                    type: boolean
                    description: |-
                        Whether working-gas / stock-level fill is publicly disclosed for this
                         asset. LNG export terminals, for instance, tend NOT to disclose; UGS
                         sites in Europe are required to disclose via GIE AGSI+.
                fillSource:
                    type: string
                    description: |-
                        Source of the disclosed fill series ("GIE AGSI+", "EIA SPR weekly
                         stock report", etc.). Empty when fill_disclosed=false.
                lastEvidenceUpdate:
                    type: string
                classifierVersion:
                    type: string
                classifierConfidence:
                    type: number
                    format: double
        StorageOperatorStatement:
            type: object
            properties:
                text:
                    type: string
                url:
                    type: string
                date:
                    type: string
        StorageSanctionRef:
            type: object
            properties:
                authority:
                    type: string
                listId:
                    type: string
                date:
                    type: string
                url:
                    type: string
        GetStorageFacilityDetailRequest:
            type: object
            properties:
                facilityId:
                    type: string
            required:
                - facilityId
            description: |-
                GetStorageFacilityDetail returns a single facility with its full evidence
                 bundle + revision log. Revisions land in Week 3 alongside the disruption
                 event log (Week 3 milestone — empty array in v1).
        GetStorageFacilityDetailResponse:
            type: object
            properties:
                facility:
                    $ref: '#/components/schemas/StorageFacilityEntry'
                revisions:
                    type: array
                    items:
                        $ref: '#/components/schemas/StorageFacilityRevisionEntry'
                fetchedAt:
                    type: string
                unavailable:
                    type: boolean
        StorageFacilityRevisionEntry:
            type: object
            properties:
                date:
                    type: string
                fieldChanged:
                    type: string
                previousValue:
                    type: string
                newValue:
                    type: string
                trigger:
                    type: string
                    description: 'One of: "classifier" | "source" | "decay" | "override"'
                sourcesUsed:
                    type: array
                    items:
                        type: string
                classifierVersion:
                    type: string
        ListFuelShortagesRequest:
            type: object
            properties:
                country:
                    type: string
                    description: Filter to one ISO 3166-1 alpha-2 country. Omit for global.
                product:
                    type: string
                    description: |-
                        Filter to one product. Accepts: "petrol" | "diesel" | "jet" | "heating_oil".
                         Omit for all products.
                severity:
                    type: string
                    description: 'Filter to one severity. Accepts: "confirmed" | "watch". Omit for both.'
            description: |-
                ListFuelShortages returns the global fuel-shortage alert registry.
                 Seeded from a curated JSON file (scripts/data/fuel-shortages.json).
                 An LLM classifier extension was scoped but not shipped — the registry
                 is curated-only today. Severity ("confirmed" or "watch") is a row
                 field authored at curation time; the evidence is shipped alongside
                 so agents and humans can audit the grounds for a severity label.

                 See docs/methodology/shortages.mdx for the evidence-threshold spec.
        ListFuelShortagesResponse:
            type: object
            properties:
                shortages:
                    type: array
                    items:
                        $ref: '#/components/schemas/FuelShortageEntry'
                fetchedAt:
                    type: string
                classifierVersion:
                    type: string
                upstreamUnavailable:
                    type: boolean
        FuelShortageEntry:
            type: object
            properties:
                id:
                    type: string
                country:
                    type: string
                product:
                    type: string
                    description: 'One of: "petrol" | "diesel" | "jet" | "heating_oil"'
                severity:
                    type: string
                    description: 'One of: "confirmed" | "watch"  (classifier output — not a client-side derivation)'
                firstSeen:
                    type: string
                lastConfirmed:
                    type: string
                resolvedAt:
                    type: string
                    description: Empty string when not yet resolved.
                impactTypes:
                    type: array
                    items:
                        type: string
                        description: |-
                            Observable consumer-facing impacts. Subset of:
                               "stations_closed" | "rationing" | "flights_cancelled" | "import_cut" | "price_spike"
                causeChain:
                    type: array
                    items:
                        type: string
                        description: |-
                            Contributing root causes, ordered primary-first. Subset of:
                               "upstream_refinery" | "logistics" | "policy" | "chokepoint" |
                               "sanction" | "war" | "import_cut"
                shortDescription:
                    type: string
                evidence:
                    $ref: '#/components/schemas/FuelShortageEvidence'
        FuelShortageEvidence:
            type: object
            properties:
                evidenceSources:
                    type: array
                    items:
                        $ref: '#/components/schemas/FuelShortageEvidenceSource'
                firstRegulatorConfirmation:
                    type: string
                    description: |-
                        ISO date of the first regulator confirmation, if any. Empty when
                         severity is "watch" on press-only signal.
                classifierVersion:
                    type: string
                classifierConfidence:
                    type: number
                    format: double
                lastEvidenceUpdate:
                    type: string
        FuelShortageEvidenceSource:
            type: object
            properties:
                authority:
                    type: string
                title:
                    type: string
                url:
                    type: string
                date:
                    type: string
                sourceType:
                    type: string
                    description: 'One of: "regulator" | "operator" | "press" | "ais-relay" | "satellite"'
        GetFuelShortageDetailRequest:
            type: object
            properties:
                shortageId:
                    type: string
            required:
                - shortageId
            description: |-
                GetFuelShortageDetail returns a single shortage with full evidence
                 bundle + citation timeline. Loaded lazily on drawer open.
        GetFuelShortageDetailResponse:
            type: object
            properties:
                shortage:
                    $ref: '#/components/schemas/FuelShortageEntry'
                fetchedAt:
                    type: string
                unavailable:
                    type: boolean
        ListEnergyDisruptionsRequest:
            type: object
            properties:
                assetId:
                    type: string
                    description: |-
                        Filter to one asset. Omit for all. When set, also narrows to the
                         matching asset_type if provided.
                assetType:
                    type: string
                    description: 'Filter to one asset type. Accepts: "pipeline" | "storage".'
                ongoingOnly:
                    type: boolean
                    description: If true, only return events with endAt empty (still ongoing).
            description: |-
                ListEnergyDisruptions returns the energy disruption event log — state
                 transitions for pipelines and storage facilities (sabotage, sanction,
                 maintenance, mechanical, weather, commercial, war). Each event ties
                 back to an assetId seeded by the pipeline or storage registry, so the
                 panel drawer can render an asset-scoped timeline without a second RPC.

                 Seeded from a curated JSON file (scripts/data/energy-disruptions.json).
                 An automated state-transition classifier was scoped but not shipped —
                 the log is curated-only today.

                 See docs/methodology/disruptions.mdx.
        ListEnergyDisruptionsResponse:
            type: object
            properties:
                events:
                    type: array
                    items:
                        $ref: '#/components/schemas/EnergyDisruptionEntry'
                fetchedAt:
                    type: string
                classifierVersion:
                    type: string
                upstreamUnavailable:
                    type: boolean
        EnergyDisruptionEntry:
            type: object
            properties:
                id:
                    type: string
                assetId:
                    type: string
                    description: Maps to a pipeline or storage-facility id seeded elsewhere.
                assetType:
                    type: string
                    description: 'One of: "pipeline" | "storage"'
                eventType:
                    type: string
                    description: |-
                        One of: "sabotage" | "sanction" | "maintenance" | "mechanical" |
                                 "weather" | "commercial" | "war" | "other"
                startAt:
                    type: string
                endAt:
                    type: string
                    description: Empty string when event is still ongoing.
                capacityOfflineBcmYr:
                    type: number
                    format: double
                    description: Headline-offline capacity (contextual — 0 when not applicable).
                capacityOfflineMbd:
                    type: number
                    format: double
                causeChain:
                    type: array
                    items:
                        type: string
                        description: Contributing causes, primary-first.
                shortDescription:
                    type: string
                sources:
                    type: array
                    items:
                        $ref: '#/components/schemas/EnergyDisruptionSource'
                classifierVersion:
                    type: string
                classifierConfidence:
                    type: number
                    format: double
                lastEvidenceUpdate:
                    type: string
        EnergyDisruptionSource:
            type: object
            properties:
                authority:
                    type: string
                title:
                    type: string
                url:
                    type: string
                date:
                    type: string
                sourceType:
                    type: string
