Skip to main content
The v2 shipping API is a PRO-gated read + webhook-subscription surface on top of WorldMonitor’s chokepoint registry and AIS tracking data.
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:
Response (200):
  • disruptionScore is 0-100 on the primary chokepoint for the route (higher = more disruption).
  • warRiskTier is one of the WAR_RISK_TIER_* enum values from the chokepoint status feed.
  • bypassOptions are filtered to those whose suitableCargoTypes includes cargoType (or is unset).
Caching: not CDN-cached — the route is premium-gated, so the gateway emits 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 return 400.
  • alertThreshold — numeric 0-100 (default 50). Values outside that range return a 400 validation response with description alertThreshold must be between 0 and 100.
Response (200):
  • subscriberIdwh_ prefix + 24 hex chars (12 random bytes).
  • secret — raw 64-char lowercase hex (32 random bytes). There is no whsec_ 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 (SET record with EX, SADD + EXPIRE on the owner index). rotate-secret and reactivate refresh 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 503 if 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).
Auth: 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).
The 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

The delivery worker re-resolves 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.
The signature contract is also published machine-readably as the 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.)