This service is documented inline (not yet proto-backed). Proto migration is tracked in issue #3207 and will replace this page with auto-generated reference.
List templates
GET /api/scenario/v1/templates
Returns the catalog of pre-defined scenario templates. Cached public, max-age=3600.
Response — abbreviated example using one of the live shipped templates (server/worldmonitor/supply-chain/v1/scenario-templates.ts):
taiwan-strait-full-closure, suez-bab-simultaneous, panama-drought-50pct, russia-baltic-grain-suspension, us-tariff-escalation-electronics. Use the live /templates response as the source of truth — the set grows over time.
Run a scenario
POST /api/scenario/v1/run
Enqueues a job. Returns 202 Accepted with a jobId the caller must poll.
- Auth: PRO entitlement required. Granted by either (a) a valid
X-WorldMonitor-Key(env key fromWORLDMONITOR_VALID_KEYS, or a user-ownedwm_-prefixed key whose owner has theapiAccessentitlement), or (b) a Clerk bearer token whose user has roleproor Dodo entitlement tier ≥ 1. A trusted browser Origin alone is not sufficient —isCallerPremium()inserver/_shared/premium-check.tsonly counts explicit credentials. Browser calls work becausepremiumFetch()(src/services/premium-fetch.ts) injects one of the two credential forms on the caller’s behalf. - Rate limits:
- 10 jobs / minute / user
- Global queue capped at 100 in-flight jobs; excess rejected with
429+Retry-After: 30
scenarioId— id from/templates. Required.iso2— optional ISO-3166-1 alpha-2 (uppercase). Scopes the scenario to one country.
202):
| Status | error | Cause |
|---|---|---|
| 400 | Invalid JSON body | Body is not valid JSON |
| 400 | scenarioId is required | Missing field |
| 400 | Unknown scenario: ... | scenarioId not in the template catalog |
| 400 | iso2 must be a 2-letter uppercase country code | Malformed iso2 |
| 403 | PRO subscription required | Not PRO |
| 405 | — | Method other than POST |
| 429 | Rate limit exceeded: 10 scenario jobs per minute | Per-user rate limit |
| 429 | Scenario queue is at capacity, please try again later | Global queue > 100 |
| 502 | Failed to enqueue scenario job | Redis enqueue failure |
| 503 | Service temporarily unavailable | Missing env |
Poll job status
GET /api/scenario/v1/status?jobId=<jobId>
Returns the job’s current state as written by the worker, or a synthesised pending stub while the job is still queued.
- Auth: same as
/run - jobId format:
scenario:{unix-ms}:{8-char-suffix}— strictly validated to guard against path traversal
status | When | Additional fields |
|---|---|---|
pending | Job enqueued but worker has not picked it up yet. Synthesised by the status handler when no Redis record exists. | — |
processing | Worker dequeued the job and started computing. Written by the worker at job pickup. | startedAt (ms epoch) |
done | Worker completed successfully. | completedAt, result (scenario-specific payload) |
failed | Worker hit a computation error. | failedAt, error (string) |
200):
200):
200) — the worker writes the result directly to Redis; the status endpoint returns it verbatim:
200):
pending and processing as non-terminal; only done and failed are terminal. Both pending and processing can legitimately persist for several seconds under load.
Errors:
| Status | error | Cause |
|---|---|---|
| 400 | Invalid or missing jobId | Missing or malformed jobId |
| 403 | PRO subscription required | Not PRO |
| 405 | — | Method other than GET |
| 500 | Corrupted job result | Worker wrote invalid JSON |
| 502 | Failed to fetch job status | Redis read failure |
| 503 | Service temporarily unavailable | Missing env |
Polling strategy
- First poll: ~1s after enqueue.
- Subsequent polls: exponential backoff (1s → 2s → 4s, cap 10s).
- Workers typically complete in 5-30 seconds depending on scenario complexity.
- If still pending after 2 minutes, the job is probably dead — re-enqueue.
