Files
worldmonitor/docs/api/SupplyChainService.openapi.yaml
Elie Habib 822eef0fa6 feat(supply-chain): Sprint 1 — Route Explorer wrapper RPC (#2980)
* feat(supply-chain): Sprint 1 — Route Explorer wrapper RPC

Adds an internal wrapper around the vendor-only route-intelligence
compute so the upcoming Route Explorer UI can call it from a browser
PRO session instead of forcing an X-WorldMonitor-Key API gate.

Backend:
- New proto get-route-explorer-lane.proto with GetRouteExplorerLane{Request,Response}
- New handler server/worldmonitor/supply-chain/v1/get-route-explorer-lane.ts
- New static lookup tables _route-explorer-static-tables.ts:
  TRANSIT_DAYS_BY_ROUTE_ID, FREIGHT_USD_BY_CARGO_TYPE,
  BYPASS_CORRIDOR_GEOMETRY_BY_ID — covers all 5 land-bridge corridors
  plus every sea-alternative corridor with hand-curated coordinates
- Wired into supply-chain handler.ts service dispatcher
- Cache key ROUTE_EXPLORER_LANE_KEY in cache-keys.ts (NOT in BOOTSTRAP_KEYS)
- Gateway entry: PREMIUM_RPC_PATHS + RPC_CACHE_TIER 'slow-browser'
- Premium path entry in src/shared/premium-paths.ts so browser PRO auth attaches

Response contract enriches route-intelligence with:
- primaryRouteGeometry polyline from TRADE_ROUTES (lon/lat pairs)
- fromPort/toPort coords on every bypass option so the client can call
  MapContainer.setBypassRoutes directly without geometry lookups
- status: 'active' | 'proposed' | 'unavailable' derived from corridor notes
  to honestly label kra_canal_future and black_sea_western_ports
- estTransitDaysRange + estFreightUsdPerTeuRange from static tables
- noModeledLane: true when origin/destination clusters share no routes

Client wrapper fetchRouteExplorerLane added to src/services/supply-chain/index.ts.

Tests: tests/route-explorer-lane.test.mts — 30-query smoke matrix
(10 country pairs × 3 HS2 codes), structural assertions only, no
hard-coded transit/cost values. Test exposes a pure computeLane()
function with an injectable status map so it does not need Redis.

Gap report (from smoke run): 12 of 30 queries fall back to a synthetic
primaryRouteId because the destination's port cluster has no shared route
with the origin (US-JP, ZA-IN, CL-CN, TR-DE × 3 HS2 each). These pairs
return noModeledLane:true; Sprint 3 will render an empty-state for them.

Plan: docs/plans/2026-04-11-001-feat-worldwide-route-explorer-plan.md

* fix(route-explorer): address PR #2980 review findings

P1: bypass warRiskTier was hard-coded to WAR_RISK_TIER_NORMAL, dropping
the live risk signal from chokepoint status. Now derived from the
statusMap via the corridor's primaryChokepointId.

P2: freight fallback in emptyResponse and client-side empty payload used
a cargo-agnostic container range for all cargo types. Removed the ranges
entirely from fallback/noModeledLane responses; they are only present
when the lane is actually modeled.

Suggestion: when noModeledLane is true, the response now returns empty
primaryRouteId, empty geometry, empty exposures, empty bypasses, and
omits transit/freight ranges. Previously it returned plausible-looking
synthetic data from the origin's first route which could mislead the UI.

Tests updated to assert the noModeledLane contract: empty fields when
the flag is set, non-empty ranges only when the lane is modeled.

* fix(route-explorer): cargo-aware route ranking + bypass waypoint risk

P1: primary route selection was order-dependent, picking whichever
shared route the origin cluster listed first. Mixed clusters like
CN/JP could return an energy lane for a container request. Now ranks
shared routes by cargo-category compatibility (container→container,
tanker→energy, bulk→bulk, roro→container) before selecting.

P1: bypass warRiskTier was copied from the primary chokepoint instead
of derived from the corridor's own waypointChokepointIds. This
overstated risk for alternatives like Cape of Good Hope whose waypoints
may have a lower risk tier. Now uses max-tier across waypoint
chokepoints, matching get-bypass-options.ts logic.

Suggestion: placeholder corridors with addedTransitDays=0 (like
gibraltar_no_bypass, cape_of_good_hope_is_bypass) are now filtered out.
Previously they could surface as active alternatives.

Regression tests added:
- CN→JP tanker: asserts energy route is selected over container route
- CN→DE with faked Suez=CRITICAL / Cape=NORMAL: asserts Cape bypass
  shows NORMAL, not CRITICAL
- ES→EG: asserts zero-transit-day placeholders are excluded

* fix(route-explorer): scope exposures to primary route + narrow placeholder filter

P1: chokepointExposures and bypassOptions were computed from the full
sharedRoutes set, mixing data from energy/container corridors into a
single response. Now scoped to the cargo-ranked primaryRouteId only,
matching the proto contract that exposures are "on the primary route."

P2: the addedTransitDays === 0 filter was too broad and removed
kra_canal_future (a proposed bypass with real modeling). Narrowed to an
explicit PLACEHOLDER_CORRIDOR_IDS set (gibraltar_no_bypass,
cape_of_good_hope_is_bypass) so proposed zero-day corridors survive and
are surfaced with CORRIDOR_STATUS_PROPOSED.

Regression tests:
- chokepointExposures follow primaryRouteId (CN->JP container)
- kra_canal_future appears as CORRIDOR_STATUS_PROPOSED for Malacca routes
- placeholder filter still excludes explicit placeholders

* fix(route-explorer): address PR #2980 review comments

1. Unavailable corridors without waypoints (e.g. black_sea_western_ports)
   now derive WAR_RISK_TIER_WAR_ZONE from their CORRIDOR_STATUS_UNAVAILABLE
   status, instead of returning WAR_RISK_TIER_UNSPECIFIED. Corridors with
   waypointChokepointIds still use max-tier across those waypoints.

2. Added fixture test with non-empty status map (suez=75/HIGH,
   malacca=30/ELEVATED) so disruptionScore and warRiskTier assertions are
   not trivially satisfied by the empty-map default path.

3. Documented the single-chokepoint bypass design gap in the test gap report:
   bypassOptions only cover the primary chokepoint; multi-chokepoint routes
   show exposure for all but bypass guidance for only the top one. Sprint 3
   will decide whether to expand to top-N or add a UI hint.
2026-04-12 08:16:02 +04:00

1068 lines
41 KiB
YAML
Raw 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.
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-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-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'
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
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
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 0100 (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 0100 (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 0100; 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
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 (01). 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 (0100).
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.