Skip to main content

API surface

All five are tenant-scoped, rate-limited (10/min run, 5/min compare, 10/min distribution, 6/min value-finder, 20/min seasonality), and write audit-log rows so DSAR exports can cite what was simulated.

Run body shape

  • offerValues is optional. When absent, valueDelta is computed using score as a proxy for value, and the response sets meta.valuesUsedProxy: true.
  • maxTraces caps the archive window at 1–20000 rows.
  • seed controls the bootstrap PRNG so the CI is reproducible.

Compare body shape

Returns a per-scenario result plus a pValues matrix and a rank ordering by value delta.

Seasonality body shape

history requires 7+ points. forecast is optional — when omitted, only the decomposition is returned.

UI

/studio/scenarios ships 5 tabs: Run, Compare, Outcome, Optimization Sweep, Seasonality. Run is the polished single-scenario form with bootstrap-CI on value delta. The other four tabs surface raw JSON request/response panels — operators paste in the body shape, hit Run, see the structured response. Chart wrappers are roadmap polish.

Outcome Distribution

POST /api/v1/scenarios/distribution slices archived decisions by a segment dimension and emits per-segment offer-share, share-delta, Gini concentration index (0=uniform, 1=winner-take-all), and KL-divergence (baseline ‖ scenario, nats).
The segmentResolver is operator-supplied because there is no Customer Prisma model — segment data lives in DataSchema-driven ds_* tables and the operator’s UI / batch driver knows how to hydrate it. Customers missing from the resolver are skipped honestly; the meta.tracesScanned + meta.segmentsCovered block reports coverage so operators see what fraction of the window was attributable.

Optimization Sweep

POST /api/v1/scenarios/value-finder — Pareto-frontier sweep over revenue × fairness × coverage.
Response rows[] carries per-scenario revenue / fairness / coverage / picks / dominated. paretoFront[] lists non-dominated names. The recommended field is the Pareto-front member with the highest geometric mean of normalized axes — a conservative default, never binding. Honest limits: fairness is returned as not-a-number when the archive lacks segments (returned, not silenced). revenue falls back to score-as-proxy when offerValues are absent.

Honest limits

  • Per-offer business value (margin / expected revenue) is best supplied by the caller. When missing, the route falls back to score as a proxy and explicitly flags it in meta.valuesUsedProxy.
  • The compare endpoint loads the archive once per call; very wide windows (maxTraces near the cap) on slow Postgres can take 10+s.
  • The seasonality endpoint does not query the DB for time-series data — the canonical KPI source varies per tenant. Caller supplies the history.