Predict.aiDocs
Examples

What-if scenarios

Move a driver, replay an event, and watch the forecast respond — counterfactuals served by the live champion.

A forecast tells you what's coming. A scenario tells you what you can do about it: what happens to sales if we raise ad spend 10%? The intervention is applied to the serving data, derived features are recomputed, and the goal's champion re-infers — so the answer is the model's real response, not a rule of thumb.

You'll need: a goal with a live champion — the one from A goal on autopilot is perfect.

1. Find what you're allowed to move

Only drivers with validated relationships are simulatable — check drivers-now first:

from predictai import PredictAI

client = PredictAI(token="pa_live_...", workspace_id="ws_...")

drivers = client.goals.get_goals_by_goal_id_drivers_now(GOAL_ID)["data"]
levers = [d["key"] for d in drivers["drivers"] if d["simulatable"]]
print("you can move:", levers)
print("try:", drivers["scenario_examples"]["examples"])
you can move: ['foot_traffic', 'promo_active']
try: ['What if foot traffic rises 10%?']

2. Run the counterfactual

Move a driver by percent (pct), absolute amount (abs), or standard deviations (sigma), as a step (immediate) or a ramp (gradual). The run is charged upfront and refunded if it fails; results are persisted, so re-reading a saved scenario is free:

scenario = client.goals.post_goals_by_goal_id_scenarios(GOAL_ID, json={
    "name": "Foot traffic +10%",
    "overrides": [
        {"key": "foot_traffic", "mode": "pct", "amount": 10, "shape": "step"},
    ],
})["data"]

for step, (base, delta) in enumerate(zip(scenario["baseline"]["point"], scenario["delta"])):
    print(f'day {step + 1:2}: {base:9.1f}  {delta:+8.1f}')
print("caveats:", scenario["caveats"][0])
day  1:   13102.4    +196.3
day  2:   13350.9    +266.6
day  3:   13571.2    +301.4
...
caveats: model response under intervention — a validated predictive relationship, not a causal guarantee

The response carries the full provenance: baseline (the untouched forecast), fan (the counterfactual), per-step delta, attribution (which override drove the change), multi-hop chains when an upstream move propagated, and honest caveats.

3. Inject an event instead

If the goal is connected to event streams, you can replay one — "what if a supply disruption hit tomorrow?" — using the stream's measured response curve:

shock = client.goals.post_goals_by_goal_id_scenarios(GOAL_ID, json={
    "name": "Supply disruption, typical size",
    "overrides": [],
    "events": [
        {"stream": "supply_disruptions", "magnitude": 1, "direction": -1},
    ],
})["data"]

print("worst step:", round(min(shock["delta"]), 1))

4. Which lever matters most?

Instead of guessing one scenario at a time, sensitivity analysis probes every simulatable driver with a symmetric ±10% step and ranks them by end-of-horizon impact — the tornado chart, in one call:

sensitivity = client.goals.post_goals_by_goal_id_scenarios_sensitivity(
    GOAL_ID, json={"magnitude_pct": 10},
)["data"]

print("baseline end of horizon:", sensitivity["baseline_end"])
for row in sensitivity["results"]:
    print(f'{row["key"]:14} +10% → {row["up_delta"]:+8.1f}   -10% → {row["down_delta"]:+8.1f}')
baseline end of horizon: 14210.55
foot_traffic   +10% →   +271.4   -10% →   -244.9
promo_active   +10% →   +118.2   -10% →   -105.7

The result is persisted — re-viewing via GET /v1/goals/{goal_id}/scenarios/sensitivity is free.

Read the numbers honestly

Scenario output is the champion's response under intervention — a validated predictive relationship, not a causal guarantee. The caveats array in every response says exactly this, and attribution + chains show you which relationships carried the effect so you can judge them yourself. Saved scenarios stay listable at GET /v1/goals/{goal_id}/scenarios — free to re-read, and each one can be explained in plain language.

On this page