Files
worldmonitor/docs/api/SupplyChainService.openapi.json
Elie Habib 7c0c08ad89 feat(energy-atlas): seed-side countries[] denorm on disruptions + CountryDeepDive row (§R #5 = B) (#3377)
* feat(energy-atlas): seed-side countries[] denorm + CountryDeepDive row (§R #5 = B)

Per plan §R/#5 decision B: denormalise countries[] at seed time on each
disruption event so CountryDeepDivePanel can filter events per country
without an asset-registry round trip. Schema join (pipeline/storage
→ event.assetId) happens once in the weekly cron, not on every panel
render. The alternative (client-side join) was rejected because it
couples UI logic to asset-registry internals and duplicates the join
for every surface that wants a per-country filter.

Changes:
- `proto/.../list_energy_disruptions.proto`: add `repeated string
  countries = 15` to EnergyDisruptionEntry with doc comment tying it
  to the plan decision and the always-non-empty invariant.
- `scripts/_energy-disruption-registry.mjs`:
    • Load pipeline-gas + pipeline-oil + storage-facilities registries
      once per seed cycle; index by id.
    • `deriveCountriesForEvent()` resolves assetId to {fromCountry,
      toCountry, transitCountries} (pipeline) or {country} (storage),
      deduped + alpha-sorted so byte-diff stability holds.
    • `buildPayload()` attaches the computed countries[] to every
      event before writing.
    • `validateRegistry()` now requires non-empty countries[] of
      ISO2 codes. Combined with the seeder's `emptyDataIsFailure:
      true`, this surfaces orphaned assetIds loudly — the next cron
      tick fails validation and seed-meta stays stale, tripping
      health alarms.
- `scripts/data/energy-disruptions.json`: fix two orphaned assetIds
  that the new join caught:
    • `cpc-force-majeure-2022`: `cpc-pipeline` → `cpc` (matches the
      entry in pipelines-oil.json).
    • `pdvsa-designation-2019`: `ve-petrol-2026-q1` (non-existent) →
      `venezuela-anzoategui-puerto-la-cruz`.
- `server/.../list-energy-disruptions.ts`: project countries[] into
  the RPC response via coerceStringArray. Legacy pre-denorm rows
  surface as empty array (always present on wire, length 0 => old).
- `src/components/CountryDeepDivePanel.ts`: add 4th Atlas row —
  "Energy disruptions in {iso2}" — filtered by `iso2 ∈ countries[]`.
  Failure is silent; EnergyDisruptionsPanel (upcoming) is the
  primary disruption surface.
- `tests/energy-disruptions-registry.test.mts`: switch to validating
  the buildPayload output (post-denorm), add §R #5 B invariant
  tests, plus a raw-JSON invariant ensuring curators don't hand-edit
  countries[] (it's derived, not declared).

Proto regen note: `make generate` currently fails with a duplicate
openapi plugin collision in buf.gen.yaml (unrelated bug — 3 plugin
entries emit to the same out dir). Worked around by temporarily
trimming buf.gen.yaml to just the TS plugins for this regen. Added
only the `countries: string[]` wire field to both service_client and
service_server; no other generated-file drift in this PR.

* chore(proto): regenerate openapi specs for countries[] field

Runs `make generate` with the sebuf v0.11.1 plugin now correctly
resolved via the PATH fix (cherry-picked from fix/makefile-generate-path-prefix).
The new `countries` field on EnergyDisruptionEntry propagates into:

- docs/api/SupplyChainService.openapi.yaml (primary per-service spec)
- docs/api/SupplyChainService.openapi.json (machine-readable variant)
- docs/api/worldmonitor.openapi.yaml (consolidated bundle)

No TypeScript drift beyond the already-committed service_client.ts /
service_server.ts updates in 80797e7cc.

* fix(energy-atlas): drop highlightEventId emission (review P2)

Codex P2: loadDisruptionsForCountry dispatched `highlightEventId` but
neither PipelineStatusPanel nor StorageFacilityMapPanel consumes it
(the openDetailHandler reads only pipelineId / facilityId). The UI's
implicit promise (event-specific highlighting) wasn't delivered —
clickthrough was asset-generic, and the extra wire field was a
misleading API surface.

Fix: emit only {pipelineId, facilityId} in the dispatched detail.
Row click opens the asset drawer; user sees the full per-asset
disruption timeline and locates the event visually.

Symmetric fix for PR #3378's EnergyDisruptionsPanel — both emitters
now match the drawer contract exactly. Re-add `highlightEventId`
here when the drawer panels ship matching consumer code
(openDetailHandler accepts it, loadDetail stores it,
renderDisruptionTimeline scrolls + emphasises the matching event).

Typecheck clean, test:data 6698/6698 pass.

* fix(energy-atlas): collision detection + abort signal + label clamp (review P2)

Three Codex P2 findings on PR #3377:

1. `loadAssetRegistries()` spread-merged gas + oil pipelines, silently
   overwriting entries on id collision. No collision today, but a
   curator adding a pipeline under the same id to both files would
   cause `deriveCountriesForEvent` to return wrong-commodity country
   data with no test flagging it.

   Fix: explicit merge loop that throws on duplicate id. The next
   cron tick fails validation, seed-meta stays stale, health alarms
   fire — same loud-failure pattern the rest of the seeder uses.

2. `loadDisruptionsForCountry` didn't thread `this.signal` through
   the RPC fetch shim. The stale-closure guard (`currentCode !== iso2`)
   discarded stale RESULTS, but the in-flight request couldn't be
   cancelled when the user switched countries or closed the panel.

   Fix: wrap globalThis.fetch with { signal: this.signal } in the
   client factory, matching the signal lifecycle the rest of the
   panel already uses.

3. `shortDescription` values up to 200 chars rendered without
   ellipsis in the compact Atlas row, overflowing the row layout.

   Fix: new `truncateDisruptionLabel` helper clamps to 80 chars with
   ellipsis. Full text still accessible via click-through to the
   asset drawer.

Typecheck clean, test:data 6698/6698 pass.
2026-04-24 19:08:07 +04:00

1 line
56 KiB
JSON
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"components":{"schemas":{"BypassCorridorOption":{"description":"BypassCorridorOption is a single enriched bypass corridor for the Route Explorer UI.\n Includes coordinate endpoints so the client can call MapContainer.setBypassRoutes\n directly without any client-side geometry lookup.","properties":{"addedCostMultiplier":{"format":"double","type":"number"},"addedTransitDays":{"format":"int32","type":"integer"},"fromPort":{"$ref":"#/components/schemas/GeoPoint"},"id":{"type":"string"},"name":{"type":"string"},"status":{"description":"Status of a bypass corridor for UI labeling. \"active\" means usable today;\n \"proposed\" means documented but not yet built/operational; \"unavailable\"\n means blockaded or otherwise blocked from use.","enum":["CORRIDOR_STATUS_UNSPECIFIED","CORRIDOR_STATUS_ACTIVE","CORRIDOR_STATUS_PROPOSED","CORRIDOR_STATUS_UNAVAILABLE"],"type":"string"},"toPort":{"$ref":"#/components/schemas/GeoPoint"},"type":{"type":"string"},"warRiskTier":{"type":"string"}},"type":"object"},"BypassOption":{"properties":{"activationThreshold":{"type":"string"},"addedCostMultiplier":{"format":"double","type":"number"},"addedTransitDays":{"format":"int32","type":"integer"},"bypassWarRiskTier":{"description":"*\n War risk tier derived from Lloyd's JWC Listed Areas + OSINT threat classification.\n This is a FREE field (no PRO gate) — it exposes the existing server-internal\n threatLevel from ChokepointConfig, making it available to clients for badges\n and bypass corridor scoring.","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"],"type":"string"},"capacityConstraintTonnage":{"format":"int64","type":"string"},"id":{"type":"string"},"liveScore":{"format":"double","type":"number"},"name":{"type":"string"},"notes":{"type":"string"},"suitableCargoTypes":{"items":{"type":"string"},"type":"array"},"type":{"type":"string"},"waypointChokepointIds":{"items":{"type":"string"},"type":"array"}},"type":"object"},"ChokepointExposureEntry":{"description":"ChokepointExposureEntry holds per-chokepoint exposure data for a country.","properties":{"chokepointId":{"description":"Canonical chokepoint ID from the chokepoint registry.","type":"string"},"chokepointName":{"description":"Human-readable chokepoint name.","type":"string"},"coastSide":{"description":"Which ocean/basin side the country's ports face (atlantic, pacific, indian, med, multi, landlocked).","type":"string"},"exposureScore":{"description":"Exposure score 0100; higher = more dependent on this chokepoint.","format":"double","type":"number"},"shockSupported":{"description":"Whether the shock model is supported for this chokepoint + hs2 combination.","type":"boolean"}},"type":"object"},"ChokepointExposureSummary":{"properties":{"chokepointId":{"type":"string"},"chokepointName":{"type":"string"},"exposurePct":{"format":"int32","type":"integer"}},"type":"object"},"ChokepointInfo":{"properties":{"activeWarnings":{"format":"int32","type":"integer"},"affectedRoutes":{"items":{"type":"string"},"type":"array"},"aisDisruptions":{"format":"int32","type":"integer"},"congestionLevel":{"type":"string"},"description":{"type":"string"},"directionalDwt":{"items":{"$ref":"#/components/schemas/DirectionalDwt"},"type":"array"},"directions":{"items":{"type":"string"},"type":"array"},"disruptionScore":{"format":"int32","type":"integer"},"flowEstimate":{"$ref":"#/components/schemas/FlowEstimate"},"id":{"type":"string"},"lat":{"format":"double","type":"number"},"lon":{"format":"double","type":"number"},"name":{"type":"string"},"status":{"type":"string"},"transitSummary":{"$ref":"#/components/schemas/TransitSummary"},"warRiskTier":{"description":"*\n War risk tier derived from Lloyd's JWC Listed Areas + OSINT threat classification.\n This is a FREE field (no PRO gate) — it exposes the existing server-internal\n threatLevel from ChokepointConfig, making it available to clients for badges\n and bypass corridor scoring.","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"],"type":"string"}},"type":"object"},"CountryProduct":{"properties":{"description":{"type":"string"},"hs4":{"type":"string"},"topExporters":{"items":{"$ref":"#/components/schemas/ProductExporter"},"type":"array"},"totalValue":{"format":"double","type":"number"},"year":{"format":"int32","type":"integer"}},"type":"object"},"CriticalMineral":{"properties":{"globalProduction":{"format":"double","type":"number"},"hhi":{"format":"double","type":"number"},"mineral":{"type":"string"},"riskRating":{"type":"string"},"topProducers":{"items":{"$ref":"#/components/schemas/MineralProducer"},"type":"array"},"unit":{"type":"string"}},"type":"object"},"DirectionalDwt":{"properties":{"direction":{"type":"string"},"dwtThousandTonnes":{"format":"double","type":"number"},"wowChangePct":{"format":"double","type":"number"}},"type":"object"},"EnergyDisruptionEntry":{"properties":{"assetId":{"description":"Maps to a pipeline or storage-facility id seeded elsewhere.","type":"string"},"assetType":{"description":"One of: \"pipeline\" | \"storage\"","type":"string"},"capacityOfflineBcmYr":{"description":"Headline-offline capacity (contextual — 0 when not applicable).","format":"double","type":"number"},"capacityOfflineMbd":{"format":"double","type":"number"},"causeChain":{"items":{"description":"Contributing causes, primary-first.","type":"string"},"type":"array"},"classifierConfidence":{"format":"double","type":"number"},"classifierVersion":{"type":"string"},"countries":{"items":{"description":"Countries touched by the referenced asset (pipeline: fromCountry +\n toCountry + transitCountries; storage: country). Denormalised at seed\n time so CountryDeepDivePanel can filter events without a second RPC.\n Always non-empty in well-formed payloads; empty only if the upstream\n asset was removed without an event update. Per plan §R/#5 decision B.","type":"string"},"type":"array"},"endAt":{"description":"Empty string when event is still ongoing.","type":"string"},"eventType":{"description":"One of: \"sabotage\" | \"sanction\" | \"maintenance\" | \"mechanical\" |\n \"weather\" | \"commercial\" | \"war\" | \"other\"","type":"string"},"id":{"type":"string"},"lastEvidenceUpdate":{"type":"string"},"shortDescription":{"type":"string"},"sources":{"items":{"$ref":"#/components/schemas/EnergyDisruptionSource"},"type":"array"},"startAt":{"type":"string"}},"type":"object"},"EnergyDisruptionSource":{"properties":{"authority":{"type":"string"},"date":{"type":"string"},"sourceType":{"type":"string"},"title":{"type":"string"},"url":{"type":"string"}},"type":"object"},"Error":{"description":"Error is returned when a handler encounters an error. It contains a simple error message that the developer can customize.","properties":{"message":{"description":"Error message (e.g., 'user not found', 'database connection failed')","type":"string"}},"type":"object"},"FieldViolation":{"description":"FieldViolation describes a single validation error for a specific field.","properties":{"description":{"description":"Human-readable description of the validation violation (e.g., 'must be a valid email address', 'required field missing')","type":"string"},"field":{"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')","type":"string"}},"required":["field","description"],"type":"object"},"FlowEstimate":{"properties":{"baselineMbd":{"format":"double","type":"number"},"currentMbd":{"format":"double","type":"number"},"disrupted":{"type":"boolean"},"flowRatio":{"format":"double","type":"number"},"hazardAlertLevel":{"type":"string"},"hazardAlertName":{"type":"string"},"source":{"type":"string"}},"type":"object"},"FuelShortageEntry":{"properties":{"causeChain":{"items":{"description":"Contributing root causes, ordered primary-first. Subset of:\n \"upstream_refinery\" | \"logistics\" | \"policy\" | \"chokepoint\" |\n \"sanction\" | \"war\" | \"import_cut\"","type":"string"},"type":"array"},"country":{"type":"string"},"evidence":{"$ref":"#/components/schemas/FuelShortageEvidence"},"firstSeen":{"type":"string"},"id":{"type":"string"},"impactTypes":{"items":{"description":"Observable consumer-facing impacts. Subset of:\n \"stations_closed\" | \"rationing\" | \"flights_cancelled\" | \"import_cut\" | \"price_spike\"","type":"string"},"type":"array"},"lastConfirmed":{"type":"string"},"product":{"description":"One of: \"petrol\" | \"diesel\" | \"jet\" | \"heating_oil\"","type":"string"},"resolvedAt":{"description":"Empty string when not yet resolved.","type":"string"},"severity":{"description":"One of: \"confirmed\" | \"watch\" (classifier output — not a client-side derivation)","type":"string"},"shortDescription":{"type":"string"}},"type":"object"},"FuelShortageEvidence":{"properties":{"classifierConfidence":{"format":"double","type":"number"},"classifierVersion":{"type":"string"},"evidenceSources":{"items":{"$ref":"#/components/schemas/FuelShortageEvidenceSource"},"type":"array"},"firstRegulatorConfirmation":{"description":"ISO date of the first regulator confirmation, if any. Empty when\n severity is \"watch\" on press-only signal.","type":"string"},"lastEvidenceUpdate":{"type":"string"}},"type":"object"},"FuelShortageEvidenceSource":{"properties":{"authority":{"type":"string"},"date":{"type":"string"},"sourceType":{"description":"One of: \"regulator\" | \"operator\" | \"press\" | \"ais-relay\" | \"satellite\"","type":"string"},"title":{"type":"string"},"url":{"type":"string"}},"type":"object"},"GeoPoint":{"description":"GeoPoint is a [longitude, latitude] pair.","properties":{"lat":{"format":"double","type":"number"},"lon":{"format":"double","type":"number"}},"type":"object"},"GetBypassOptionsRequest":{"properties":{"cargoType":{"description":"container | tanker | bulk | roro (default: \"container\")","type":"string"},"chokepointId":{"type":"string"},"closurePct":{"description":"0-100, percent of capacity blocked (default: 100)","format":"int32","type":"integer"}},"required":["chokepointId"],"type":"object"},"GetBypassOptionsResponse":{"properties":{"cargoType":{"type":"string"},"chokepointId":{"type":"string"},"closurePct":{"format":"int32","type":"integer"},"fetchedAt":{"type":"string"},"options":{"items":{"$ref":"#/components/schemas/BypassOption"},"type":"array"},"primaryChokepointWarRiskTier":{"description":"*\n War risk tier derived from Lloyd's JWC Listed Areas + OSINT threat classification.\n This is a FREE field (no PRO gate) — it exposes the existing server-internal\n threatLevel from ChokepointConfig, making it available to clients for badges\n and bypass corridor scoring.","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"],"type":"string"}},"type":"object"},"GetChokepointHistoryRequest":{"description":"GetChokepointHistory returns the transit-count history for a single\n chokepoint. Loaded lazily on card expand so the main chokepoint-status\n response can stay compact (no 180-day history per chokepoint).","properties":{"chokepointId":{"type":"string"}},"required":["chokepointId"],"type":"object"},"GetChokepointHistoryResponse":{"properties":{"chokepointId":{"type":"string"},"fetchedAt":{"format":"int64","type":"string"},"history":{"items":{"$ref":"#/components/schemas/TransitDayCount"},"type":"array"}},"type":"object"},"GetChokepointStatusRequest":{"type":"object"},"GetChokepointStatusResponse":{"properties":{"chokepoints":{"items":{"$ref":"#/components/schemas/ChokepointInfo"},"type":"array"},"fetchedAt":{"type":"string"},"upstreamUnavailable":{"type":"boolean"}},"type":"object"},"GetCountryChokepointIndexRequest":{"description":"GetCountryChokepointIndexRequest specifies the country and optional HS2 chapter.","properties":{"hs2":{"description":"HS2 chapter (2-digit string). Defaults to \"27\" (energy/mineral fuels) when absent.","type":"string"},"iso2":{"description":"ISO 3166-1 alpha-2 country code (uppercase).","pattern":"^[A-Z]{2}$","type":"string"}},"required":["iso2"],"type":"object"},"GetCountryChokepointIndexResponse":{"description":"GetCountryChokepointIndexResponse returns exposure scores for all relevant chokepoints.","properties":{"exposures":{"items":{"$ref":"#/components/schemas/ChokepointExposureEntry"},"type":"array"},"fetchedAt":{"description":"ISO timestamp of when this data was last seeded.","type":"string"},"hs2":{"description":"HS2 chapter used for the computation.","type":"string"},"iso2":{"description":"ISO 3166-1 alpha-2 country code echoed from the request.","type":"string"},"primaryChokepointId":{"description":"Canonical ID of the chokepoint with the highest exposure score.","type":"string"},"vulnerabilityIndex":{"description":"Composite vulnerability index 0100 (weighted sum of top-3 exposures).","format":"double","type":"number"}},"type":"object"},"GetCountryCostShockRequest":{"properties":{"chokepointId":{"type":"string"},"hs2":{"description":"HS2 chapter (default: \"27\")","type":"string"},"iso2":{"pattern":"^[A-Z]{2}$","type":"string"}},"required":["iso2","chokepointId"],"type":"object"},"GetCountryCostShockResponse":{"properties":{"chokepointId":{"type":"string"},"coverageDays":{"description":"Energy stockpile coverage in days (IEA data, HS 27 only; 0 for non-energy sectors or net exporters)","format":"int32","type":"integer"},"fetchedAt":{"type":"string"},"hasEnergyModel":{"description":"Whether supply_deficit_pct and coverage_days are modelled (true) or unavailable (false)","type":"boolean"},"hs2":{"type":"string"},"iso2":{"type":"string"},"supplyDeficitPct":{"description":"Average refined-product supply deficit % under full closure (Gasoline/Diesel/Jet fuel/LPG average; HS 27 only)","format":"double","type":"number"},"unavailableReason":{"description":"Null/unavailable explanation for non-energy sectors","type":"string"},"warRiskPremiumBps":{"description":"War risk insurance premium in basis points for this chokepoint","format":"int32","type":"integer"},"warRiskTier":{"description":"*\n War risk tier derived from Lloyd's JWC Listed Areas + OSINT threat classification.\n This is a FREE field (no PRO gate) — it exposes the existing server-internal\n threatLevel from ChokepointConfig, making it available to clients for badges\n and bypass corridor scoring.","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"],"type":"string"}},"type":"object"},"GetCountryProductsRequest":{"properties":{"iso2":{"pattern":"^[A-Z]{2}$","type":"string"}},"required":["iso2"],"type":"object"},"GetCountryProductsResponse":{"properties":{"fetchedAt":{"description":"ISO timestamp from the seeded payload (empty when no data is cached).","type":"string"},"iso2":{"type":"string"},"products":{"items":{"$ref":"#/components/schemas/CountryProduct"},"type":"array"}},"type":"object"},"GetCriticalMineralsRequest":{"type":"object"},"GetCriticalMineralsResponse":{"properties":{"fetchedAt":{"type":"string"},"minerals":{"items":{"$ref":"#/components/schemas/CriticalMineral"},"type":"array"},"upstreamUnavailable":{"type":"boolean"}},"type":"object"},"GetFuelShortageDetailRequest":{"description":"GetFuelShortageDetail returns a single shortage with full evidence\n bundle + citation timeline. Loaded lazily on drawer open.","properties":{"shortageId":{"type":"string"}},"required":["shortageId"],"type":"object"},"GetFuelShortageDetailResponse":{"properties":{"fetchedAt":{"type":"string"},"shortage":{"$ref":"#/components/schemas/FuelShortageEntry"},"unavailable":{"type":"boolean"}},"type":"object"},"GetMultiSectorCostShockRequest":{"properties":{"chokepointId":{"type":"string"},"closureDays":{"description":"Closure-window duration in days. Server clamps to [1, 365]. Defaults to 30.","format":"int32","type":"integer"},"iso2":{"pattern":"^[A-Z]{2}$","type":"string"}},"required":["iso2","chokepointId"],"type":"object"},"GetMultiSectorCostShockResponse":{"properties":{"chokepointId":{"type":"string"},"closureDays":{"description":"Server-clamped closure-window duration in days (1-365).","format":"int32","type":"integer"},"fetchedAt":{"type":"string"},"iso2":{"type":"string"},"sectors":{"items":{"$ref":"#/components/schemas/MultiSectorCostShock"},"type":"array"},"totalAddedCost":{"description":"Sum of total_cost_shock across all sectors.","format":"double","type":"number"},"unavailableReason":{"description":"Populated when no seeded import data is available for the country.","type":"string"},"warRiskTier":{"description":"*\n War risk tier derived from Lloyd's JWC Listed Areas + OSINT threat classification.\n This is a FREE field (no PRO gate) — it exposes the existing server-internal\n threatLevel from ChokepointConfig, making it available to clients for badges\n and bypass corridor scoring.","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"],"type":"string"}},"type":"object"},"GetPipelineDetailRequest":{"description":"GetPipelineDetail returns a single pipeline with its full evidence bundle.\n Evidence surface here is richer than ListPipelinesResponse — the list view\n is designed for the map layer's compact shape; detail is designed for the\n click-through drawer.","properties":{"pipelineId":{"type":"string"}},"required":["pipelineId"],"type":"object"},"GetPipelineDetailResponse":{"properties":{"fetchedAt":{"type":"string"},"pipeline":{"$ref":"#/components/schemas/PipelineEntry"},"revisions":{"items":{"$ref":"#/components/schemas/PipelineRevisionEntry"},"type":"array"},"unavailable":{"type":"boolean"}},"type":"object"},"GetRouteExplorerLaneRequest":{"properties":{"cargoType":{"description":"One of: container, tanker, bulk, roro","type":"string"},"fromIso2":{"pattern":"^[A-Z]{2}$","type":"string"},"hs2":{"description":"HS2 chapter code, e.g. \"27\", \"85\"","type":"string"},"toIso2":{"pattern":"^[A-Z]{2}$","type":"string"}},"required":["fromIso2","toIso2","hs2","cargoType"],"type":"object"},"GetRouteExplorerLaneResponse":{"properties":{"bypassOptions":{"items":{"$ref":"#/components/schemas/BypassCorridorOption"},"type":"array"},"cargoType":{"type":"string"},"chokepointExposures":{"items":{"$ref":"#/components/schemas/ChokepointExposureSummary"},"type":"array"},"disruptionScore":{"format":"double","type":"number"},"estFreightUsdPerTeuRange":{"$ref":"#/components/schemas/NumberRange"},"estTransitDaysRange":{"$ref":"#/components/schemas/NumberRange"},"fetchedAt":{"type":"string"},"fromIso2":{"type":"string"},"hs2":{"type":"string"},"noModeledLane":{"description":"True when the wrapper fell back to the origin's first route (no shared route\n between origin and destination clusters). Signals \"no modeled lane\" to the UI.","type":"boolean"},"primaryRouteGeometry":{"items":{"$ref":"#/components/schemas/GeoPoint"},"type":"array"},"primaryRouteId":{"description":"Primary trade route ID from TRADE_ROUTES config. Empty when no modeled lane.","type":"string"},"toIso2":{"type":"string"},"warRiskTier":{"type":"string"}},"type":"object"},"GetRouteImpactRequest":{"properties":{"fromIso2":{"pattern":"^[A-Z]{2}$","type":"string"},"hs2":{"type":"string"},"toIso2":{"pattern":"^[A-Z]{2}$","type":"string"}},"required":["fromIso2","toIso2","hs2"],"type":"object"},"GetRouteImpactResponse":{"properties":{"comtradeSource":{"type":"string"},"dependencyFlags":{"items":{"description":"DependencyFlag classifies how a country+sector dependency can fail.","enum":["DEPENDENCY_FLAG_UNSPECIFIED","DEPENDENCY_FLAG_SINGLE_SOURCE_CRITICAL","DEPENDENCY_FLAG_SINGLE_CORRIDOR_CRITICAL","DEPENDENCY_FLAG_COMPOUND_RISK","DEPENDENCY_FLAG_DIVERSIFIABLE"],"type":"string"},"type":"array"},"fetchedAt":{"type":"string"},"hs2InSeededUniverse":{"type":"boolean"},"laneValueUsd":{"format":"double","type":"number"},"primaryExporterIso2":{"type":"string"},"primaryExporterShare":{"format":"double","type":"number"},"resilienceScore":{"format":"double","type":"number"},"topStrategicProducts":{"items":{"$ref":"#/components/schemas/StrategicProduct"},"type":"array"}},"type":"object"},"GetSectorDependencyRequest":{"properties":{"hs2":{"description":"HS2 chapter code, e.g. \"27\" (mineral fuels), \"85\" (electronics)","type":"string"},"iso2":{"pattern":"^[A-Z]{2}$","type":"string"}},"required":["iso2","hs2"],"type":"object"},"GetSectorDependencyResponse":{"properties":{"fetchedAt":{"type":"string"},"flags":{"items":{"description":"DependencyFlag classifies how a country+sector dependency can fail.","enum":["DEPENDENCY_FLAG_UNSPECIFIED","DEPENDENCY_FLAG_SINGLE_SOURCE_CRITICAL","DEPENDENCY_FLAG_SINGLE_CORRIDOR_CRITICAL","DEPENDENCY_FLAG_COMPOUND_RISK","DEPENDENCY_FLAG_DIVERSIFIABLE"],"type":"string"},"type":"array"},"hasViableBypass":{"description":"Whether at least one viable bypass corridor exists for the primary chokepoint.","type":"boolean"},"hs2":{"type":"string"},"hs2Label":{"description":"Human-readable HS2 chapter name.","type":"string"},"iso2":{"type":"string"},"primaryChokepointExposure":{"description":"Exposure score for the primary chokepoint (0100).","format":"double","type":"number"},"primaryChokepointId":{"description":"Chokepoint ID with the highest exposure score for this country+sector.","type":"string"},"primaryExporterIso2":{"description":"ISO2 of the country supplying the largest share of this sector's imports.","type":"string"},"primaryExporterShare":{"description":"Share of imports from the primary exporter (01). 0 = no Comtrade data available.","format":"double","type":"number"}},"type":"object"},"GetShippingRatesRequest":{"type":"object"},"GetShippingRatesResponse":{"properties":{"fetchedAt":{"type":"string"},"indices":{"items":{"$ref":"#/components/schemas/ShippingIndex"},"type":"array"},"upstreamUnavailable":{"type":"boolean"}},"type":"object"},"GetShippingStressRequest":{"type":"object"},"GetShippingStressResponse":{"properties":{"carriers":{"items":{"$ref":"#/components/schemas/ShippingStressCarrier"},"type":"array"},"fetchedAt":{"description":"Warning: Values \u003e 2^53 may lose precision in JavaScript","format":"int64","type":"integer"},"stressLevel":{"description":"\"low\" | \"moderate\" | \"elevated\" | \"critical\".","type":"string"},"stressScore":{"description":"Composite stress score 0100 (higher = more disruption).","format":"double","type":"number"},"upstreamUnavailable":{"description":"Set to true when upstream data source is unavailable and cached data is stale.","type":"boolean"}},"type":"object"},"GetStorageFacilityDetailRequest":{"description":"GetStorageFacilityDetail returns a single facility with its full evidence\n bundle + revision log. Revisions land in Week 3 alongside the disruption\n event log (Week 3 milestone — empty array in v1).","properties":{"facilityId":{"type":"string"}},"required":["facilityId"],"type":"object"},"GetStorageFacilityDetailResponse":{"properties":{"facility":{"$ref":"#/components/schemas/StorageFacilityEntry"},"fetchedAt":{"type":"string"},"revisions":{"items":{"$ref":"#/components/schemas/StorageFacilityRevisionEntry"},"type":"array"},"unavailable":{"type":"boolean"}},"type":"object"},"LatLon":{"properties":{"lat":{"format":"double","type":"number"},"lon":{"format":"double","type":"number"}},"type":"object"},"ListEnergyDisruptionsRequest":{"description":"ListEnergyDisruptions returns the energy disruption event log — state\n transitions for pipelines and storage facilities (sabotage, sanction,\n maintenance, mechanical, weather, commercial, war). Each event ties\n back to an assetId seeded by the pipeline or storage registry, so the\n panel drawer can render an asset-scoped timeline without a second RPC.\n\n Seeded from a curated JSON file (scripts/data/energy-disruptions.json).\n An automated state-transition classifier was scoped but not shipped —\n the log is curated-only today.\n\n See docs/methodology/disruptions.mdx.","properties":{"assetId":{"description":"Filter to one asset. Omit for all. When set, also narrows to the\n matching asset_type if provided.","type":"string"},"assetType":{"description":"Filter to one asset type. Accepts: \"pipeline\" | \"storage\".","type":"string"},"ongoingOnly":{"description":"If true, only return events with endAt empty (still ongoing).","type":"boolean"}},"type":"object"},"ListEnergyDisruptionsResponse":{"properties":{"classifierVersion":{"type":"string"},"events":{"items":{"$ref":"#/components/schemas/EnergyDisruptionEntry"},"type":"array"},"fetchedAt":{"type":"string"},"upstreamUnavailable":{"type":"boolean"}},"type":"object"},"ListFuelShortagesRequest":{"description":"ListFuelShortages returns the global fuel-shortage alert registry.\n Seeded from a curated JSON file (scripts/data/fuel-shortages.json).\n An LLM classifier extension was scoped but not shipped — the registry\n is curated-only today. Severity (\"confirmed\" or \"watch\") is a row\n field authored at curation time; the evidence is shipped alongside\n so agents and humans can audit the grounds for a severity label.\n\n See docs/methodology/shortages.mdx for the evidence-threshold spec.","properties":{"country":{"description":"Filter to one ISO 3166-1 alpha-2 country. Omit for global.","type":"string"},"product":{"description":"Filter to one product. Accepts: \"petrol\" | \"diesel\" | \"jet\" | \"heating_oil\".\n Omit for all products.","type":"string"},"severity":{"description":"Filter to one severity. Accepts: \"confirmed\" | \"watch\". Omit for both.","type":"string"}},"type":"object"},"ListFuelShortagesResponse":{"properties":{"classifierVersion":{"type":"string"},"fetchedAt":{"type":"string"},"shortages":{"items":{"$ref":"#/components/schemas/FuelShortageEntry"},"type":"array"},"upstreamUnavailable":{"type":"boolean"}},"type":"object"},"ListPipelinesRequest":{"description":"ListPipelines returns the full oil and/or gas pipeline registry with\n evidence-based status per asset. Registry is curated (see\n docs/methodology/pipelines.mdx) and refreshed weekly by\n scripts/seed-pipelines-{gas,oil}.mjs. Typical consumer: PipelineStatusPanel\n rendering the DeckGL PathLayer.\n\n The public badge is DERIVED from the evidence bundle server-side at\n read time — callers MUST use `public_badge` for display, not invent\n their own derivation from `evidence` fields. This keeps the atlas and\n MCP clients consistent and versioned together.","properties":{"commodityType":{"description":"Filter to one commodity. Omit (or pass empty) to receive both.","type":"string"}},"type":"object"},"ListPipelinesResponse":{"properties":{"classifierVersion":{"type":"string"},"fetchedAt":{"type":"string"},"pipelines":{"items":{"$ref":"#/components/schemas/PipelineEntry"},"type":"array"},"upstreamUnavailable":{"type":"boolean"}},"type":"object"},"ListStorageFacilitiesRequest":{"description":"ListStorageFacilities returns the curated strategic storage registry\n (underground gas storage, strategic petroleum reserves, LNG terminals,\n crude tank farms) with evidence-based status per asset. Registry is\n curated (see docs/methodology/storage.mdx) and refreshed weekly by\n scripts/seed-storage-facilities.mjs. Typical consumer:\n StorageFacilityMapPanel rendering the DeckGL ScatterplotLayer.\n\n Like pipelines, the public badge is DERIVED from the evidence bundle\n server-side at read time. Callers MUST use `public_badge` for display\n rather than inventing their own derivation from `evidence` fields, so\n the atlas and MCP clients stay consistent and versioned together.","properties":{"facilityType":{"description":"Filter to one facility type. Accepts:\n \"ugs\" | \"spr\" | \"lng_export\" | \"lng_import\" | \"crude_tank_farm\"\n Omit (or pass empty) to receive all types.","type":"string"}},"type":"object"},"ListStorageFacilitiesResponse":{"properties":{"classifierVersion":{"type":"string"},"facilities":{"items":{"$ref":"#/components/schemas/StorageFacilityEntry"},"type":"array"},"fetchedAt":{"type":"string"},"upstreamUnavailable":{"type":"boolean"}},"type":"object"},"MineralProducer":{"properties":{"country":{"type":"string"},"countryCode":{"type":"string"},"productionTonnes":{"format":"double","type":"number"},"sharePct":{"format":"double","type":"number"}},"type":"object"},"MultiSectorCostShock":{"properties":{"addedTransitDays":{"description":"Bypass-corridor transit penalty (informational).","format":"int32","type":"integer"},"closureDays":{"description":"Echoes the clamped closure duration used for total_cost_shock (1-365).","format":"int32","type":"integer"},"freightAddedPctPerTon":{"description":"Bypass-corridor freight uplift fraction (0.10 == +10% per ton).","format":"double","type":"number"},"hs2":{"description":"HS2 chapter code (e.g. \"27\" mineral fuels, \"85\" electronics).","type":"string"},"hs2Label":{"description":"Friendly chapter label (e.g. \"Energy\", \"Electronics\").","type":"string"},"importValueAnnual":{"description":"Total annual import value (USD) for this sector.","format":"double","type":"number"},"totalCostShock":{"description":"Cost for the requested closure_days window.","format":"double","type":"number"},"totalCostShock30Days":{"format":"double","type":"number"},"totalCostShock90Days":{"format":"double","type":"number"},"totalCostShockPerDay":{"format":"double","type":"number"},"warRiskPremiumBps":{"description":"War-risk insurance premium (basis points) sourced from the chokepoint tier.","format":"int32","type":"integer"}},"type":"object"},"NumberRange":{"description":"Inclusive integer range for transit days / freight USD estimates.","properties":{"max":{"format":"int32","type":"integer"},"min":{"format":"int32","type":"integer"}},"type":"object"},"OperatorStatement":{"properties":{"date":{"type":"string"},"text":{"type":"string"},"url":{"type":"string"}},"type":"object"},"PipelineEntry":{"properties":{"capacityBcmYr":{"format":"double","type":"number"},"capacityMbd":{"format":"double","type":"number"},"commodityType":{"description":"Either \"gas\" or \"oil\".","type":"string"},"endPoint":{"$ref":"#/components/schemas/LatLon"},"evidence":{"$ref":"#/components/schemas/PipelineEvidence"},"fromCountry":{"type":"string"},"id":{"type":"string"},"inService":{"format":"int32","type":"integer"},"lengthKm":{"format":"int32","type":"integer"},"name":{"type":"string"},"operator":{"type":"string"},"publicBadge":{"description":"Server-derived public badge. One of:\n \"flowing\" | \"reduced\" | \"offline\" | \"disputed\"","type":"string"},"startPoint":{"$ref":"#/components/schemas/LatLon"},"toCountry":{"type":"string"},"transitCountries":{"items":{"type":"string"},"type":"array"},"waypoints":{"items":{"$ref":"#/components/schemas/LatLon"},"type":"array"}},"type":"object"},"PipelineEvidence":{"properties":{"classifierConfidence":{"format":"double","type":"number"},"classifierVersion":{"type":"string"},"commercialState":{"description":"One of: \"under_contract\" | \"expired\" | \"suspended\" | \"unknown\"","type":"string"},"lastEvidenceUpdate":{"type":"string"},"operatorStatement":{"$ref":"#/components/schemas/OperatorStatement"},"physicalState":{"description":"One of: \"flowing\" | \"reduced\" | \"offline\" | \"unknown\"","type":"string"},"physicalStateSource":{"description":"One of: \"operator\" | \"regulator\" | \"press\" | \"satellite\" | \"ais-relay\"","type":"string"},"sanctionRefs":{"items":{"$ref":"#/components/schemas/SanctionRef"},"type":"array"}},"type":"object"},"PipelineRevisionEntry":{"properties":{"classifierVersion":{"type":"string"},"date":{"type":"string"},"fieldChanged":{"type":"string"},"newValue":{"type":"string"},"previousValue":{"type":"string"},"sourcesUsed":{"items":{"type":"string"},"type":"array"},"trigger":{"description":"One of: \"classifier\" | \"source\" | \"decay\" | \"override\"","type":"string"}},"type":"object"},"ProductExporter":{"properties":{"partnerCode":{"format":"int32","type":"integer"},"partnerIso2":{"type":"string"},"share":{"format":"double","type":"number"},"value":{"format":"double","type":"number"}},"type":"object"},"SanctionRef":{"properties":{"authority":{"type":"string"},"date":{"type":"string"},"listId":{"type":"string"},"url":{"type":"string"}},"type":"object"},"ShippingIndex":{"properties":{"changePct":{"format":"double","type":"number"},"currentValue":{"format":"double","type":"number"},"history":{"items":{"$ref":"#/components/schemas/ShippingRatePoint"},"type":"array"},"indexId":{"type":"string"},"name":{"type":"string"},"previousValue":{"format":"double","type":"number"},"spikeAlert":{"type":"boolean"},"unit":{"type":"string"}},"type":"object"},"ShippingRatePoint":{"properties":{"date":{"type":"string"},"value":{"format":"double","type":"number"}},"type":"object"},"ShippingStressCarrier":{"description":"ShippingStressCarrier represents market stress data for a carrier or shipping index.","properties":{"carrierType":{"description":"Carrier type: \"etf\" | \"carrier\" | \"index\".","type":"string"},"changePct":{"description":"Percentage change from previous close.","format":"double","type":"number"},"name":{"description":"Human-readable name.","type":"string"},"price":{"description":"Current price.","format":"double","type":"number"},"sparkline":{"items":{"description":"30-day price sparkline.","format":"double","type":"number"},"type":"array"},"symbol":{"description":"Ticker or identifier (e.g., \"BDRY\", \"ZIM\").","type":"string"}},"type":"object"},"StorageEvidence":{"properties":{"classifierConfidence":{"format":"double","type":"number"},"classifierVersion":{"type":"string"},"commercialState":{"description":"One of: \"under_contract\" | \"expired\" | \"suspended\" | \"unknown\"","type":"string"},"fillDisclosed":{"description":"Whether working-gas / stock-level fill is publicly disclosed for this\n asset. LNG export terminals, for instance, tend NOT to disclose; UGS\n sites in Europe are required to disclose via GIE AGSI+.","type":"boolean"},"fillSource":{"description":"Source of the disclosed fill series (\"GIE AGSI+\", \"EIA SPR weekly\n stock report\", etc.). Empty when fill_disclosed=false.","type":"string"},"lastEvidenceUpdate":{"type":"string"},"operatorStatement":{"$ref":"#/components/schemas/StorageOperatorStatement"},"physicalState":{"description":"One of: \"operational\" | \"reduced\" | \"offline\" | \"under_construction\" | \"unknown\"","type":"string"},"physicalStateSource":{"description":"One of: \"operator\" | \"regulator\" | \"press\" | \"satellite\" | \"ais-relay\"","type":"string"},"sanctionRefs":{"items":{"$ref":"#/components/schemas/StorageSanctionRef"},"type":"array"}},"type":"object"},"StorageFacilityEntry":{"properties":{"capacityMb":{"format":"double","type":"number"},"capacityMtpa":{"format":"double","type":"number"},"capacityTwh":{"description":"Working capacity in the facility's native unit (see working_capacity_unit).\n Exactly ONE of these is populated per facility, chosen by facility_type:\n ugs → capacity_twh\n spr, crude_tank_farm → capacity_mb\n lng_export, lng_import → capacity_mtpa","format":"double","type":"number"},"country":{"type":"string"},"evidence":{"$ref":"#/components/schemas/StorageEvidence"},"facilityType":{"description":"One of: \"ugs\" | \"spr\" | \"lng_export\" | \"lng_import\" | \"crude_tank_farm\"","type":"string"},"id":{"type":"string"},"inService":{"format":"int32","type":"integer"},"location":{"$ref":"#/components/schemas/StorageLatLon"},"name":{"type":"string"},"operator":{"type":"string"},"publicBadge":{"description":"Server-derived public badge. One of:\n \"operational\" | \"reduced\" | \"offline\" | \"disputed\"","type":"string"},"workingCapacityUnit":{"description":"One of: \"TWh\" | \"Mb\" | \"Mtpa\"","type":"string"}},"type":"object"},"StorageFacilityRevisionEntry":{"properties":{"classifierVersion":{"type":"string"},"date":{"type":"string"},"fieldChanged":{"type":"string"},"newValue":{"type":"string"},"previousValue":{"type":"string"},"sourcesUsed":{"items":{"type":"string"},"type":"array"},"trigger":{"description":"One of: \"classifier\" | \"source\" | \"decay\" | \"override\"","type":"string"}},"type":"object"},"StorageLatLon":{"properties":{"lat":{"format":"double","type":"number"},"lon":{"format":"double","type":"number"}},"type":"object"},"StorageOperatorStatement":{"properties":{"date":{"type":"string"},"text":{"type":"string"},"url":{"type":"string"}},"type":"object"},"StorageSanctionRef":{"properties":{"authority":{"type":"string"},"date":{"type":"string"},"listId":{"type":"string"},"url":{"type":"string"}},"type":"object"},"StrategicProduct":{"properties":{"hs4":{"type":"string"},"label":{"type":"string"},"primaryChokepointId":{"type":"string"},"topExporterIso2":{"type":"string"},"topExporterShare":{"format":"double","type":"number"},"totalValueUsd":{"format":"double","type":"number"}},"type":"object"},"TransitDayCount":{"properties":{"capContainer":{"format":"double","type":"number"},"capDryBulk":{"format":"double","type":"number"},"capGeneralCargo":{"format":"double","type":"number"},"capRoro":{"format":"double","type":"number"},"capTanker":{"format":"double","type":"number"},"cargo":{"format":"int32","type":"integer"},"container":{"format":"int32","type":"integer"},"date":{"type":"string"},"dryBulk":{"format":"int32","type":"integer"},"generalCargo":{"format":"int32","type":"integer"},"other":{"format":"int32","type":"integer"},"roro":{"format":"int32","type":"integer"},"tanker":{"format":"int32","type":"integer"},"total":{"format":"int32","type":"integer"}},"type":"object"},"TransitSummary":{"properties":{"dataAvailable":{"description":"False when the upstream portwatch/relay source did not return data for\n this chokepoint in the current cycle — the summary fields are zero-state\n fill, not a genuine \"zero traffic\" reading. Client should render a\n \"transit data unavailable\" indicator and skip stat/chart rendering.","type":"boolean"},"disruptionPct":{"format":"double","type":"number"},"history":{"items":{"$ref":"#/components/schemas/TransitDayCount"},"type":"array"},"incidentCount7d":{"format":"int32","type":"integer"},"riskLevel":{"type":"string"},"riskReportAction":{"type":"string"},"riskSummary":{"type":"string"},"todayCargo":{"format":"int32","type":"integer"},"todayOther":{"format":"int32","type":"integer"},"todayTanker":{"format":"int32","type":"integer"},"todayTotal":{"format":"int32","type":"integer"},"wowChangePct":{"format":"double","type":"number"}},"type":"object"},"ValidationError":{"description":"ValidationError is returned when request validation fails. It contains a list of field violations describing what went wrong.","properties":{"violations":{"description":"List of validation violations","items":{"$ref":"#/components/schemas/FieldViolation"},"type":"array"}},"required":["violations"],"type":"object"}}},"info":{"title":"SupplyChainService API","version":"1.0.0"},"openapi":"3.1.0","paths":{"/api/supply-chain/v1/get-bypass-options":{"get":{"description":"GetBypassOptions returns ranked bypass corridors for a chokepoint. PRO-gated.","operationId":"GetBypassOptions","parameters":[{"in":"query","name":"chokepointId","required":false,"schema":{"type":"string"}},{"description":"container | tanker | bulk | roro (default: \"container\")","in":"query","name":"cargoType","required":false,"schema":{"type":"string"}},{"description":"0-100, percent of capacity blocked (default: 100)","in":"query","name":"closurePct","required":false,"schema":{"format":"int32","type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetBypassOptionsResponse"}}},"description":"Successful response"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}},"description":"Validation error"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Error response"}},"summary":"GetBypassOptions","tags":["SupplyChainService"]}},"/api/supply-chain/v1/get-chokepoint-history":{"get":{"description":"GetChokepointHistory returns transit-count history for a single chokepoint,\n loaded lazily on card expand. Keeps the status RPC compact (no 180-day\n history per chokepoint on every call).","operationId":"GetChokepointHistory","parameters":[{"in":"query","name":"chokepointId","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetChokepointHistoryResponse"}}},"description":"Successful response"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}},"description":"Validation error"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Error response"}},"summary":"GetChokepointHistory","tags":["SupplyChainService"]}},"/api/supply-chain/v1/get-chokepoint-status":{"get":{"operationId":"GetChokepointStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetChokepointStatusResponse"}}},"description":"Successful response"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}},"description":"Validation error"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Error response"}},"summary":"GetChokepointStatus","tags":["SupplyChainService"]}},"/api/supply-chain/v1/get-country-chokepoint-index":{"get":{"description":"GetCountryChokepointIndex returns per-chokepoint exposure scores for a country. PRO-gated.","operationId":"GetCountryChokepointIndex","parameters":[{"description":"ISO 3166-1 alpha-2 country code (uppercase).","in":"query","name":"iso2","required":false,"schema":{"type":"string"}},{"description":"HS2 chapter (2-digit string). Defaults to \"27\" (energy/mineral fuels) when absent.","in":"query","name":"hs2","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetCountryChokepointIndexResponse"}}},"description":"Successful response"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}},"description":"Validation error"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Error response"}},"summary":"GetCountryChokepointIndex","tags":["SupplyChainService"]}},"/api/supply-chain/v1/get-country-cost-shock":{"get":{"description":"GetCountryCostShock returns cost shock and war risk data for a country+chokepoint. PRO-gated.","operationId":"GetCountryCostShock","parameters":[{"in":"query","name":"iso2","required":false,"schema":{"type":"string"}},{"in":"query","name":"chokepointId","required":false,"schema":{"type":"string"}},{"description":"HS2 chapter (default: \"27\")","in":"query","name":"hs2","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetCountryCostShockResponse"}}},"description":"Successful response"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}},"description":"Validation error"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Error response"}},"summary":"GetCountryCostShock","tags":["SupplyChainService"]}},"/api/supply-chain/v1/get-country-products":{"get":{"description":"GetCountryProducts returns the seeded bilateral-HS4 import basket for a country. PRO-gated.","operationId":"GetCountryProducts","parameters":[{"in":"query","name":"iso2","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetCountryProductsResponse"}}},"description":"Successful response"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}},"description":"Validation error"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Error response"}},"summary":"GetCountryProducts","tags":["SupplyChainService"]}},"/api/supply-chain/v1/get-critical-minerals":{"get":{"operationId":"GetCriticalMinerals","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetCriticalMineralsResponse"}}},"description":"Successful response"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}},"description":"Validation error"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Error response"}},"summary":"GetCriticalMinerals","tags":["SupplyChainService"]}},"/api/supply-chain/v1/get-fuel-shortage-detail":{"get":{"description":"GetFuelShortageDetail returns a single shortage with full evidence\n bundle and citation timeline. Loaded lazily on drawer open.","operationId":"GetFuelShortageDetail","parameters":[{"in":"query","name":"shortageId","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetFuelShortageDetailResponse"}}},"description":"Successful response"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}},"description":"Validation error"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Error response"}},"summary":"GetFuelShortageDetail","tags":["SupplyChainService"]}},"/api/supply-chain/v1/get-multi-sector-cost-shock":{"get":{"description":"GetMultiSectorCostShock returns per-sector cost-shock estimates for a\n country+chokepoint+closure-window. PRO-gated.","operationId":"GetMultiSectorCostShock","parameters":[{"in":"query","name":"iso2","required":false,"schema":{"type":"string"}},{"in":"query","name":"chokepointId","required":false,"schema":{"type":"string"}},{"description":"Closure-window duration in days. Server clamps to [1, 365]. Defaults to 30.","in":"query","name":"closureDays","required":false,"schema":{"format":"int32","type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetMultiSectorCostShockResponse"}}},"description":"Successful response"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}},"description":"Validation error"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Error response"}},"summary":"GetMultiSectorCostShock","tags":["SupplyChainService"]}},"/api/supply-chain/v1/get-pipeline-detail":{"get":{"description":"GetPipelineDetail returns a single pipeline with full evidence bundle\n + auto-revision-log entries. Loaded lazily on drawer open.","operationId":"GetPipelineDetail","parameters":[{"in":"query","name":"pipelineId","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetPipelineDetailResponse"}}},"description":"Successful response"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}},"description":"Validation error"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Error response"}},"summary":"GetPipelineDetail","tags":["SupplyChainService"]}},"/api/supply-chain/v1/get-route-explorer-lane":{"get":{"description":"GetRouteExplorerLane returns the primary maritime route, chokepoint exposures,\n bypass options with geometry, war risk, and static transit/freight estimates for\n a country pair + HS2 + cargo type. PRO-gated. Wraps the route-intelligence vendor\n endpoint's compute with browser-callable auth and adds fields needed by the\n Route Explorer UI.","operationId":"GetRouteExplorerLane","parameters":[{"in":"query","name":"fromIso2","required":false,"schema":{"type":"string"}},{"in":"query","name":"toIso2","required":false,"schema":{"type":"string"}},{"description":"HS2 chapter code, e.g. \"27\", \"85\"","in":"query","name":"hs2","required":false,"schema":{"type":"string"}},{"description":"One of: container, tanker, bulk, roro","in":"query","name":"cargoType","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetRouteExplorerLaneResponse"}}},"description":"Successful response"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}},"description":"Validation error"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Error response"}},"summary":"GetRouteExplorerLane","tags":["SupplyChainService"]}},"/api/supply-chain/v1/get-route-impact":{"get":{"operationId":"GetRouteImpact","parameters":[{"in":"query","name":"fromIso2","required":false,"schema":{"type":"string"}},{"in":"query","name":"toIso2","required":false,"schema":{"type":"string"}},{"in":"query","name":"hs2","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetRouteImpactResponse"}}},"description":"Successful response"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}},"description":"Validation error"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Error response"}},"summary":"GetRouteImpact","tags":["SupplyChainService"]}},"/api/supply-chain/v1/get-sector-dependency":{"get":{"description":"GetSectorDependency returns dependency flags and risk profile for a country+HS2 sector. PRO-gated.","operationId":"GetSectorDependency","parameters":[{"in":"query","name":"iso2","required":false,"schema":{"type":"string"}},{"description":"HS2 chapter code, e.g. \"27\" (mineral fuels), \"85\" (electronics)","in":"query","name":"hs2","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSectorDependencyResponse"}}},"description":"Successful response"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}},"description":"Validation error"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Error response"}},"summary":"GetSectorDependency","tags":["SupplyChainService"]}},"/api/supply-chain/v1/get-shipping-rates":{"get":{"operationId":"GetShippingRates","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetShippingRatesResponse"}}},"description":"Successful response"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}},"description":"Validation error"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Error response"}},"summary":"GetShippingRates","tags":["SupplyChainService"]}},"/api/supply-chain/v1/get-shipping-stress":{"get":{"description":"GetShippingStress returns carrier market data and a composite stress index.","operationId":"GetShippingStress","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetShippingStressResponse"}}},"description":"Successful response"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}},"description":"Validation error"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Error response"}},"summary":"GetShippingStress","tags":["SupplyChainService"]}},"/api/supply-chain/v1/get-storage-facility-detail":{"get":{"description":"GetStorageFacilityDetail returns a single facility with full evidence\n bundle + revision log. Loaded lazily on drawer open.","operationId":"GetStorageFacilityDetail","parameters":[{"in":"query","name":"facilityId","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetStorageFacilityDetailResponse"}}},"description":"Successful response"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}},"description":"Validation error"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Error response"}},"summary":"GetStorageFacilityDetail","tags":["SupplyChainService"]}},"/api/supply-chain/v1/list-energy-disruptions":{"get":{"description":"ListEnergyDisruptions returns the disruption event log for pipelines\n and storage facilities. Supports per-asset or per-asset-type filtering\n so panel drawers can fetch a scoped timeline without pulling the\n full registry.","operationId":"ListEnergyDisruptions","parameters":[{"description":"Filter to one asset. Omit for all. When set, also narrows to the\n matching asset_type if provided.","in":"query","name":"assetId","required":false,"schema":{"type":"string"}},{"description":"Filter to one asset type. Accepts: \"pipeline\" | \"storage\".","in":"query","name":"assetType","required":false,"schema":{"type":"string"}},{"description":"If true, only return events with endAt empty (still ongoing).","in":"query","name":"ongoingOnly","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListEnergyDisruptionsResponse"}}},"description":"Successful response"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}},"description":"Validation error"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Error response"}},"summary":"ListEnergyDisruptions","tags":["SupplyChainService"]}},"/api/supply-chain/v1/list-fuel-shortages":{"get":{"description":"ListFuelShortages returns the global fuel-shortage alert registry.\n Curated-only: severity (\"confirmed\" | \"watch\") is a row field authored\n at curation time, not a client-side derivation. Free tier.","operationId":"ListFuelShortages","parameters":[{"description":"Filter to one ISO 3166-1 alpha-2 country. Omit for global.","in":"query","name":"country","required":false,"schema":{"type":"string"}},{"description":"Filter to one product. Accepts: \"petrol\" | \"diesel\" | \"jet\" | \"heating_oil\".\n Omit for all products.","in":"query","name":"product","required":false,"schema":{"type":"string"}},{"description":"Filter to one severity. Accepts: \"confirmed\" | \"watch\". Omit for both.","in":"query","name":"severity","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListFuelShortagesResponse"}}},"description":"Successful response"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}},"description":"Validation error"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Error response"}},"summary":"ListFuelShortages","tags":["SupplyChainService"]}},"/api/supply-chain/v1/list-pipelines":{"get":{"description":"ListPipelines returns the curated oil \u0026 gas pipeline registry for the\n Energy Atlas PathLayer. Public badges are DERIVED from evidence bundles\n server-side and versioned (classifier_version). Free-tier; see\n docs/methodology/pipelines.mdx for data + classifier spec.","operationId":"ListPipelines","parameters":[{"description":"Filter to one commodity. Omit (or pass empty) to receive both.","in":"query","name":"commodityType","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListPipelinesResponse"}}},"description":"Successful response"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}},"description":"Validation error"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Error response"}},"summary":"ListPipelines","tags":["SupplyChainService"]}},"/api/supply-chain/v1/list-storage-facilities":{"get":{"description":"ListStorageFacilities returns the curated strategic storage registry\n (UGS + SPR + LNG + crude tank farms) for the Energy Atlas DeckGL\n ScatterplotLayer. Public badges are DERIVED from evidence bundles\n server-side and versioned (classifier_version). Free-tier; see\n docs/methodology/storage.mdx.","operationId":"ListStorageFacilities","parameters":[{"description":"Filter to one facility type. Accepts:\n \"ugs\" | \"spr\" | \"lng_export\" | \"lng_import\" | \"crude_tank_farm\"\n Omit (or pass empty) to receive all types.","in":"query","name":"facilityType","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListStorageFacilitiesResponse"}}},"description":"Successful response"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}},"description":"Validation error"},"default":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Error response"}},"summary":"ListStorageFacilities","tags":["SupplyChainService"]}}}}