mirror of
https://github.com/koala73/worldmonitor.git
synced 2026-04-25 17:14:57 +02:00
* docs(mintlify): cover MCP, OAuth, non-RPC endpoints, and usage Audit against api/ + proto/ revealed 9 OpenAPI specs missing from nav, the scenario/v1 service undocumented, and MCP (32 tools + OAuth 2.1 flow) with no user-facing docs. The stale Docs_To_Review/API_REFERENCE.md still pointed at pre-migration endpoints that no longer exist. - Wire 9 orphaned specs into docs.json: ConsumerPrices, Forecast, Health, Imagery, Radiation, Resilience, Sanctions, Thermal, Webcam - Hand-write ScenarioService.openapi.yaml (3 RPCs) until it's proto-backed (tracked in issue #3207) - New MCP page with tool catalog + client setup (Claude Desktop/web, Cursor) - New MDX for OAuth, Platform, Brief, Commerce, Notifications, Shipping v2, Proxies - New Usage group: quickstart, auth matrix, rate limits, errors - Remove docs/Docs_To_Review/API_REFERENCE.md and EXTERNAL_APIS.md (referenced dead endpoints); add README flagging dir as archival * docs(mintlify): move scenario docs out of generated docs/api/ tree The pre-push hook enforces that docs/api/ is proto-generated only. Replace the hand-written ScenarioService.openapi.yaml with a plain MDX page (docs/api-scenarios.mdx) until the proto migration lands (tracked in issue #3207). * docs(mintlify): fix factual errors flagged in PR review Reviewer caught 5 endpoints where I speculated on shape/method/limits instead of reading the code. All fixes cross-checked against the source: - api-shipping-v2: route-intelligence is GET with query params (fromIso2, toIso2, cargoType, hs2), not POST with a JSON body. Response shape is {primaryRouteId, chokepointExposures[], bypassOptions[], warRiskTier, disruptionScore, ...}. - api-commerce: /api/product-catalog returns {tiers, fetchedAt, cachedUntil, priceSource} with tier groups free|pro|api_starter| enterprise, not the invented {currency, plans}. Document the DELETE purge path too. - api-notifications: Slack/Discord /oauth/start are POST + Clerk JWT + PRO (returning {oauthUrl}), not GET redirects. Callbacks remain GET. - api-platform: /api/version returns the latest GitHub Release ({version, tag, url, prerelease}), not deployed commit/build metadata. - api-oauth + mcp: /api/oauth/register limit is 5/60s/IP (match code), not 10/hour. Also caught while double-checking: /api/register-interest and /api/contact are 5/60min and 3/60min respectively (1-hour window, not 1-minute). Both require Turnstile. Removed the fabricated limits for share-url, notification-channels, create-checkout (they fall back to the default per-IP limit). * docs(mintlify): second-round fixes — verify every claim against source Reviewer caught 7 more cases where I described API behavior I hadn't read. Each fix below cross-checked against the handler. - api-commerce (product-catalog): tiers are flat objects with monthlyPrice/annualPrice/monthlyProductId/annualProductId on paid tiers, price+period for free, price:null for enterprise. There is no nested plans[] array. - api-commerce (referral/me): returns {code, shareUrl}, not counts. Code is a deterministic 8-char HMAC of the Clerk userId; binding into Convex is fire-and-forget via ctx.waitUntil. - api-notifications (notification-channels): actual action set is create-pairing-token, set-channel, set-web-push, delete-channel, set-alert-rules, set-quiet-hours, set-digest-settings. Replaced the made-up list. - api-shipping-v2 (webhooks): alertThreshold is numeric 0-100 (default 50), not a severity string. Subscriber IDs are wh_+24hex; secret is raw 64-char hex (no whsec_ prefix). POST registration returns 201. Added the management routes: GET /{id}, POST /{id}/rotate-secret, POST /{id}/reactivate. - api-platform (cache-purge): auth is Authorization: Bearer RELAY_SHARED_SECRET, not an admin-key header. Body takes keys[] and/or patterns[] (not {key} or {tag}), with explicit per-request caps and prefix-blocklist behavior. - api-platform (download): platform+variant query params, not file=<id>. Response is a 302 to a GitHub release asset; documented the full platform/variant tables. - mcp: server also accepts direct X-WorldMonitor-Key in addition to OAuth bearer. Fixed the curl example which was incorrectly sending a wm_live_ API key as a bearer token. - api-notifications (youtube/live): handler reads channel or videoId, not channelId. - usage-auth: corrected the auth-matrix row for /api/mcp to reflect that OAuth is one of two accepted modes. * docs(mintlify): fix Greptile review findings - mcp.mdx: 'Five' slow tools → 'Six' (list contains 6 tools) - api-scenarios.mdx: replace invalid JSON numeric separator (8_400_000_000) with plain integer (8400000000) Greptile's third finding — /api/oauth/register rate-limit contradiction across api-oauth.mdx / mcp.mdx / usage-rate-limits.mdx — was already resolved in commit4f2600b2a(reviewed commit waseb5654647).
82 lines
3.2 KiB
Plaintext
82 lines
3.2 KiB
Plaintext
---
|
||
title: "AI Brief Endpoints"
|
||
description: "Read, share, and render the AI-composed daily intelligence brief."
|
||
---
|
||
|
||
WorldMonitor composes a per-user **daily intelligence brief** on Railway, stores it in Redis at `brief:{userId}:{issueDate}`, and exposes these routes for dashboard readback, public sharing, and Telegram/Slack carousel rendering.
|
||
|
||
<Info>
|
||
All read routes require a valid Clerk session and a PRO tier, except the public share route (`/api/brief/public/{hash}`).
|
||
</Info>
|
||
|
||
## Latest brief (authenticated)
|
||
|
||
### `GET /api/latest-brief`
|
||
|
||
Returns a short summary of the caller's most recent composed brief, or `{ status: "composing" }` if today's run hasn't produced a brief yet.
|
||
|
||
| Status | Response |
|
||
|--------|----------|
|
||
| 200 OK | `{ issueDate, dateLong, greeting, threadCount, magazineUrl }` |
|
||
| 200 OK | `{ status: "composing" }` — no brief for today yet |
|
||
| 401 | Missing / invalid Clerk JWT |
|
||
| 403 | `pro_required` |
|
||
| 503 | `BRIEF_URL_SIGNING_SECRET` not configured |
|
||
|
||
The `magazineUrl` is a freshly-signed URL — the HMAC binds `{userId, issueDate}` so it only works for the authenticated owner.
|
||
|
||
### `GET /api/brief/{userId}/{issueDate}`
|
||
|
||
Full brief body for `issueDate` (YYYY-MM-DD). HMAC-signed URL required. Returns the structured brief JSON used by the magazine reader view.
|
||
|
||
## Sharing
|
||
|
||
### `POST /api/brief/share-url?date=YYYY-MM-DD`
|
||
|
||
Materialises a public share pointer for the caller's brief on `date`. Idempotent — hash is a pure function of `{userId, issueDate, BRIEF_SHARE_SECRET}`.
|
||
|
||
| Status | Response |
|
||
|--------|----------|
|
||
| 200 | `{ shareUrl, hash, issueDate }` |
|
||
| 400 | `invalid_date_shape` / `invalid_payload` |
|
||
| 401 | `UNAUTHENTICATED` |
|
||
| 403 | `pro_required` |
|
||
| 404 | `brief_not_found` — reader can't share what doesn't exist |
|
||
| 503 | `service_unavailable` |
|
||
|
||
### `GET /api/brief/public/{hash}`
|
||
|
||
**Unauthenticated** public read of a previously-shared brief. The hash resolves to a `brief:public:{hash} → {userId, issueDate}` Redis pointer; if absent, the brief was never shared. Share pointers are written lazily (on share, not on compose).
|
||
|
||
## Carousel (images for social)
|
||
|
||
### `GET /api/brief/carousel/{userId}/{issueDate}/{page}.png`
|
||
|
||
Server-rendered PNG page (`page` = 1..N) of the brief, intended for Telegram `sendMediaGroup`, Slack `chat.postMessage`, LinkedIn, etc.
|
||
|
||
- Rendered via `@resvg/resvg-js` with the bundled Linux native binding.
|
||
- `Content-Type: image/png`, 1080×1350 (4:5 portrait).
|
||
- Not gated — uses the HMAC'd path as the capability.
|
||
|
||
## Ancillary
|
||
|
||
### `GET /api/story?date=YYYY-MM-DD`
|
||
|
||
Public read-only "story view" (web reader) for a shared brief. SEO-friendly HTML response.
|
||
|
||
### `GET /api/og-story?date=YYYY-MM-DD`
|
||
|
||
Open Graph preview image for `/api/story`. Returns `image/png`, cached aggressively.
|
||
|
||
### `POST /api/chat-analyst`
|
||
|
||
Streaming chat endpoint for the "Ask the analyst" in-dashboard assistant. Takes a user prompt + recent-signal context; returns SSE tokens.
|
||
|
||
- Auth: Clerk JWT + PRO
|
||
- Streams: `text/event-stream`
|
||
- Back-end: `intelligence/v1/chat-analyst-*` handlers compose context + prompt
|
||
|
||
### `POST /api/widget-agent`
|
||
|
||
Single-shot completion endpoint used by embedded widget iframes. Auth via `X-WorldMonitor-Key` (partner keys). Rate-limited per key.
|