Analyst
Ask the goal questions in plain language — streamed answers grounded in its forecast, graph, and what-if engine.
Every goal has an analyst: a conversational interface grounded in that goal's deployed model, driver graph, latest forecast, track record, and what-if engine. Ask why the forecast dipped, what's driving it this week, or what happens if a driver moves — the analyst reads the same artifacts the API exposes and can run simulations on your behalf.
Conversations are grouped into sessions, scoped to you: a session belongs to one workspace, one goal, and one user. Nobody else's sessions are visible to you, and yours aren't visible to them.
Ask a question (streaming)
The analyst streams its answer:
| Field | Meaning |
|---|---|
question required | The question, in plain language. |
session_id optional | Continue an existing conversation. Omit to start a new one — the start event carries the new ID. |
curl -N -X POST "$API_BASE/v1/goals/$GOAL_ID/analyst/stream" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Workspace-Id: $WORKSPACE_ID" \
-H "Content-Type: application/json" \
-d '{ "question": "Why is the forecast dipping next week?" }'The response is a Server-Sent Events stream (Content-Type: text/event-stream). Each line is a data: payload with an event field:
data: {"event": "start", "session_id": "s_4b7d…"}
data: {"event": "text_delta", "text": "The dip traces to ad_spend, which fell "}
data: {"event": "tool_start", "tool": "run_scenario", "label": "Simulating ad_spend −8%"}
data: {"event": "tool_end", "tool": "run_scenario"}
data: {"event": "attachment", "kind": "forecast_chart", "data": { "…": "…" }}
data: {"event": "done", "session_id": "s_4b7d…"}event | Meaning |
|---|---|
start | The turn began; carries the session_id (save it to continue the conversation) |
text_delta | A chunk of the answer — concatenate these in order |
tool_start / tool_end | The analyst is using one of its tools (reading the forecast, running a scenario, …) |
attachment | A structured artifact to render — a chart, a table |
warning | A non-fatal notice (for example, a driver's data is stale) |
done | The turn finished cleanly |
error | The turn failed; carries a message |
Keepalive comments flow while a slow tool runs, so the connection survives
long simulations. If the stream fails mid-turn you get a final error
event, never a dropped connection.
| Status | Why | Example message |
|---|---|---|
400 | Missing question | "'question' is required" |
404 | Goal doesn't exist in this workspace | "Goal not found" |
Analyst turns bill like other goal actions: per turn plus per tool the analyst uses — a simulation it runs for you costs the same as running it yourself. See Billing.
List sessions
Your sessions for this goal, newest first, without turn bodies:
curl "$API_BASE/v1/goals/$GOAL_ID/analyst/sessions?limit=20" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Workspace-Id: $WORKSPACE_ID"from predictai import PredictAI
client = PredictAI(token="pa_live_…", workspace_id="ws_…")
data = client.goals.get_goals_by_goal_id_analyst_sessions(GOAL_ID, params={"limit": 20})
import { PredictAI } from "@predictai/sdk";
const client = new PredictAI({ token: "pa_live_…", workspaceId: "ws_…" });
const data = await client.goals.getGoalsByGoalIdAnalystSessions(GOAL_ID, { params: { limit: 20 } });
{
"data": {
"sessions": [
{
"session_id": "s_4b7d…",
"title": "Why is the forecast dipping next week?",
"turns": 3,
"created_at": "2026-07-15T14:02:00+00:00",
"updated_at": "2026-07-15T14:11:38+00:00"
}
]
}
}| Parameter | Meaning |
|---|---|
limit optional | 1–200. Defaults to 50. |
Get one session
The full transcript:
curl "$API_BASE/v1/goals/$GOAL_ID/analyst/sessions/$SESSION_ID" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Workspace-Id: $WORKSPACE_ID"from predictai import PredictAI
client = PredictAI(token="pa_live_…", workspace_id="ws_…")
data = client.goals.get_goals_by_goal_id_analyst_sessions_by_session_id(GOAL_ID, SESSION_ID)
import { PredictAI } from "@predictai/sdk";
const client = new PredictAI({ token: "pa_live_…", workspaceId: "ws_…" });
const data = await client.goals.getGoalsByGoalIdAnalystSessionsBySessionId(GOAL_ID, SESSION_ID);
{
"data": {
"session": {
"session_id": "s_4b7d…",
"goal_id": "b7e9c2d4-…",
"turns": [
{ "role": "user", "text": "Why is the forecast dipping next week?" },
{ "role": "analyst", "text": "The dip traces to ad_spend…", "attachments": [] }
],
"created_at": "2026-07-15T14:02:00+00:00"
}
}
}A session that doesn't exist — or belongs to another user — returns 404
with "Session not found", indistinguishably.
Delete a session
curl -X DELETE "$API_BASE/v1/goals/$GOAL_ID/analyst/sessions/$SESSION_ID" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Workspace-Id: $WORKSPACE_ID"from predictai import PredictAI
client = PredictAI(token="pa_live_…", workspace_id="ws_…")
data = client.goals.delete_goals_by_goal_id_analyst_sessions_by_session_id(GOAL_ID, SESSION_ID)
import { PredictAI } from "@predictai/sdk";
const client = new PredictAI({ token: "pa_live_…", workspaceId: "ws_…" });
const data = await client.goals.deleteGoalsByGoalIdAnalystSessionsBySessionId(GOAL_ID, SESSION_ID);
{ "data": { "deleted": true, "session_id": "s_4b7d…" } }
