Decision intelligence as a contract.
Everything the LEXUN engine does — framing, evidence gates, reproducible simulation, adversarial challenge, frozen resolutions, outcome scoring and calibration — expressed as a versioned OpenAPI contract you can build against today.
The OpenAPI 3.0 contract (v1.0.0-beta) is stable and downloadable now, and the engine that implements its semantics runs today — locally, in the browser, as auditable JavaScript modules. The hosted HTTPS service ships with LEXUN's backend foundation. Nothing on this page pretends otherwise.
The hosted API is paid, and gated by design. There is no free public endpoint and there will not be one: every hosted call costs real compute, so every hosted call requires a scoped API key, and keys are issued only with paid plans. The server-side endpoints this site already runs answer this site’s own pages, refuse everyone else, and cap each visitor’s pace — the gate is real code, not a promise. To be first in line for hosted keys, ask for access.
Design principles
Honesty is in the types
Probabilities return as bands with raw values reserved for scoring. 409 insufficient-evidence is a first-class response, not an error to route around — the engine refuses to invent values.
Reproducible by construction
Every simulation carries model version, seed, input hash and a reproducibility ID. Same inputs, same seed, same result — on any conforming implementation.
Immutable history
Resolution criteria freeze at forecast time. Outcomes are write-once (409 on second write). Calibration is computed only from resolved forecasts.
Idempotent writes
All POSTs accept an Idempotency-Key header, so retries never double-create decisions or double-report outcomes.
Endpoints (v1)
| Method & path | Purpose | Notable responses |
|---|---|---|
POST /v1/decisions | Create a decision (frame stage) | 201 created · 422 invalid frame |
GET /v1/decisions | List decisions | 200 |
POST /v1/decisions/{id}/evidence | Attach classified evidence (fact / assumption / external / unknown) | 201 |
POST /v1/decisions/{id}/simulate | Run the domain model, reproducibly | 200 forecast · 409 insufficient-evidence — body explains which gates failed |
POST /v1/forecasts/{id}/challenge | Independent adversarial review (separate method) | 200 findings + disagreement record |
POST /v1/forecasts/{id}/outcome | Report the outcome against the frozen criterion | 201 scored · 409 already reported (immutable) |
GET /v1/calibration | Calibration by domain and horizon | 200 — clients MUST render "not enough resolved outcomes yet" below n=10 |
GET /v1/exports/{decisionId} | Full auditable export: inputs, evidence, run, forecast, outcome | 200 |
Example — run a reproducible simulation
The dueDate is always generatedAt plus the stated horizon — resolution dates derive from the freeze timestamp, never from the clock at read time. The block above is valid JSON as printed; paste it into any validator.
# request POST /v1/decisions/dec_8f2/simulate Authorization: Bearer <api-key> # scoped key; org keys never read personal decisions Idempotency-Key: 7c9e6679-7425-40de-944b Content-Type: application/json { "model": "runway", "inputs": { "cashToday": 80000, "monthlyRevenue": 12000, "monthlyCosts": 18000, "revenueGrowthPct": 2, "revenueVolPct": 15, "horizonMonths": 12 }, "classifications": { "cashToday": "user-fact", "monthlyRevenue": "user-fact", "monthlyCosts": "user-fact", "revenueGrowthPct": "assumption", "revenueVolPct": "assumption" } } # response 200 — identical for anyone with these inputs (seed is input-derived) { "status": "ok", "probabilityDisplay": { "text": "60–70%", "lo": 0.60, "hi": 0.70 }, "confidence": { "level": "moderate", "reason": "3 of 5 evidence inputs are facts" }, "runwayMonths": { "p10": 9, "p50": 13, "p90": 13 }, "costOfDelay": { "delayMonths": 3, "probabilityLost": 0.15 }, "resolution": { "criterion": "cash > £0 at horizon, no emergency funding", "dueDate": "2027-07-26" }, "reproducibility": { "id": "rw_52f9a7c3_234", "modelVersion": "1.0.0", "seed": 564, "paths": 5000, "inputHash": "52f9a7c3" } } # response 409 — the gate is the product, not an error { "status": "insufficient-evidence", "reason": "Fields marked unknown: monthlyCosts. The model will not invent values." }
This exact response shape is what the in-browser engine returns today — verify it yourself; the seed-564 example reproduces on this site.
Authentication, versioning, limits
| Area | Contract |
|---|---|
| Auth | Bearer API keys, scoped per workspace. Keys ship with paid plans only — there is no anonymous or free tier of the hosted API. Organisation keys can never read individuals' personal decisions. |
| Versioning | Path-versioned (/v1/). Model versions ride in every response; breaking model changes bump the model version, never silently change results. |
| Rate limits | Planned defaults: 60 simulate/min, 600 reads/min per key, 429 with Retry-After. Final numbers ship with the hosted service. |
| Errors | 401 bad key · 404 unknown id · 409 insufficient-evidence / immutable conflict · 422 validation with per-field issues · 429 rate limited. |
| Data retention | Hosted decisions are the customer's; export and hard-delete endpoints are part of the v1 surface, not an afterthought. |
Use the engine today — locally
The exact modules that power this site are auditable JavaScript with a stable surface. No network, no keys, fully reproducible:
// node — using the modules served by this site const Rm = require('./platform/runway-model.js'); const r = Rm.analyse( { cashToday: 80000, monthlyRevenue: 12000, monthlyCosts: 18000, revenueGrowthPct: 2, revenueVolPct: 15, costGrowthPct: 0, horizonMonths: 12 }, { cashToday: 'user-fact', monthlyRevenue: 'user-fact', monthlyCosts: 'user-fact' } ); console.log(r.probabilityDisplay.text); // "60–70%" — every time
Sandbox, webhooks, status page and changelog ship with the hosted service — they are not listed as available because they aren't yet.
For AI agents (MCP)
If your agent is about to reason about runway, hand it a tool that shows its working. Agents doing money arithmetic themselves is a documented failure mode; LEXUN’s models are deterministic, versioned and receipted. The bundle this site is deployed from ships a working Model Context Protocol server — the same engine modules the browser runs, exposed as five tools.
| Tool | What it answers |
|---|---|
| run_runway | Probability a business still has cash at the horizon. Refuses anything that is not that question. |
| run_career_change | Probability a savings buffer survives an income transition. Career success is refused, not estimated. |
| run_major_purchase | Probability a buffer survives an upfront cost plus a new commitment. |
| verify_receipt | Re-runs a reproducibility receipt and reports whether a claimed band matches — forgeries are called out. |
| get_reference_class | The dated, source-linked UK outside view for a domain — or an explicit statement that none defensibly exists. |
Run it locally against the deployed files (no keys, no accounts, no network calls out):
# download the site bundle, then point your MCP client at:
{ "mcpServers": { "lexun": { "command": "node", "args": ["mcp/server.mjs"] } } }
The contract: stateless — compute, return, store nothing; no user identity, no logging of inputs, and the process holds nothing between calls. Every successful answer carries the full reproducibility receipt (model@version, seed, input hash) so the calling agent can cite it and any human can check it on /verify. The evidence gates apply to agents exactly as to humans: missing inputs return a structured refusal, never a guess. Every response says, in machine-readable text, that it is an estimate with an error band and not advice. The server is exercised by the same zero-fault release gate as everything else on this site.
Registry listings and a hosted MCP endpoint are planned, not live — today the server runs from the bundle on your machine, which is also the most private way to run it. The hosted API above will share this same stateless contract.