ML Studio (What-If) API

ML Studio enables what-if analysis against deployed models. Create a session with a baseline input, define scenarios that change specific features, run predictions for each scenario, and compare the results.

All endpoints are prefixed with /api/studio.


Create Session

POST /api/studio/sessions

Create a new what-if analysis session. The baseline input is immediately sent for prediction to establish the reference point. The session stores the baseline prediction, feature contributions (SHAP values), and the feature schema derived from the underlying dataset.

Request Body

Field

Type

Required

Description

project_id

string

Yes

UUID of the project.

deployment_id

string

Yes

UUID of the active deployment to analyze.

baseline_input

object

Yes

Feature values for the baseline prediction. Only features from the model’s schema are used; the target column is automatically excluded.

Example

curl -X POST "$BASE_URL/api/studio/sessions" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "project_id": "d4e5f6a7-b8c9-0123-def4-567890123456",
    "deployment_id": "d0e1f2a3-b4c5-6789-0123-def456789012",
    "baseline_input": {
      "amount": 29.99,
      "merchant_id": 42,
      "hour": 14,
      "day_of_week": 3
    }
  }'
import requests

resp = requests.post(f"{BASE_URL}/api/studio/sessions", headers={
    "Authorization": "Bearer YOUR_API_KEY",
}, json={
    "project_id": "d4e5f6a7-b8c9-0123-def4-567890123456",
    "deployment_id": "d0e1f2a3-b4c5-6789-0123-def456789012",
    "baseline_input": {
        "amount": 29.99,
        "merchant_id": 42,
        "hour": 14,
        "day_of_week": 3,
    },
})
session_id = resp.json()["studio_session_id"]
print("Session created:", session_id)

Response 201 Created

{
  "id": "1e2f3a4b-5c6d-7890-1234-567890abcdef",
  "studio_session_id": "1e2f3a4b-5c6d-7890-1234-567890abcdef"
}

List Sessions

GET /api/studio/sessions

Query Parameters

Parameter

Type

Default

Description

project_id

string

Required. Filter by project.

limit

integer

50

Max items (1–100).

offset

integer

0

Pagination offset.

Example

curl "$BASE_URL/api/studio/sessions?project_id=d4e5f6a7-b8c9-0123-def4-567890123456" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response 200 OK

{
  "items": [
    {
      "id": "1e2f3a4b-5c6d-7890-1234-567890abcdef",
      "project_id": "d4e5f6a7-b8c9-0123-def4-567890123456",
      "deployment_id": "d0e1f2a3-b4c5-6789-0123-def456789012",
      "created_at": "2026-02-28T09:00:00Z"
    }
  ],
  "total": 1,
  "limit": 50,
  "offset": 0
}

Get Session Detail

GET /api/studio/sessions/{session_id}

Return the session with baseline input/output, feature schema, and all scenarios.

Example

curl "$BASE_URL/api/studio/sessions/1e2f3a4b-5c6d-7890-1234-567890abcdef" \
  -H "Authorization: Bearer YOUR_API_KEY"
resp = requests.get(
    f"{BASE_URL}/api/studio/sessions/1e2f3a4b-5c6d-7890-1234-567890abcdef",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
)
session = resp.json()
print("Baseline prediction:", session["baseline_output"])
for s in session["scenarios"]:
    print(f"  Scenario '{s['name']}': {s['output']}")

Response 200 OK

{
  "id": "1e2f3a4b-5c6d-7890-1234-567890abcdef",
  "project_id": "d4e5f6a7-b8c9-0123-def4-567890123456",
  "deployment_id": "d0e1f2a3-b4c5-6789-0123-def456789012",
  "feature_schema": {
    "amount": "float64",
    "merchant_id": "int64",
    "hour": "int64",
    "day_of_week": "int64"
  },
  "baseline_input": {
    "amount": 29.99,
    "merchant_id": 42,
    "hour": 14,
    "day_of_week": 3
  },
  "baseline_output": {
    "prediction": "0"
  },
  "baseline_contributions": [
    {"feature": "amount", "value": -0.32},
    {"feature": "merchant_id", "value": -0.15},
    {"feature": "hour", "value": -0.08},
    {"feature": "BiasTerm", "value": -1.20}
  ],
  "created_at": "2026-02-28T09:00:00Z",
  "scenarios": [
    {
      "id": "2f3a4b5c-6d7e-8901-2345-67890abcdef1",
      "name": "High amount at night",
      "description": "What if amount is 10x and hour is 3am?",
      "changes": {"amount": 9500.00, "hour": 3},
      "status": "succeeded",
      "scenario_input": {
        "amount": 9500.00,
        "merchant_id": 42,
        "hour": 3,
        "day_of_week": 3
      },
      "output": {"prediction": "1"},
      "contributions": [
        {"feature": "amount", "value": 1.85},
        {"feature": "hour", "value": 0.92},
        {"feature": "BiasTerm", "value": -1.20}
      ],
      "error": null,
      "created_at": "2026-02-28T09:05:00Z"
    }
  ]
}

Get Feature Schema

GET /api/studio/deployments/{deployment_id}/schema

Return the feature schema for a deployment, derived from the training dataset’s column metadata. The target column is automatically excluded.

Example

curl "$BASE_URL/api/studio/deployments/d0e1f2a3-b4c5-6789-0123-def456789012/schema" \
  -H "Authorization: Bearer YOUR_API_KEY"

Response 200 OK

{
  "deployment_id": "d0e1f2a3-b4c5-6789-0123-def456789012",
  "project_id": "d4e5f6a7-b8c9-0123-def4-567890123456",
  "model_id": "a7b8c9d0-e1f2-3456-7890-abcdef123456",
  "experiment_id": "b8c9d0e1-f2a3-4567-8901-bcdef1234567",
  "dataset_version_id": "f6a7b8c9-d0e1-2345-6789-0abcdef12345",
  "target_column": "is_fraud",
  "feature_schema": {
    "amount": "float64",
    "merchant_id": "int64",
    "hour": "int64",
    "day_of_week": "int64"
  }
}

Create Scenario

POST /api/studio/sessions/{session_id}/scenarios

Define a what-if scenario by specifying which features to change relative to the baseline input. Only the changes dict is required; unchanged features are inherited from the baseline.

Request Body

Field

Type

Required

Description

name

string

Yes

Scenario name.

description

string

No

Optional description of the hypothesis.

changes

object

Yes

Dictionary of feature-value overrides. Keys must match the feature schema.

Example

curl -X POST "$BASE_URL/api/studio/sessions/1e2f3a4b-5c6d-7890-1234-567890abcdef/scenarios" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "High amount at night",
    "description": "What if the transaction amount is 10x higher and happens at 3am?",
    "changes": {
      "amount": 9500.00,
      "hour": 3
    }
  }'
resp = requests.post(
    f"{BASE_URL}/api/studio/sessions/1e2f3a4b-5c6d-7890-1234-567890abcdef/scenarios",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
    json={
        "name": "High amount at night",
        "description": "What if amount is 10x and hour is 3am?",
        "changes": {"amount": 9500.00, "hour": 3},
    },
)
scenario_id = resp.json()["scenario_id"]

Response 201 Created

{
  "id": "2f3a4b5c-6d7e-8901-2345-67890abcdef1",
  "scenario_id": "2f3a4b5c-6d7e-8901-2345-67890abcdef1",
  "status": "pending"
}

Run Scenario

POST /api/studio/scenarios/{scenario_id}/run

Execute the scenario by applying the feature changes to the baseline input and running a prediction through the deployment. The scenario transitions from pending to running to succeeded (or failed).

Example

curl -X POST "$BASE_URL/api/studio/scenarios/2f3a4b5c-6d7e-8901-2345-67890abcdef1/run" \
  -H "Authorization: Bearer YOUR_API_KEY"
resp = requests.post(
    f"{BASE_URL}/api/studio/scenarios/2f3a4b5c-6d7e-8901-2345-67890abcdef1/run",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
)
result = resp.json()
print("Scenario prediction:", result["output"]["prediction"])

Response 200 OK

{
  "ok": true,
  "output": {
    "prediction": "1"
  },
  "contributions": [
    {"feature": "amount", "value": 1.85},
    {"feature": "hour", "value": 0.92},
    {"feature": "merchant_id", "value": -0.15},
    {"feature": "BiasTerm", "value": -1.20}
  ]
}

Compare Scenarios

GET /api/studio/sessions/{session_id}/compare

Compare the baseline with all completed scenarios. Returns the baseline output and, for each scenario, the output and a delta (difference in prediction value and top contributing features).

Example

curl "$BASE_URL/api/studio/sessions/1e2f3a4b-5c6d-7890-1234-567890abcdef/compare" \
  -H "Authorization: Bearer YOUR_API_KEY"
resp = requests.get(
    f"{BASE_URL}/api/studio/sessions/1e2f3a4b-5c6d-7890-1234-567890abcdef/compare",
    headers={"Authorization": "Bearer YOUR_API_KEY"},
)
data = resp.json()
print("Baseline:", data["baseline"]["output"])
for s in data["scenarios"]:
    print(f"  {s['name']}: prediction delta = {s['delta'].get('prediction')}")

Response 200 OK

{
  "baseline": {
    "output": {"prediction": "0"},
    "contributions": [
      {"feature": "amount", "value": -0.32},
      {"feature": "merchant_id", "value": -0.15},
      {"feature": "BiasTerm", "value": -1.20}
    ]
  },
  "scenarios": [
    {
      "id": "2f3a4b5c-6d7e-8901-2345-67890abcdef1",
      "name": "High amount at night",
      "output": {"prediction": "1"},
      "delta": {
        "prediction": null,
        "contributions": [
          {"feature": "amount", "delta": 2.17},
          {"feature": "hour", "delta": 1.00},
          {"feature": "merchant_id", "delta": 0.0}
        ]
      }
    }
  ]
}

Note

When the prediction is categorical (classification), the prediction delta is null because difference between class labels is not meaningful. For regression models, the delta is the numeric difference between the scenario and baseline predictions.

Contribution deltas are always numeric and represent the change in SHAP value for each feature. They are sorted by absolute delta (descending) and limited to the top 10 features.


End-to-End What-If Example

import requests

BASE_URL = "http://localhost:8888"
HEADERS = {"Authorization": "Bearer YOUR_API_KEY"}

# 1. Get the feature schema for a deployment
schema = requests.get(
    f"{BASE_URL}/api/studio/deployments/d0e1f2a3-b4c5-6789-0123-def456789012/schema",
    headers=HEADERS,
).json()
print("Features:", list(schema["feature_schema"].keys()))

# 2. Create a session with baseline input
resp = requests.post(f"{BASE_URL}/api/studio/sessions", headers=HEADERS, json={
    "project_id": "d4e5f6a7-b8c9-0123-def4-567890123456",
    "deployment_id": "d0e1f2a3-b4c5-6789-0123-def456789012",
    "baseline_input": {
        "amount": 29.99,
        "merchant_id": 42,
        "hour": 14,
        "day_of_week": 3,
    },
})
session_id = resp.json()["studio_session_id"]

# 3. Create scenarios
scenarios = [
    {"name": "High amount", "changes": {"amount": 9500.00}},
    {"name": "Late night", "changes": {"hour": 3}},
    {"name": "High amount + late night", "changes": {"amount": 9500.00, "hour": 3}},
]
scenario_ids = []
for s in scenarios:
    r = requests.post(
        f"{BASE_URL}/api/studio/sessions/{session_id}/scenarios",
        headers=HEADERS,
        json={"name": s["name"], "changes": s["changes"]},
    )
    scenario_ids.append(r.json()["scenario_id"])

# 4. Run all scenarios
for sid in scenario_ids:
    requests.post(f"{BASE_URL}/api/studio/scenarios/{sid}/run", headers=HEADERS)

# 5. Compare results
comparison = requests.get(
    f"{BASE_URL}/api/studio/sessions/{session_id}/compare",
    headers=HEADERS,
).json()

print(f"Baseline: {comparison['baseline']['output']}")
for s in comparison["scenarios"]:
    print(f"  {s['name']}: {s['output']} (delta={s['delta']})")

See also