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).
73 lines
3.3 KiB
Plaintext
73 lines
3.3 KiB
Plaintext
---
|
|
title: "Errors"
|
|
description: "Standard error shape, common status codes, and retry strategies."
|
|
---
|
|
|
|
## Error shape
|
|
|
|
All error responses are JSON with `Content-Type: application/json`:
|
|
|
|
```json
|
|
{ "error": "brief_not_found" }
|
|
```
|
|
|
|
Some endpoints include additional fields:
|
|
|
|
```json
|
|
{
|
|
"error": "invalid_request",
|
|
"error_description": "iso2 must be a 2-letter uppercase country code",
|
|
"field": "iso2"
|
|
}
|
|
```
|
|
|
|
OAuth endpoints follow [RFC 6749 §5.2](https://datatracker.ietf.org/doc/html/rfc6749#section-5.2) error codes: `invalid_request`, `invalid_client`, `invalid_grant`, `unsupported_grant_type`, `invalid_scope`.
|
|
|
|
## Status codes
|
|
|
|
| Code | Meaning | Retry? |
|
|
|------|---------|--------|
|
|
| `200` | OK | — |
|
|
| `202` | Accepted — job enqueued (e.g. `scenario/v1/run`). Poll `statusUrl`. | — |
|
|
| `304` | Not Modified (conditional cache hit) | — |
|
|
| `400` | Bad request — validation error | No — fix input |
|
|
| `401` | Missing / invalid auth | No — fix auth |
|
|
| `403` | Authenticated but not entitled (usually `pro_required`) | No — upgrade |
|
|
| `404` | Resource / tool / brief / entity not found | No |
|
|
| `405` | Method not allowed | No |
|
|
| `409` | Conflict (duplicate webhook registration, etc.) | No |
|
|
| `413` | Payload too large | No |
|
|
| `429` | Rate limited | Yes — honor `Retry-After` |
|
|
| `500` | Server bug | Yes — with backoff, then report |
|
|
| `502` | Upstream / Convex / Dodo failure | Yes — exponential backoff |
|
|
| `503` | Service unavailable — missing env or dependency down | Yes — exponential backoff |
|
|
| `504` | Upstream timeout | Yes — with backoff |
|
|
|
|
## Common error strings
|
|
|
|
| `error` value | Meaning |
|
|
|---------------|---------|
|
|
| `UNAUTHENTICATED` | No valid Clerk JWT or API key. |
|
|
| `pro_required` | Authenticated, but account is not PRO. |
|
|
| `invalid_payload` | Body failed schema validation. |
|
|
| `invalid_date_shape` | Date param not `YYYY-MM-DD`. |
|
|
| `brief_not_found` | No composed brief for the requested `{userId, issueDate}`. |
|
|
| `Rate limit exceeded` | 429 fired; honor `Retry-After`. |
|
|
| `Service temporarily unavailable` | Upstash or another hard dependency missing at request time. |
|
|
| `service_unavailable` | Signing secret / required env not configured. |
|
|
| `Failed to enqueue scenario job` | Redis pipeline failure on `/api/scenario/v1/run`. |
|
|
|
|
## Retry strategy
|
|
|
|
**Idempotent reads** (`GET`): retry 429/5xx with exponential backoff (1s, 2s, 4s, cap 30s, 5 attempts). Most GET responses are cached at the edge, so the retry usually goes faster.
|
|
|
|
**Writes**: never auto-retry 4xx. For 5xx on writes, inspect: `POST /api/brief/share-url` and `POST /api/v2/shipping/webhooks` are idempotent; `POST /api/scenario/v1/run` is **not** — it enqueues a new job on each call. Retrying a 5xx on `run` may double-charge the rate-limit counter.
|
|
|
|
**MCP**: the server returns tool errors in the JSON-RPC result with `isError: true` and a text explanation — those are not HTTP errors. Handle them at the tool-call layer.
|
|
|
|
## Debugging
|
|
|
|
- Every edge response includes `x-vercel-id` and `x-worldmonitor-deploy` headers — include those when reporting issues.
|
|
- Sentry alerts forward to [status.worldmonitor.app](https://status.worldmonitor.app/).
|
|
- `GET /api/health` and `GET /api/seed-health` show per-seed freshness; a stale seed is the most common root cause of unexpected empty payloads.
|