A goal on autopilot
Name an outcome, let the platform find its drivers, train champions, and keep a forecast live — then ask it why.
Everything in CSV to live forecast — segments, models, pipelines, deployment — can be delegated. A goal takes one input, the thing you care about, and runs the whole loop for you: it profiles the target, discovers what drives it, builds a segment from the survivors, trains a tournament of models, and keeps the best one serving. This example creates one and reads back not just the forecast, but the why behind it.
You'll need: a workspace with real history — several signals over months, like the one built in CSV to live forecast. Discovery finds relationships; it needs data to find them in.
1. Create the goal
Two fields. Horizon, cadence, drivers, models — all resolved from your data:
from predictai import PredictAI
client = PredictAI(token="pa_live_...", workspace_id="ws_...")
goal = client.goals.post_goals(json={
"target_key": "daily_sales",
"goal_type": "forecast_value",
})
GOAL_ID = goal["data"]["goal_id"]
RUN_ID = goal["data"]["run_id"]
print("goal:", GOAL_ID, "- first discovery run queued,",
goal["data"]["credits_charged"], "credits charged")import { PredictAI } from "@predictai/sdk";
const client = new PredictAI({ token: "pa_live_...", workspaceId: "ws_..." });
const goal = await client.goals.postGoals({
json: { target_key: "daily_sales", goal_type: "forecast_value" },
});
const GOAL_ID: string = goal.data.goal_id;
const RUN_ID: string = goal.data.run_id;
console.log("goal:", GOAL_ID, "- first discovery run queued,",
goal.data.credits_charged, "credits charged");2. Watch discovery work
A discovery run walks a funnel — profile the target, generate candidate drivers, screen, validate with false-discovery control, write the survivors as graph edges. Poll the run and watch the stages tick by:
import time
while True:
run = client.goals.get_goals_runs_by_run_id(RUN_ID)["data"]["run"]
print("discovery:", run["status"])
if run["status"] in ("completed", "failed"):
break
time.sleep(30)const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
for (;;) {
const { data } = await client.goals.getGoalsRunsByRunId(RUN_ID);
console.log("discovery:", data.run.status);
if (data.run.status === "completed" || data.run.status === "failed") break;
await sleep(30_000);
}With auto-fit on (the default), a completed run flows straight into a
tournament: candidate models train on the discovered segment and the
best becomes the goal's champion. Wait for the goal to reach serving:
while True:
detail = client.goals.get_goals_by_goal_id(GOAL_ID)["data"]
print("goal:", detail["goal"]["status"])
if detail["goal"]["status"] == "serving":
break
time.sleep(60)for (;;) {
const { data } = await client.goals.getGoalsByGoalId(GOAL_ID);
console.log("goal:", data.goal.status);
if (data.goal.status === "serving") break;
await sleep(60_000);
}3. Read the forecast
The goal keeps its forecast fresh on the target's cadence. One read gets the chart-ready path, uncertainty bands, and the champion's honest track record:
data = client.goals.get_goals_by_goal_id_forecast(GOAL_ID)["data"]
fc = data["forecast"]
print("next 14 days:", [round(v) for v in fc["point"]])
print("accuracy :", fc["model_quality"]["accuracy_score"])
print("coverage :", data["track_record"]["interval_coverage"])const { data } = await client.goals.getGoalsByGoalIdForecast(GOAL_ID);
const fc = data.forecast;
console.log("next 14 days:", fc.point.map(Math.round));
console.log("accuracy :", fc.model_quality.accuracy_score);
console.log("coverage :", data.track_record.interval_coverage);next 14 days: [13102, 13351, 13571, ...]
accuracy : 91.4
coverage : 0.87For production integrations, prefer the stable inference alias — it always routes to the current champion, surviving champion swaps:
forecast = client.inference.post_inference_goals_by_goal_id_infer_segment(GOAL_ID, json={})const forecast = await client.inference.postInferenceGoalsByGoalIdInferSegment(GOAL_ID, { json: {} });4. Ask it why
This is what separates a goal from a bare model: the forecast comes with its reasoning. Drivers-now projects the validated relationship graph as current pressure — each driver's latest move and the direction it's pushing the target:
drivers = client.goals.get_goals_by_goal_id_drivers_now(GOAL_ID)["data"]
for d in drivers["drivers"]:
print(f'{d["key"]:14} {d["pressure"]:8} '
f'(moved {d["change_pct"]:+.1f}% over its {d["lag_bins"]}-step lead, '
f'confidence {d["confidence"]:.2f})')const drivers = await client.goals.getGoalsByGoalIdDriversNow(GOAL_ID);
for (const d of drivers.data.drivers) {
console.log(
`${d.key}: ${d.pressure} (moved ${d.change_pct.toFixed(1)}% ` +
`over its ${d.lag_bins}-step lead, confidence ${d.confidence.toFixed(2)})`,
);
}foot_traffic upward (moved +10.4% over its 1-step lead, confidence 0.94)
promo_active upward (moved +100.0% over its 2-step lead, confidence 0.88)What's running now
You created two fields' worth of configuration; the platform now owns the
loop: re-discovery when data drifts, retrains on the goal's policy,
champion swaps only when a challenger genuinely wins, and shock handling
when connected event streams spike. Steer it with options (autopilot
level, spending caps, model lineup) on
create or edit.
- Move a driver and watch the forecast respond — What-if scenarios.
- Get told when the forecast crosses a line — Alerts that watch the forecast.

