Skip to content

Commit 019fac0

Browse files
koala73e
andauthored
feat(blog): add six use-case posts (MCP, API tutorial, workflows) (#4260)
* feat(blog): add six use-case posts targeting SEO and developer adoption Shifts blog strategy from feature tours toward job-to-be-done content: MCP server for AI agents, supply-chain early-warning API tutorial, country-risk monitoring workflow, journalist OSINT verification, CII methodology explainer (AEO), and a 15-minute morning briefing routine. All facts grounded in docs/ (endpoints, quotas, weights). Includes 1200x630 hero images, sitemap lastmod entries, and contextual cross-links from the two most-related existing posts. Roadmap for the next 10 posts: #4258 * fix(blog): make supply-chain webhook receiver snippet actually runnable Address Greptile review on PR #4260: the Express receiver claimed to be "working" but `req.rawBody` is undefined without an explicit verify hook on express.json(), and `seenDeliveries` was referenced without being declared or populated. Add the middleware wiring, declare the dedup Set, call .add() after processing, and note the raw-bytes/TTL caveats. --------- Co-authored-by: e <e@e.co>
1 parent c2d8eab commit 019fac0

15 files changed

Lines changed: 702 additions & 2 deletions

blog-site/astro.config.mjs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,13 @@ const POST_DATES = {
2020
'https://www.worldmonitor.app/blog/posts/what-is-worldmonitor-real-time-global-intelligence/': '2026-02-10',
2121
'https://www.worldmonitor.app/blog/posts/worldmonitor-in-21-languages-global-intelligence-for-everyone/': '2026-03-04',
2222
'https://www.worldmonitor.app/blog/posts/worldmonitor-vs-traditional-intelligence-tools/': '2026-03-11',
23-
'https://www.worldmonitor.app/blog/': '2026-03-19',
23+
'https://www.worldmonitor.app/blog/posts/country-instability-index-methodology-explained/': '2026-05-30',
24+
'https://www.worldmonitor.app/blog/posts/daily-intelligence-briefing-workflow-15-minutes/': '2026-06-02',
25+
'https://www.worldmonitor.app/blog/posts/verify-breaking-news-osint-workflow-journalists/': '2026-06-04',
26+
'https://www.worldmonitor.app/blog/posts/country-risk-monitoring-workflow-for-analysts/': '2026-06-06',
27+
'https://www.worldmonitor.app/blog/posts/build-supply-chain-early-warning-system-api/': '2026-06-08',
28+
'https://www.worldmonitor.app/blog/posts/worldmonitor-mcp-server-ai-agents-real-time-intelligence/': '2026-06-10',
29+
'https://www.worldmonitor.app/blog/': '2026-06-10',
2430
};
2531

2632
export default defineConfig({
74.7 KB
Loading
114 KB
Loading
72.8 KB
Loading
68.3 KB
Loading
79.6 KB
Loading
114 KB
Loading

blog-site/src/content/blog/build-on-worldmonitor-developer-api-open-source.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,8 @@ World Monitor's open, typed, proto-first architecture is the alternative:
196196

197197
The intelligence platform of the future isn't a product. It's an ecosystem. World Monitor is building the foundation.
198198

199+
Building an AI agent instead of an app? The same platform is exposed as a Model Context Protocol server with 39 live tools — see [how to connect Claude and other agents to World Monitor's MCP server](/blog/posts/worldmonitor-mcp-server-ai-agents-real-time-intelligence/).
200+
199201
## Frequently Asked Questions
200202

201203
**Is the World Monitor API free to use?**
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
---
2+
title: "Build a Supply-Chain Early-Warning System in an Afternoon"
3+
description: "Score your trade lanes for chokepoint exposure, subscribe to disruption webhooks, and pipe alerts to Slack — a working tutorial on the World Monitor API."
4+
metaTitle: "Supply Chain Risk API Tutorial: Chokepoint Alerts | World Monitor"
5+
keywords: "supply chain risk API, chokepoint monitoring API, shipping disruption alerts, maritime risk API, trade route risk scoring, supply chain early warning system"
6+
audience: "Supply chain engineers, logistics developers, procurement analysts, platform teams, risk managers"
7+
heroImage: "/blog/images/blog/build-supply-chain-early-warning-system-api.jpg"
8+
pubDate: "2026-06-08"
9+
---
10+
11+
When the Strait of Hormuz shut down this spring, companies found out in one of two ways. Some read about it in the news and started calling freight forwarders. Others had already received a webhook hours earlier, when the disruption score crossed their alert threshold, and were quoting Cape of Good Hope routings before their competitors knew there was a problem.
12+
13+
The second group did not have a $50,000-a-year risk platform. The capability they used — [live chokepoint tracking](/blog/posts/tracking-global-trade-routes-chokepoints-freight-costs/) with programmatic alerts — is part of World Monitor's API. This post builds that early-warning system end to end: score your lanes, subscribe to alerts, verify deliveries, and route them to Slack.
14+
15+
You need an API key (`X-WorldMonitor-Key`, issued with [Pro or API plans](https://www.worldmonitor.app/pro)) and about an afternoon.
16+
17+
## The Architecture
18+
19+
Three signals, three endpoints:
20+
21+
1. **Lane exposure** — which chokepoints does each of my trade lanes depend on? (`/api/v2/shipping/route-intelligence`)
22+
2. **Live disruption** — push notification when a chokepoint's disruption score crosses my threshold (`/api/v2/shipping/webhooks`)
23+
3. **Country context** — structural resilience of origin and destination countries (`/api/resilience/v1/get-resilience-score`)
24+
25+
A small receiver service glues them together and posts to Slack.
26+
27+
## Step 1: Score Your Lanes
28+
29+
For each lane you ship, ask the route-intelligence endpoint what it crosses and how disrupted that is right now:
30+
31+
```bash
32+
curl -s 'https://api.worldmonitor.app/api/v2/shipping/route-intelligence?fromIso2=AE&toIso2=NL&cargoType=tanker&hs2=27' \
33+
-H 'X-WorldMonitor-Key: wm_YOUR_KEY'
34+
```
35+
36+
The response tells you everything a routing decision needs:
37+
38+
```json
39+
{
40+
"primaryRouteId": "ae-to-eu-via-hormuz-suez",
41+
"chokepointExposures": [
42+
{ "chokepointId": "hormuz_strait", "chokepointName": "Strait of Hormuz", "exposurePct": 100 },
43+
{ "chokepointId": "suez", "chokepointName": "Suez Canal", "exposurePct": 100 }
44+
],
45+
"bypassOptions": [
46+
{
47+
"name": "Cape of Good Hope",
48+
"type": "maritime_detour",
49+
"addedTransitDays": 12,
50+
"addedCostMultiplier": 1.35,
51+
"activationThreshold": "DISRUPTION_SCORE_60"
52+
}
53+
],
54+
"warRiskTier": "WAR_RISK_TIER_ELEVATED",
55+
"disruptionScore": 68
56+
}
57+
```
58+
59+
Read it like this: this tanker lane is 100% exposed to both Hormuz and Suez, the current disruption score on the primary chokepoint is 68/100, and the documented bypass adds 12 transit days at a 1.35× cost multiplier. `cargoType` matters — bypass options are filtered to corridors suitable for your cargo (`container`, `tanker`, `bulk`, or `roro`), and `hs2` lets you scope by commodity chapter.
60+
61+
Run this once for every lane in your network and you have an exposure matrix: which chokepoints, at what percentage, with what fallback. Most teams discover that 70% of their volume funnels through two or three waterways.
62+
63+
## Step 2: Subscribe to Disruption Webhooks
64+
65+
Polling is for prototypes. Register a webhook for the chokepoints your matrix surfaced:
66+
67+
```bash
68+
curl -s -X POST 'https://api.worldmonitor.app/api/v2/shipping/webhooks' \
69+
-H 'X-WorldMonitor-Key: wm_YOUR_KEY' \
70+
-H 'Content-Type: application/json' \
71+
-d '{
72+
"callbackUrl": "https://alerts.yourcompany.com/wm-shipping",
73+
"chokepointIds": ["hormuz_strait", "suez", "bab_el_mandeb"],
74+
"alertThreshold": 60
75+
}'
76+
```
77+
78+
The `201` response returns a `subscriberId` and a one-time `secret` — persist it; the server never shows it again (there is a `rotate-secret` endpoint when you need a new one). Omitting `chokepointIds` subscribes you to all 13 monitored chokepoints. Subscriptions expire after 30 days, so re-register on a monthly cron to keep both the record and the owner index alive.
79+
80+
When a chokepoint's disruption score crosses your threshold, you get:
81+
82+
```
83+
POST https://alerts.yourcompany.com/wm-shipping
84+
X-WM-Signature: sha256=<HMAC-SHA256(body, secret)>
85+
X-WM-Delivery-Id: <ulid>
86+
X-WM-Event: chokepoint.disruption
87+
88+
{
89+
"chokepointId": "hormuz_strait",
90+
"score": 74,
91+
"alertThreshold": 60,
92+
"triggeredAt": "2026-06-08T12:03:00Z",
93+
"reason": "ais_congestion_spike"
94+
}
95+
```
96+
97+
## Step 3: Verify and Route to Slack
98+
99+
Never trust an unverified webhook. The signature is a standard HMAC over the raw body:
100+
101+
```js
102+
import express from 'express';
103+
import { createHmac, timingSafeEqual } from 'node:crypto';
104+
105+
const app = express();
106+
107+
// Express does not expose the raw body by default, but HMAC must be
108+
// computed over the exact bytes that were signed. Capture them here.
109+
app.use(express.json({
110+
verify: (req, _res, buf) => { req.rawBody = buf; },
111+
}));
112+
113+
function verify(rawBody, signatureHeader, secret) {
114+
const expected = createHmac('sha256', secret).update(rawBody).digest('hex');
115+
const received = (signatureHeader || '').replace('sha256=', '');
116+
return received.length === expected.length &&
117+
timingSafeEqual(Buffer.from(received), Buffer.from(expected));
118+
}
119+
120+
// Remembers delivery IDs we have already processed. Use a TTL cache or a
121+
// shared store (Redis) in production — an unbounded Set leaks memory.
122+
const seenDeliveries = new Set();
123+
124+
app.post('/wm-shipping', (req, res) => {
125+
if (!verify(req.rawBody, req.headers['x-wm-signature'], SECRET)) {
126+
return res.status(401).end();
127+
}
128+
129+
const deliveryId = req.headers['x-wm-delivery-id'];
130+
if (seenDeliveries.has(deliveryId)) return res.status(200).end();
131+
seenDeliveries.add(deliveryId);
132+
133+
const { chokepointId, score, reason } = req.body;
134+
postToSlack(`:rotating_light: ${chokepointId} disruption at ${score}/100 (${reason}). ` +
135+
`Affected lanes: ${lanesExposedTo(chokepointId).join(', ')}`);
136+
res.status(200).end();
137+
});
138+
```
139+
140+
Three production notes from the delivery contract: the HMAC must be computed over the **raw request bytes**, which is why the `express.json` `verify` hook stashes `req.rawBody` — recomputing it from the parsed object will not match; delivery is **at-least-once**, so deduplicate on `X-WM-Delivery-Id` (back the Set with a TTL cache or Redis so it does not grow forever); and repeated delivery failures deactivate the subscription, so wire up the `reactivate` endpoint in your runbook.
141+
142+
The `lanesExposedTo()` lookup is your exposure matrix from Step 1 — that is what turns a generic "Hormuz is disrupted" alert into "your AE→NL tanker lane just lost its primary route; the Cape bypass costs 1.35× and 12 extra days."
143+
144+
## Step 4: Add Country Context
145+
146+
Chokepoints are not the only failure mode. A supplier country sliding into instability disrupts production before anything reaches a port. Pull structural resilience for your origin countries:
147+
148+
```bash
149+
curl -s 'https://api.worldmonitor.app/api/resilience/v1/get-resilience-score?countryCode=EG' \
150+
-H 'X-WorldMonitor-Key: wm_YOUR_KEY'
151+
```
152+
153+
You get a 0–100 resilience score with per-domain breakdowns (energy, infrastructure, governance, security and more), a trend, and a 30-day change — computed across 196 countries and refreshed every six hours. Combine it with the real-time [Country Instability Index](/blog/posts/country-instability-index-methodology-explained/) and you cover both clocks: CII for what is burning this week, resilience for which countries absorb shocks and which shatter.
154+
155+
A simple weekly job that flags any origin country whose resilience dropped more than a few points in 30 days catches slow-burn deterioration that no chokepoint webhook will ever see.
156+
157+
## What You End Up With
158+
159+
- An **exposure matrix** mapping every lane to its chokepoints, bypasses, and current disruption scores
160+
- **Push alerts** within minutes of a disruption-score threshold crossing, signed and deduplicated
161+
- **Country-level early warning** on supplier fragility, refreshed every six hours
162+
- A Slack channel that occasionally says something genuinely important
163+
164+
Total code: one webhook receiver and two cron jobs. If you want to stress-test the design, the [scenario engine](https://www.worldmonitor.app/docs/scenario-engine) simulates events like a Taiwan Strait closure or a Panama drought against live trade data — and AI agents can run the same checks conversationally through the [MCP server](/blog/posts/worldmonitor-mcp-server-ai-agents-real-time-intelligence/).
165+
166+
## Frequently Asked Questions
167+
168+
**Which chokepoints can I monitor?**
169+
170+
All 13 strategic waterways World Monitor tracks, including the Strait of Hormuz, Suez Canal, Bab el-Mandeb, Strait of Malacca, Panama Canal, Taiwan Strait, Bosporus, Kerch Strait, and the Cape of Good Hope bypass corridor.
171+
172+
**How fresh is the disruption data?**
173+
174+
Chokepoint transit counts blend IMF PortWatch weekly baselines with real-time AIS crossing counters; disruption scores update continuously and route-intelligence responses are cached for at most 60 seconds.
175+
176+
**Do I need to host my own receiver?**
177+
178+
For webhooks, yes — any HTTPS endpoint works (private and loopback addresses are rejected at registration). If you just want notifications without code, Pro accounts can route alerts to Slack, Discord, Telegram, or email through the built-in notification channels instead.
179+
180+
---
181+
182+
**Get an API key at [worldmonitor.app/pro](https://www.worldmonitor.app/pro), pull the full OpenAPI spec from [worldmonitor.app/openapi.yaml](https://www.worldmonitor.app/openapi.yaml), and ship the early-warning system your freight forwarder thinks you bought from someone expensive.**

0 commit comments

Comments
 (0)