======================= 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``. .. contents:: Endpoints :local: :depth: 1 ---- Create Session -------------- .. code-block:: text 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** .. list-table:: :header-rows: 1 :widths: 20 10 10 60 * - 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** .. code-block:: bash 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 } }' .. code-block:: python 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`` .. code-block:: json { "id": "1e2f3a4b-5c6d-7890-1234-567890abcdef", "studio_session_id": "1e2f3a4b-5c6d-7890-1234-567890abcdef" } ---- List Sessions ------------- .. code-block:: text GET /api/studio/sessions **Query Parameters** .. list-table:: :header-rows: 1 :widths: 20 10 10 60 * - Parameter - Type - Default - Description * - ``project_id`` - string - -- - **Required.** Filter by project. * - ``limit`` - integer - 50 - Max items (1--100). * - ``offset`` - integer - 0 - Pagination offset. **Example** .. code-block:: bash curl "$BASE_URL/api/studio/sessions?project_id=d4e5f6a7-b8c9-0123-def4-567890123456" \ -H "Authorization: Bearer YOUR_API_KEY" **Response** ``200 OK`` .. code-block:: json { "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 ------------------ .. code-block:: text GET /api/studio/sessions/{session_id} Return the session with baseline input/output, feature schema, and all scenarios. **Example** .. code-block:: bash curl "$BASE_URL/api/studio/sessions/1e2f3a4b-5c6d-7890-1234-567890abcdef" \ -H "Authorization: Bearer YOUR_API_KEY" .. code-block:: python 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`` .. code-block:: json { "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 ------------------ .. code-block:: text 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** .. code-block:: bash curl "$BASE_URL/api/studio/deployments/d0e1f2a3-b4c5-6789-0123-def456789012/schema" \ -H "Authorization: Bearer YOUR_API_KEY" **Response** ``200 OK`` .. code-block:: json { "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 --------------- .. code-block:: text 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** .. list-table:: :header-rows: 1 :widths: 20 10 10 60 * - 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** .. code-block:: bash 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 } }' .. code-block:: python 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`` .. code-block:: json { "id": "2f3a4b5c-6d7e-8901-2345-67890abcdef1", "scenario_id": "2f3a4b5c-6d7e-8901-2345-67890abcdef1", "status": "pending" } ---- Run Scenario ------------ .. code-block:: text 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** .. code-block:: bash curl -X POST "$BASE_URL/api/studio/scenarios/2f3a4b5c-6d7e-8901-2345-67890abcdef1/run" \ -H "Authorization: Bearer YOUR_API_KEY" .. code-block:: python 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`` .. code-block:: json { "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 ----------------- .. code-block:: text 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** .. code-block:: bash curl "$BASE_URL/api/studio/sessions/1e2f3a4b-5c6d-7890-1234-567890abcdef/compare" \ -H "Authorization: Bearer YOUR_API_KEY" .. code-block:: python 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`` .. code-block:: json { "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 -------------------------- .. code-block:: python 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']})") ---- .. seealso:: - :doc:`deployments` -- Creating and managing deployments. - :doc:`models` -- Model details and direct predictions.