Simulate (coming soon)
What-if projections over your own corpus - apply a scenario to a deal or interaction cohort and get projected outcomes with the evidence slice behind them
The simulate endpoint is coming soon. This page documents the planned surface so you can design against it; request and response shapes may change before launch, and the operation is absent from the OpenAPI spec and tool catalog until it ships.
POST /simulate answers "what would happen if" over your own data: pick a cohort of deals or interactions, state a scenario as typed assumptions, and get back the projected outcomes side by side with the baseline — plus the evidence slice the projection was computed from, so every number is auditable. It is a projection over the corpus you already have, not a forecast pulled from market priors: the model is your funnel, at your grain, under your assumption.
Reach for it when a GTM decision needs a number before you commit — "what does win rate look like if security review stops killing deals?", "what happens to Q4 pipeline if enterprise cycle time drops 20%?" — and pair it with evals after the fact to score the projection against what actually happened.
| Operation | simulate.run |
| REST | POST /simulate |
| MCP | simulate tool, action run |
| Scope | data:read |
Request shape
| Field | Type | Default | Notes |
|---|---|---|---|
scenario | string (≤500) | — (required) | The what-if, in plain language. Names the projection in the response; the typed assumptions are what actually compute. |
surface | deals | interactions | deals | Which corpus the cohort is drawn from. |
cohort | object | the full surface | { "filters": [...] } — the same typed {field, op, value} predicates as the routed search filter lane, validated against the same field catalog. |
assumptions | array (max 8) | — (required) | Typed changes to apply (below). |
metrics | array (max 8) | ["count"] | What to project: win_rate, cycle_days, sum_deal_amount, count. |
horizon_days | int | 90 | How far forward the projection runs, cap 365. |
Each assumption is one typed change:
op | Shape | Meaning |
|---|---|---|
scale | { "op": "scale", "field": "cycle_days", "factor": 0.8 } | Multiply a numeric behavior by a factor. |
set | { "op": "set", "field": "deal_stage_status", "from": "lost", "to": "open", "where": { "loss_reason": "security_review" } } | Reassign an outcome for the rows matching where. |
shift | { "op": "shift", "field": "timestamp", "days": -14 } | Move an event-time behavior earlier or later. |
An unknown field, an operator the field does not admit, or an assumption over a field outside the cohort's surface comes back as a typed invalid_argument naming the offender and the allowed set — the same self-teaching error contract as the filter lane.
Example — win rate without security-review losses
curl -X POST "https://app.amdahl.co/api/platform/v1/simulate" \
-H "X-API-Key: $AMDAHL_KEY" \
-H "Content-Type: application/json" \
-d '{
"scenario": "What if security review stopped killing enterprise deals?",
"surface": "deals",
"cohort": {
"filters": [ { "field": "deal_amount", "op": "gte", "value": 50000 } ]
},
"assumptions": [
{ "op": "set", "field": "deal_stage_status", "from": "lost", "to": "open", "where": { "loss_reason": "security_review" } }
],
"metrics": ["win_rate", "sum_deal_amount"],
"horizon_days": 90
}'Over MCP, the simulate tool's run action takes the same body:
{
"action": "run",
"scenario": "What if security review stopped killing enterprise deals?",
"surface": "deals",
"cohort": { "filters": [ { "field": "deal_amount", "op": "gte", "value": 50000 } ] },
"assumptions": [
{ "op": "set", "field": "deal_stage_status", "from": "lost", "to": "open", "where": { "loss_reason": "security_review" } }
],
"metrics": ["win_rate", "sum_deal_amount"]
}The response — baseline, projection, and the delta, with the evidence slice as the receipt:
{
"success": true,
"scenario": "What if security review stopped killing enterprise deals?",
"projection": {
"baseline": { "win_rate": 0.21, "sum_deal_amount": 1240000 },
"projected": { "win_rate": 0.29, "sum_deal_amount": 1710000 },
"delta": { "win_rate": 0.08, "sum_deal_amount": 470000 }
},
"confidence": "medium",
"evidence": {
"cohort_size": 118,
"rows_affected": 14,
"sample": [
{ "deal_id": "9214…", "deal_stage_status": "lost", "loss_reason": "security_review", "deal_amount": 85000 }
],
"compiled": { "sql": "SELECT … FROM deals WHERE `deal_amount` >= 50000 …" }
},
"assumptions_applied": [
{ "op": "set", "field": "deal_stage_status", "rows_matched": 14 }
],
"freshness": { "source": "bigquery", "computed_at": "2026-07-22T04:12:09Z" },
"timing": { "total_ms": 2400 }
}Reading it
projection.baselineis computed live from the cohort, through the same tenant-scoped, access-checked query gate as every other read — the projection and the baseline can never disagree about what the cohort is.confidenceishigh/medium/low, driven by cohort size and how many rows the assumptions actually touched. Adeltariding onrows_affected: 3is an anecdote, not a projection — renderconfidencenext to every number.evidence.compiled.sqlis the receipt for the cohort slice;evidence.sampleis a bounded peek at the rows the assumptions changed. Store both with the projection so it can be audited later.- Honest abstention: a cohort too thin to project returns
projection: nullwithreason: "cohort_too_thin"and the cohort facts — the same coverage-not-failure contract as lookalike'savailable: false. The HTTP status is 200; branch on the field, not the status.
A prompt to hand an agent
Run a what-if over our own pipeline — use the simulate tool.
Scenario: {e.g. "enterprise deals if security-review losses became open
deals again"}. Build the cohort with typed filters (check the field names
first via the search tool's fields action), state the assumptions as typed
ops (set / scale / shift), and project {win_rate and total amount} over
{90} days.
Report baseline vs projected vs delta, the confidence level, and how many
rows the assumptions actually touched. If it abstains with
cohort_too_thin, widen the cohort and say so — never invent a projection.Tips
- Assumptions are typed for a reason. The plain-language
scenariolabels the run; only the typedassumptionscompute. If you cannot express the what-if asset/scale/shiftover catalog fields, it is a judgment question — take it to Chat instead. - Project the metric, not the rows.
metricsaggregates over the full cohort server-side, the same aggregate-in-the-query discipline as the filter lane — never pull rows and project client-side over a capped slice. - Close the loop with evals. A projection you never score is a guess with a JSON envelope: when the horizon passes, hand the projection and the actuals to
POST /evals.
See also
- Evals (coming soon) — the backward-looking sibling: simulate projects, evals scores.
- Search — the filter vocabulary the cohort and assumptions are validated against.
- Endpoints overview — the whole surface and prerequisites.