Files
worldmonitor/docs/api/SupplyChainService.openapi.yaml
Elie Habib a742537ae5 feat(supply-chain): Sprint D — GetSectorDependency RPC + vendor route-intelligence API + webhooks (#2905)
* feat(supply-chain): Sprint D — GetSectorDependency RPC + vendor route-intelligence API + webhooks

* fix(supply-chain): move bypass-corridors + chokepoint-registry to server/_shared to fix api/ boundary violations

* fix(supply-chain): webhooks — persist secret, fix sub-resource routing, add ownership check

* fix(supply-chain): address PR #2905 review findings

- Use SHA-256(apiKey) for ownerTag instead of last-12-chars (unambiguous ownership)
- Implement GET /api/v2/shipping/webhooks list route via per-owner Redis Set index
- Tighten SSRF: https-only, expanded metadata hostname blocklist, document DNS rebinding edge-runtime limitation
- Fix get-sector-dependency.ts stale src/config/ imports → server/_shared/ (Greptile P1)

* fix(supply-chain): getSectorDependency returns blank primaryChokepointId for landlocked countries

computeExposures() previously mapped over all of CHOKEPOINT_REGISTRY even
when nearestRouteIds was empty, producing a full array of score-0 entries
in registry insertion order. The caller's exposures[0] then picked the
first registry entry (Suez) as the "primary" chokepoint despite
primaryChokepointExposure = 0. LI, AD, SM, BT and other landlocked
countries were all silently assigned a fake chokepoint.

Fix: guard at the top of computeExposures() -- return [] when input is
empty so primaryChokepointId stays '' and primaryChokepointExposure stays 0.
2026-04-10 17:12:29 +04:00

886 lines
34 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'
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