All v2 shipping endpoints require
X-WorldMonitor-Key (server-to-server). Browser origins are not trusted here — validateApiKey runs with forceKey: true.Route intelligence
GET /api/v2/shipping/route-intelligence
Scores a country-pair trade route for chokepoint exposure and current disruption risk.
Query parameters:
Example:
200):
disruptionScoreis 0-100 on the primary chokepoint for the route (higher = more disruption).warRiskTieris one of theWAR_RISK_TIER_*enum values from the chokepoint status feed.bypassOptionsare filtered to those whosesuitableCargoTypesincludescargoType(or is unset).
Cache-Control: private, max-age=300, stale-while-revalidate=60, stale-if-error=1800 and no shared-cache header.
Errors:
Webhook subscriptions
POST /api/v2/shipping/webhooks
Registers a webhook for chokepoint disruption alerts. Returns 200 OK.
Request:
callbackUrl— required, HTTPS only, must not resolve to a private/loopback address (SSRF guard at registration).chokepointIds— optional. Omitting or passing an empty array subscribes to all registered chokepoints. Unknown IDs return400.alertThreshold— numeric 0-100 (default50). Values outside that range return a400validation response with descriptionalertThreshold must be between 0 and 100.
200):
subscriberId—wh_prefix + 24 hex chars (12 random bytes).secret— raw 64-char lowercase hex (32 random bytes). There is nowhsec_prefix. Persist it — the server never returns it again except on rotation.- TTL: 30 days on both the subscriber record and the per-owner index set. Only re-registration refreshes both, via a non-transactional pipeline (
SETrecord withEX,SADD+EXPIREon the owner index).rotate-secretandreactivaterefresh the record’s TTL only — they do not touch the owner-index set’s expiry, so the owner index can expire independently if a caller only ever rotates or reactivates within a 30-day window. Re-register to keep both alive. - Registration and listing each scan one owner-index page and check at most 100 subscriber records for expiry. Confirmed expired members are removed only if the record is still absent. Scan progress resumes on later calls. Redis may return more than 100 IDs in a scan page; listing still enumerates the full index. Redis read or cleanup failures return 503 and preserve ambiguous members.
- Registration returns
503if any of the three writes cannot be confirmed. Partial records or index entries can remain; a retry can create another registration. No rollback is guaranteed. - Ownership is tracked via SHA-256 of the caller’s API key (never secret — stored as
ownerTag).
X-WorldMonitor-Key (forceKey: true) + PRO. Returns 401 / 403 otherwise.
GET /api/v2/shipping/webhooks
Lists the caller’s registered webhooks (filtered by the SHA-256 owner tag of the calling API key).
secret is intentionally omitted from list and status responses.
The three
{subscriberId} sub-routes below are hand-written edge handlers, not generated gateway RPCs: they do not appear in the published OpenAPI spec (ShippingV2Service) or in generated clients, and their 403 message uses the literal PRO subscription required (all-caps), unlike the gateway routes’ Pro subscription required.GET /api/v2/shipping/webhooks/{subscriberId}
Status read for a single webhook. Returns the same record shape as in GET /webhooks (no secret). 404 if unknown, 403 if owned by a different API key.
POST /api/v2/shipping/webhooks/{subscriberId}/rotate-secret
Generates and returns a new secret. The record’s secret is replaced in place; the old secret stops validating immediately.
POST /api/v2/shipping/webhooks/{subscriberId}/reactivate
Flips active: true on the record (use after investigating and fixing a delivery failure that caused deactivation).
Delivery format
callbackUrl before each send and re-checks against PRIVATE_HOSTNAME_PATTERNS to mitigate DNS rebinding. Delivery is at-least-once — consumers must handle duplicates via X-WM-Delivery-Id.
Verifying deliveries
Every delivery is signed so you can confirm it genuinely came from WorldMonitor.X-WM-Signature is sha256=<hex>, where <hex> is the lowercase-hex HMAC-SHA256 of the exact raw request body, keyed by the secret returned at registration.
To verify: recompute sha256= + hex(HMAC_SHA256(key=secret, message=rawBody)) over the bytes exactly as received (do not re-serialize the JSON), and compare against X-WM-Signature in constant time. Use the secret string verbatim as the HMAC key — do not hex-decode it. Reject the delivery if the signatures differ.
chokepoint.disruption entry under webhooks in the OpenAPI spec.
Test your verification against a signed sample
A ready-to-verify sample delivery is published at/.well-known/webhook-sample.json. It carries a fixed sample secret, the exact raw body string, and the resulting signature. Recompute sha256= + hex(HMAC_SHA256(key=secret, message=body)) over the exact bytes of body and confirm it equals signature — if it matches, your verification will accept real deliveries. (The sample secret is a fixture; each live subscription gets its own secret from RegisterWebhook.)
