openapi: 3.1.0 info: title: ShippingV2Service API version: 1.0.0 paths: /api/v2/shipping/route-intelligence: get: tags: - ShippingV2Service summary: RouteIntelligence description: |- RouteIntelligence scores a country-pair trade route for chokepoint exposure and current disruption risk. Partner-facing; wire shape is byte-compatible with the pre-migration JSON response documented at docs/api-shipping-v2.mdx. operationId: RouteIntelligence parameters: - name: fromIso2 in: query description: Origin country, ISO-3166-1 alpha-2 uppercase. required: false schema: type: string - name: toIso2 in: query description: Destination country, ISO-3166-1 alpha-2 uppercase. required: false schema: type: string - name: cargoType in: query description: |- Cargo type — one of: container (default), tanker, bulk, roro. Empty string defers to the server default. Unknown values are coerced to "container" to preserve legacy behavior. required: false schema: type: string - name: hs2 in: query description: |- 2-digit HS commodity code (default "27" — mineral fuels). Non-digit characters are stripped server-side to match legacy behavior. required: false schema: type: string responses: "200": description: Successful response content: application/json: schema: $ref: '#/components/schemas/RouteIntelligenceResponse' "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/v2/shipping/webhooks: get: tags: - ShippingV2Service summary: ListWebhooks description: |- ListWebhooks returns the caller's registered webhooks filtered by the SHA-256 owner tag of the calling API key. The `secret` is intentionally omitted from the response; use rotate-secret to obtain a new one. operationId: ListWebhooks responses: "200": description: Successful response content: application/json: schema: $ref: '#/components/schemas/ListWebhooksResponse' "400": description: Validation error content: application/json: schema: $ref: '#/components/schemas/ValidationError' default: description: Error response content: application/json: schema: $ref: '#/components/schemas/Error' post: tags: - ShippingV2Service summary: RegisterWebhook description: |- RegisterWebhook subscribes a callback URL to chokepoint disruption alerts. Returns the subscriberId and the raw HMAC secret — the secret is never returned again except via rotate-secret. operationId: RegisterWebhook requestBody: content: application/json: schema: $ref: '#/components/schemas/RegisterWebhookRequest' required: true responses: "200": description: Successful response content: application/json: schema: $ref: '#/components/schemas/RegisterWebhookResponse' "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. RouteIntelligenceRequest: type: object properties: fromIso2: type: string pattern: ^[A-Z]{2}$ description: Origin country, ISO-3166-1 alpha-2 uppercase. toIso2: type: string pattern: ^[A-Z]{2}$ description: Destination country, ISO-3166-1 alpha-2 uppercase. cargoType: type: string description: |- Cargo type — one of: container (default), tanker, bulk, roro. Empty string defers to the server default. Unknown values are coerced to "container" to preserve legacy behavior. hs2: type: string description: |- 2-digit HS commodity code (default "27" — mineral fuels). Non-digit characters are stripped server-side to match legacy behavior. required: - fromIso2 - toIso2 description: |- RouteIntelligenceRequest scopes a route-intelligence query by origin and destination country. Query-parameter names are preserved verbatim from the legacy partner contract (fromIso2/toIso2/cargoType/hs2 — camelCase). RouteIntelligenceResponse: type: object properties: fromIso2: type: string toIso2: type: string cargoType: type: string hs2: type: string primaryRouteId: type: string chokepointExposures: type: array items: $ref: '#/components/schemas/ChokepointExposure' bypassOptions: type: array items: $ref: '#/components/schemas/BypassOption' warRiskTier: type: string description: War-risk tier enum string, e.g., "WAR_RISK_TIER_NORMAL" or "WAR_RISK_TIER_ELEVATED". disruptionScore: type: integer format: int32 description: Disruption score of the primary chokepoint, 0-100. fetchedAt: type: string description: ISO-8601 timestamp of when the response was assembled. description: |- RouteIntelligenceResponse wire shape preserved byte-for-byte from the pre-migration JSON at docs/api-shipping-v2.mdx. `fetched_at` is intentionally a string (ISO-8601) rather than int64 epoch ms because partners depend on the ISO-8601 shape. ChokepointExposure: type: object properties: chokepointId: type: string chokepointName: type: string exposurePct: type: integer format: int32 description: Single chokepoint exposure for a route. BypassOption: type: object properties: id: type: string name: type: string type: type: string description: Type of bypass (e.g., "maritime_detour", "land_corridor"). addedTransitDays: type: integer format: int32 addedCostMultiplier: type: number format: double activationThreshold: type: string description: Enum-like string, e.g., "DISRUPTION_SCORE_60". description: Single bypass-corridor option around a disrupted chokepoint. RegisterWebhookRequest: type: object properties: callbackUrl: type: string maxLength: 2048 minLength: 8 description: |- HTTPS callback URL. Must not resolve to a private/loopback address at registration time (SSRF guard). The delivery worker re-validates the resolved IP before each send to mitigate DNS rebinding. chokepointIds: type: array items: type: string description: |- Zero or more chokepoint IDs to subscribe to. Empty list subscribes to the entire CHOKEPOINT_REGISTRY. Unknown IDs fail with 400. alertThreshold: type: integer maximum: 100 minimum: 0 format: int32 description: Disruption-score threshold for delivery, 0-100. Default 50. required: - callbackUrl description: |- RegisterWebhookRequest creates a new chokepoint-disruption webhook subscription. Wire shape is byte-compatible with the pre-migration legacy POST body. RegisterWebhookResponse: type: object properties: subscriberId: type: string description: '`wh_` prefix + 24 lowercase hex chars (12 random bytes).' secret: type: string description: Raw 64-char lowercase hex secret (32 random bytes). No `whsec_` prefix. description: |- RegisterWebhookResponse wire shape preserved exactly — partners persist the `secret` because the server never returns it again except via rotate-secret. ListWebhooksRequest: type: object description: |- ListWebhooksRequest has no fields — the owner is derived from the caller's API-key fingerprint (SHA-256 of X-WorldMonitor-Key). ListWebhooksResponse: type: object properties: webhooks: type: array items: $ref: '#/components/schemas/WebhookSummary' description: |- ListWebhooksResponse wire shape preserved exactly: the `webhooks` field name and the omission of `secret` are part of the partner contract. WebhookSummary: type: object properties: subscriberId: type: string callbackUrl: type: string chokepointIds: type: array items: type: string alertThreshold: type: integer format: int32 createdAt: type: string description: ISO-8601 timestamp of registration. active: type: boolean description: |- Single webhook record in the list response. `secret` is intentionally omitted; use rotate-secret to obtain a new one.