{
  "openapi": "3.1.0",
  "x-audience": "client",
  "info": {
    "title": "Spavik API",
    "version": "1.0.0",
    "description": "Prediction and forecasting for developers. Creating a model is training it (POST /v1/models, data + target); you then predict by its id (POST /v1/models/{id}/predict). Forecasting (POST /v1/forecast) creates no persistent model. Model predictions cost the engine price per scored row. Forecasting costs the engine price per requested horizon step per call (1 credit on the default engine), with all series included; training costs nothing. Send your API key in X-Spavik-Api-Key."
  },
  "servers": [
    {
      "url": "https://api.spavik.co"
    }
  ],
  "security": [
    {
      "apiKey": []
    }
  ],
  "tags": [
    {
      "name": "Prediction"
    },
    {
      "name": "Models"
    },
    {
      "name": "Usage and billing"
    }
  ],
  "paths": {
    "/v1/forecast/backtest": {
      "parameters": [
        {
          "name": "User-Agent",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 1024
          },
          "description": "Information about the agent originating the request, when known. Browsers supply their own value. Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        },
        {
          "name": "X-Spavik-Client-Name",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 128
          },
          "description": "HTTP caller: SDK, MCP server or direct application (for example spavik-sdk-python, spavik-sdk-javascript or spavik-mcp). Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        },
        {
          "name": "X-Spavik-Client-Version",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 64
          },
          "description": "Version of that SDK, MCP server or direct application. Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        }
      ],
      "post": {
        "x-audience": "client",
        "tags": [
          "Prediction"
        ],
        "summary": "Validate series structure and compare backtest scores with baselines",
        "description": "engine_family is optional (flair, presage or prophecy), defaulting to the family marked is_default in GET /v1/engine-families. Optionally provide a full engine_version such as presage-2 or presage-latest; otherwise the latest active version of that family is selected, with the same plan and retirement checks as forecasting. No model identifier is accepted. backtest.engine identifies the engine used. Creates nothing, charges nothing. Validates the series (length, regular interval, duplicate timestamps, numeric target) and, if it holds, replays windows of the history to score what the engine would have predicted against what happened. Monthly series must use month-start or month-end timestamps; other fixed monthly days, including days clamped to the end of a short month, are not supported. All series must share the same frequency and calendar anchor, evaluated in UTC.\n\nEvery HTTP 200 response includes a non-null assessment (status, metric, message). `validation` describes input validity; `assessment` compares the observed MASE with both the naive reference and the historical-median constant reference. Both must be present, valid and scored on the same evaluation points. A strict improvement over both gives `better_than_baseline`; otherwise `not_better_than_baseline`. Missing evidence or a skipped backtest gives `inconclusive`. No statistical significance or temporal predictability is asserted. No comparison is made against a fixed MASE threshold of 1.\n\nThe result applies only to the requested horizon (default 1) and point_estimate (default mean). Both point_estimate and quantiles are forwarded to the engine, as in POST /v1/forecast. Window arrays come from the engine and describe observed variation, not a confidence interval. With evaluation.protocol_version = 2, MASE is normalized per series using its initial context, then averaged with equal weight per eligible series. The assessment counts evaluation.mase_n_eval, not all backtested points; series with zero or unavailable scales are excluded from every MASE. backtest.evaluation exposes the protocol, scales, window dates, exclusions and weighting. Without protocol metadata, the older engine aggregation is preserved and no per-series weighting is implied. Do not recompute aggregates as unweighted window averages. Window indices can represent different dates across series. Counts and skipped reasons describe the available evidence. When validation fails, backtest is null and assessment.status is not_evaluated. The former signal and backtest.verdict fields are replaced by assessment. Forward assessment unchanged; an absent or null assessment is a contract violation, not inconclusive.\n\ninclude_backtest_predictions=true adds historical evaluated observations to backtest.predictions without new inference. Future forecast values are never returned here: forecasting is billed, this route is free. Use POST /v1/forecast to get them.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "target",
                  "data"
                ],
                "properties": {
                  "engine_family": {
                    "type": "string",
                    "enum": [
                      "flair",
                      "presage",
                      "prophecy"
                    ],
                    "example": "presage",
                    "description": "Engine family for this call. Omitted: the family marked is_default in GET /v1/engine-families. No persisted model is used. engine_version must belong to this family, including when the family is omitted."
                  },
                  "engine_version": {
                    "type": "string",
                    "pattern": "^(flair|presage|prophecy)-(latest|[1-9][0-9]*(\\.[0-9]+)?)$",
                    "maxLength": 64,
                    "example": "presage-2",
                    "description": "Full engine identifier within the selected family, e.g. presage-2 or presage-latest. Omitted or <family>-latest: latest active version of that family. A different family is refused. Responses identify the exact version used."
                  },
                  "backtest": {
                    "type": "boolean",
                    "default": false,
                    "description": "Evaluate historical windows on POST /v1/forecast. The dedicated backtest route always evaluates."
                  },
                  "include_backtest_predictions": {
                    "type": "boolean",
                    "default": false,
                    "description": "Return the historical observations already evaluated by the backtest, without additional inference or credits. On POST /v1/forecast, also set backtest=true. Does not force a skipped or disabled backtest to run. Detail is always JSON."
                  },
                  "target": {
                    "type": "string"
                  },
                  "horizon": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 1000,
                    "description": "Number of future steps (alias: prediction_length)."
                  },
                  "data": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "additionalProperties": true
                    },
                    "description": "Data rows, one object per row."
                  },
                  "timestamp": {
                    "type": "string",
                    "description": "Timestamp column (inferred when omitted)."
                  },
                  "quantiles": {
                    "type": "array",
                    "items": {
                      "type": "number"
                    }
                  },
                  "item_id": {
                    "type": "string",
                    "description": "Column identifying several series. Each series is forecast on its own history only."
                  },
                  "point_estimate": {
                    "type": "string",
                    "enum": [
                      "mean",
                      "median"
                    ],
                    "description": "Statistic served in <target>_prediction (defaults to mean)."
                  }
                }
              },
              "example": {
                "engine_family": "presage",
                "target": "ventes",
                "horizon": 7,
                "data": [
                  {
                    "date": "2026-07-01",
                    "ventes": 100
                  },
                  {
                    "date": "2026-07-02",
                    "ventes": 112
                  },
                  {
                    "date": "2026-07-03",
                    "ventes": 118
                  },
                  {
                    "date": "2026-07-04",
                    "ventes": 121
                  },
                  {
                    "date": "2026-07-05",
                    "ventes": 130
                  },
                  {
                    "date": "2026-07-06",
                    "ventes": 84
                  },
                  {
                    "date": "2026-07-07",
                    "ventes": 72
                  },
                  {
                    "date": "2026-07-08",
                    "ventes": 105
                  },
                  {
                    "date": "2026-07-09",
                    "ventes": 117
                  },
                  {
                    "date": "2026-07-10",
                    "ventes": 123
                  },
                  {
                    "date": "2026-07-11",
                    "ventes": 126
                  },
                  {
                    "date": "2026-07-12",
                    "ventes": 135
                  },
                  {
                    "date": "2026-07-13",
                    "ventes": 89
                  },
                  {
                    "date": "2026-07-14",
                    "ventes": 77
                  },
                  {
                    "date": "2026-07-15",
                    "ventes": 110
                  },
                  {
                    "date": "2026-07-16",
                    "ventes": 122
                  },
                  {
                    "date": "2026-07-17",
                    "ventes": 128
                  },
                  {
                    "date": "2026-07-18",
                    "ventes": 131
                  },
                  {
                    "date": "2026-07-19",
                    "ventes": 140
                  },
                  {
                    "date": "2026-07-20",
                    "ventes": 94
                  },
                  {
                    "date": "2026-07-21",
                    "ventes": 82
                  },
                  {
                    "date": "2026-07-22",
                    "ventes": 115
                  },
                  {
                    "date": "2026-07-23",
                    "ventes": 127
                  },
                  {
                    "date": "2026-07-24",
                    "ventes": 133
                  },
                  {
                    "date": "2026-07-25",
                    "ventes": 136
                  },
                  {
                    "date": "2026-07-26",
                    "ventes": 145
                  },
                  {
                    "date": "2026-07-27",
                    "ventes": 99
                  },
                  {
                    "date": "2026-07-28",
                    "ventes": 87
                  }
                ]
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "target",
                  "file"
                ],
                "properties": {
                  "engine_family": {
                    "type": "string",
                    "enum": [
                      "flair",
                      "presage",
                      "prophecy"
                    ],
                    "example": "presage",
                    "description": "Engine family for this call. Omitted: the family marked is_default in GET /v1/engine-families. No persisted model is used. engine_version must belong to this family, including when the family is omitted."
                  },
                  "engine_version": {
                    "type": "string",
                    "pattern": "^(flair|presage|prophecy)-(latest|[1-9][0-9]*(\\.[0-9]+)?)$",
                    "maxLength": 64,
                    "example": "presage-2",
                    "description": "Full engine identifier within the selected family, e.g. presage-2 or presage-latest. Omitted or <family>-latest: latest active version of that family. A different family is refused. Responses identify the exact version used."
                  },
                  "backtest": {
                    "type": "boolean",
                    "default": false
                  },
                  "include_backtest_predictions": {
                    "type": "boolean",
                    "default": false,
                    "description": "Return the historical observations already evaluated by the backtest, without additional inference or credits. On POST /v1/forecast, also set backtest=true. Does not force a skipped or disabled backtest to run. Detail is always JSON."
                  },
                  "target": {
                    "type": "string"
                  },
                  "horizon": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 1000
                  },
                  "prediction_length": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 1000
                  },
                  "point_estimate": {
                    "type": "string",
                    "enum": [
                      "mean",
                      "median"
                    ],
                    "default": "mean"
                  },
                  "quantiles": {
                    "type": "string",
                    "description": "Comma-separated quantiles, e.g. 0.1,0.5,0.9."
                  },
                  "item_id": {
                    "type": "string"
                  },
                  "file": {
                    "type": "string",
                    "format": "binary"
                  },
                  "timestamp": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "X-Spavik-Model-Version": {
                "schema": {
                  "type": "string",
                  "example": "presage-2"
                },
                "description": "Exact engine used to produce this result, including on an idempotent replay. Never a latest alias. Distinct from the model data version."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "assessment"
                  ],
                  "properties": {
                    "target": {
                      "type": "string"
                    },
                    "timestamp": {
                      "type": "string"
                    },
                    "item_id": {
                      "type": "string",
                      "nullable": true
                    },
                    "horizon": {
                      "type": "integer",
                      "minimum": 1,
                      "maximum": 1000
                    },
                    "point_estimate": {
                      "type": "string",
                      "enum": [
                        "mean",
                        "median"
                      ]
                    },
                    "n_rows": {
                      "type": "integer"
                    },
                    "columns": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "validation": {
                      "type": "object",
                      "additionalProperties": true
                    },
                    "assessment": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "status",
                        "metric",
                        "message"
                      ],
                      "properties": {
                        "status": {
                          "type": "string",
                          "description": "Display better_than_baseline as Better than the reference on this sample, alongside assessment.message and the evaluated observation count. A dataset without signal can beat a baseline by chance. Do not present this as a reliability or deployment approval.",
                          "enum": [
                            "better_than_baseline",
                            "not_better_than_baseline",
                            "inconclusive",
                            "not_evaluated"
                          ]
                        },
                        "metric": {
                          "type": "string",
                          "enum": [
                            "AUC",
                            "MAE",
                            "MASE"
                          ]
                        },
                        "message": {
                          "type": "string"
                        }
                      },
                      "description": "The single API conclusion, always present and non-null in HTTP 200 responses, including when the trial cannot run. Forward status, metric and message without recomputing them from scores. An absent or null assessment is a contract violation, not an inconclusive result. This is a descriptive comparison on the evaluated observations: better means strictly better than every required reference; equality is not better. No fixed gain or sample-size threshold implies significance. Inconclusive means missing or invalid evidence, or a skipped backtest; not_evaluated means validation prevented the trial. No status establishes reliability, future performance or absence of predictive signal."
                    },
                    "hint": {
                      "type": "string",
                      "nullable": true
                    },
                    "backtest": {
                      "type": "object",
                      "nullable": true,
                      "additionalProperties": true,
                      "required": [
                        "engine"
                      ],
                      "properties": {
                        "engine": {
                          "type": "string",
                          "description": "Exact engine used for this backtest.",
                          "example": "presage-2"
                        },
                        "metrics": {
                          "type": "object",
                          "nullable": true,
                          "additionalProperties": {
                            "type": "number",
                            "nullable": true
                          },
                          "description": "Scores on evaluated observations. Classification: AUC, Accuracy, AveragePrecision (binary only), LogLoss and MacroF1. Regression: MAE, RMSE, R². Forecasting adds MASE, Bias, BiasRatio and Pinball. Undefined scores are null; see metric_context.undefined_reasons. Secondary scores do not change the primary assessment.",
                          "properties": {
                            "AUC": {
                              "type": "number",
                              "nullable": true,
                              "minimum": 0,
                              "maximum": 1,
                              "description": "ROC AUC, binary positive class or multiclass one-vs-rest macro average."
                            },
                            "Accuracy": {
                              "type": "number",
                              "nullable": true,
                              "minimum": 0,
                              "maximum": 1
                            },
                            "AveragePrecision": {
                              "type": "number",
                              "nullable": true,
                              "minimum": 0,
                              "maximum": 1,
                              "description": "Binary non-interpolated average precision, not trapezoidal PR-AUC. Null without positive observations."
                            },
                            "LogLoss": {
                              "type": "number",
                              "nullable": true,
                              "minimum": 0,
                              "description": "Mean negative log probability of the actual class, natural logarithm, float64 epsilon clipping. Lower is better."
                            },
                            "MacroF1": {
                              "type": "number",
                              "nullable": true,
                              "minimum": 0,
                              "maximum": 1,
                              "description": "Unweighted mean of defined per-class F1 scores. Classes absent from both actual and predicted labels are excluded."
                            },
                            "MAE": {
                              "type": "number",
                              "nullable": true,
                              "minimum": 0,
                              "description": "Mean absolute error in target units."
                            },
                            "RMSE": {
                              "type": "number",
                              "nullable": true,
                              "minimum": 0
                            },
                            "R²": {
                              "type": "number",
                              "nullable": true,
                              "description": "Null for fewer than two observations or constant actual values. Can be negative."
                            },
                            "MASE": {
                              "type": "number",
                              "nullable": true,
                              "minimum": 0,
                              "description": "Compare with the scored naive baseline on the same backtest, not with a fixed threshold of 1."
                            },
                            "Bias": {
                              "type": "number",
                              "nullable": true
                            },
                            "BiasRatio": {
                              "type": "number",
                              "nullable": true
                            },
                            "Pinball": {
                              "type": "number",
                              "nullable": true,
                              "minimum": 0
                            }
                          }
                        },
                        "baselines": {
                          "type": "object",
                          "nullable": true,
                          "description": "References indexed by metric. Read floor for the numeric score, not the whole object. Fit provenance must come from the engine; a missing fit_on does not mean train.",
                          "additionalProperties": {
                            "type": "object",
                            "properties": {
                              "floor": {
                                "type": "number",
                                "nullable": true
                              },
                              "class_order": {
                                "type": "array",
                                "nullable": true,
                                "items": {
                                  "type": "string"
                                }
                              },
                              "probabilities": {
                                "type": "array",
                                "nullable": true,
                                "items": {
                                  "type": "number",
                                  "minimum": 0,
                                  "maximum": 1
                                }
                              },
                              "undefined_reason": {
                                "type": "string",
                                "nullable": true
                              },
                              "relative_error_reduction": {
                                "type": "number",
                                "nullable": true,
                                "description": "For observed loss metrics only: 1 - model score / baseline score on identical outcomes. Fraction, possibly negative. Null for non-loss metrics, zero baseline or unavailable scores."
                              },
                              "reduction_undefined_reason": {
                                "type": "string",
                                "nullable": true
                              },
                              "label": {
                                "type": "string"
                              },
                              "higher_better": {
                                "type": "boolean"
                              },
                              "fit_on": {
                                "type": "string",
                                "nullable": true,
                                "enum": [
                                  "train",
                                  "test",
                                  "theoretical",
                                  null
                                ],
                                "description": "Where reference parameters were determined. MAE comparison requires train."
                              },
                              "prediction": {
                                "type": [
                                  "number",
                                  "string",
                                  "null"
                                ],
                                "description": "Constant prediction fitted on training data, or null for a probability vector or forecasting reference."
                              },
                              "constant_floor": {
                                "type": "number",
                                "nullable": true,
                                "description": "MASE of the historical-median reference on the same evaluation points."
                              },
                              "constant_label": {
                                "type": "string",
                                "nullable": true
                              }
                            }
                          }
                        },
                        "evaluation": {
                          "type": "object",
                          "nullable": true,
                          "description": "Engine rolling-origin protocol. Null when not supplied. In v2, MASE excludes zero or unavailable scales and weights eligible series equally; each series weights its evaluated points equally. Missing window/MASE metadata is not inferred when the backtest is skipped.",
                          "properties": {
                            "metric_context": {
                              "$ref": "#/components/schemas/MetricContext"
                            },
                            "protocol_version": {
                              "type": "integer",
                              "example": 2
                            },
                            "interpretation": {
                              "type": "string",
                              "enum": [
                                "descriptive_only"
                              ]
                            },
                            "kind": {
                              "type": "string",
                              "enum": [
                                "rolling_origin"
                              ]
                            },
                            "requested": {
                              "type": "boolean"
                            },
                            "n_input": {
                              "type": "integer",
                              "minimum": 0
                            },
                            "n_missing_target": {
                              "type": "integer",
                              "minimum": 0
                            },
                            "sampling": {
                              "type": "string",
                              "enum": [
                                "none"
                              ]
                            },
                            "horizon": {
                              "type": "integer",
                              "minimum": 0
                            },
                            "max_windows": {
                              "type": "integer",
                              "minimum": 0
                            },
                            "min_context": {
                              "type": "integer",
                              "minimum": 0
                            },
                            "window_order": {
                              "type": "string",
                              "enum": [
                                "oldest_to_newest"
                              ]
                            },
                            "mase_weighting": {
                              "type": "string",
                              "enum": [
                                "equal_series_then_equal_points_within_series"
                              ]
                            },
                            "other_metrics_weighting": {
                              "type": "string",
                              "enum": [
                                "equal_evaluated_points"
                              ]
                            },
                            "mase_n_series": {
                              "type": "integer",
                              "minimum": 0
                            },
                            "mase_n_eval": {
                              "type": "integer",
                              "minimum": 0
                            },
                            "series": {
                              "type": "array",
                              "items": {
                                "type": "object",
                                "properties": {
                                  "series": {
                                    "type": "string"
                                  },
                                  "n_points": {
                                    "type": "integer",
                                    "minimum": 0
                                  },
                                  "seasonal_period": {
                                    "type": "integer",
                                    "nullable": true,
                                    "minimum": 1
                                  },
                                  "mase_scale": {
                                    "type": "number",
                                    "nullable": true
                                  },
                                  "n_windows": {
                                    "type": "integer",
                                    "minimum": 0
                                  },
                                  "n_eval": {
                                    "type": "integer",
                                    "minimum": 0
                                  },
                                  "scale_context_start": {
                                    "type": "string",
                                    "nullable": true,
                                    "description": "UTC timestamp; engine v2 omits the timezone suffix."
                                  },
                                  "scale_context_end": {
                                    "type": "string",
                                    "nullable": true,
                                    "description": "UTC timestamp; engine v2 omits the timezone suffix."
                                  },
                                  "scale_n_context": {
                                    "type": "integer",
                                    "minimum": 0
                                  },
                                  "backtest_excluded_reason": {
                                    "type": "string",
                                    "nullable": true
                                  },
                                  "mase_excluded_reason": {
                                    "type": "string",
                                    "nullable": true
                                  }
                                }
                              }
                            },
                            "windows": {
                              "type": "array",
                              "items": {
                                "type": "object",
                                "properties": {
                                  "series": {
                                    "type": "string"
                                  },
                                  "window": {
                                    "type": "integer",
                                    "minimum": 0,
                                    "description": "Zero identifies the latest window."
                                  },
                                  "n_context": {
                                    "type": "integer",
                                    "minimum": 0
                                  },
                                  "n_eval": {
                                    "type": "integer",
                                    "minimum": 0
                                  },
                                  "context_end": {
                                    "type": "string",
                                    "nullable": true,
                                    "description": "UTC timestamp; engine v2 omits the timezone suffix."
                                  },
                                  "test_start": {
                                    "type": "string",
                                    "nullable": true,
                                    "description": "UTC timestamp; engine v2 omits the timezone suffix."
                                  },
                                  "test_end": {
                                    "type": "string",
                                    "nullable": true,
                                    "description": "UTC timestamp; engine v2 omits the timezone suffix."
                                  }
                                }
                              }
                            }
                          }
                        },
                        "intervals": {
                          "$ref": "#/components/schemas/IntervalMetrics"
                        },
                        "predictions": {
                          "$ref": "#/components/schemas/BacktestPredictions"
                        },
                        "by_window": {
                          "type": "array",
                          "nullable": true,
                          "items": {
                            "type": "number",
                            "nullable": true
                          }
                        },
                        "naive_by_window": {
                          "type": "array",
                          "nullable": true,
                          "items": {
                            "type": "number",
                            "nullable": true
                          }
                        },
                        "constant_by_window": {
                          "type": "array",
                          "nullable": true,
                          "items": {
                            "type": "number",
                            "nullable": true
                          }
                        },
                        "n_series": {
                          "type": "integer",
                          "nullable": true,
                          "minimum": 1
                        },
                        "n_windows": {
                          "type": "integer",
                          "nullable": true,
                          "minimum": 1
                        },
                        "n_eval": {
                          "type": "integer",
                          "nullable": true,
                          "minimum": 1,
                          "description": "All backtested observations, including those excluded from MASE. For MASE, use evaluation.mase_n_eval in protocol v2."
                        },
                        "n_context": {
                          "type": "integer",
                          "nullable": true,
                          "minimum": 1
                        },
                        "seasonal_period": {
                          "type": "integer",
                          "nullable": true,
                          "minimum": 1
                        },
                        "skipped": {
                          "type": "string",
                          "nullable": true
                        },
                        "warnings": {
                          "type": "array",
                          "nullable": true,
                          "items": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "ENGINE_FAMILY_REQUIRED, ENGINE_FAMILY_MISMATCH, INVALID_ENGINE_VERSION, UNKNOWN_ENGINE_VERSION, series unreadable, target or timestamp missing, or invalid horizon (VALIDATION_ERROR).",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Authentication required, or invalid token.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "ENGINE_PLAN_REQUIRED: the selected engine requires a higher plan.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This caller is not allowed to perform this action.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "ENGINE_RETIRED: the requested engine version has been retired.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "ENGINE_BUSY: the engine has no available capacity; ENGINE_UNAVAILABLE: the engine is unreachable or returned a server error. Try again shortly.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ]
      }
    },
    "/v1/forecast": {
      "parameters": [
        {
          "name": "User-Agent",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 1024
          },
          "description": "Information about the agent originating the request, when known. Browsers supply their own value. Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        },
        {
          "name": "X-Spavik-Client-Name",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 128
          },
          "description": "HTTP caller: SDK, MCP server or direct application (for example spavik-sdk-python, spavik-sdk-javascript or spavik-mcp). Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        },
        {
          "name": "X-Spavik-Client-Version",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 64
          },
          "description": "Version of that SDK, MCP server or direct application. Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        }
      ],
      "post": {
        "x-audience": "client",
        "tags": [
          "Prediction"
        ],
        "summary": "Forecast a time series (zero-shot)",
        "description": "engine_family is optional (flair, presage or prophecy), defaulting to the family marked is_default in GET /v1/engine-families. Optionally provide a full engine_version such as presage-2 or presage-latest; otherwise the latest active version of that family is selected. No model identifier is accepted. The response engine identifies the exact version used. Retired versions are refused without substitution. Billed the requested horizon times the serving engine credits per row (1 on the default engine). Providing 25 historical days and requesting 7 future days costs 7 credits on an engine priced at 1, or 14 credits on an engine priced at 2. All series in the call are included. The call is refused (402 CREDITS_EXHAUSTED) before computation when this cost exceeds the available credits. The engine price is fixed for the duration of the call.\n\nSeveral series (item_id) are forecast independently, each from its own history. One series does not learn from another. Historical input rows and quantiles do not multiply the charge. With backtest=true, include_backtest_predictions=true returns the historical evaluated observations in backtest.predictions, without additional inference or credits.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Same workspace, method, path, key and payload prevent another execution or charge, subject to current model access. Successful responses are replayed for every data policy, including ZDR, from the technical PostgreSQL cache (default retention 24 hours from operation creation). The data policy only controls inference input/output archiving to GCS/BigQuery; it never changes replay behavior. A different key or an expired completed key starts a new operation and may be charged again. REQUEST_IN_PROGRESS: wait Retry-After. REQUEST_INTERRUPTED: outcome uncertain; contact support. IDEMPOTENCY_RESPONSE_UNAVAILABLE: a completed response is missing; do not retry with a new key. Completed keys expire; unresolved keys remain protected. Reusing a key with different input returns 422. A key is required on the original call for safe retries."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "target",
                  "data"
                ],
                "properties": {
                  "engine_family": {
                    "type": "string",
                    "enum": [
                      "flair",
                      "presage",
                      "prophecy"
                    ],
                    "example": "presage",
                    "description": "Engine family for this call. Omitted: the family marked is_default in GET /v1/engine-families. No persisted model is used. engine_version must belong to this family, including when the family is omitted."
                  },
                  "engine_version": {
                    "type": "string",
                    "pattern": "^(flair|presage|prophecy)-(latest|[1-9][0-9]*(\\.[0-9]+)?)$",
                    "maxLength": 64,
                    "example": "presage-2",
                    "description": "Full engine identifier within the selected family, e.g. presage-2 or presage-latest. Omitted or <family>-latest: latest active version of that family. A different family is refused. Responses identify the exact version used."
                  },
                  "backtest": {
                    "type": "boolean",
                    "default": false,
                    "description": "Evaluate historical windows on POST /v1/forecast. The dedicated backtest route always evaluates."
                  },
                  "include_backtest_predictions": {
                    "type": "boolean",
                    "default": false,
                    "description": "Return the historical observations already evaluated by the backtest, without additional inference or credits. On POST /v1/forecast, also set backtest=true. Does not force a skipped or disabled backtest to run. Detail is always JSON."
                  },
                  "target": {
                    "type": "string"
                  },
                  "horizon": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 1000,
                    "description": "Number of future steps (alias: prediction_length)."
                  },
                  "data": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "additionalProperties": true
                    },
                    "description": "Data rows, one object per row."
                  },
                  "timestamp": {
                    "type": "string",
                    "description": "Timestamp column (inferred when omitted)."
                  },
                  "quantiles": {
                    "type": "array",
                    "items": {
                      "type": "number"
                    }
                  },
                  "item_id": {
                    "type": "string",
                    "description": "Column identifying several series. Each series is forecast on its own history only."
                  },
                  "point_estimate": {
                    "type": "string",
                    "enum": [
                      "mean",
                      "median"
                    ],
                    "description": "Statistic served in <target>_prediction (defaults to mean)."
                  }
                }
              },
              "example": {
                "engine_family": "presage",
                "target": "ventes",
                "horizon": 7,
                "data": [
                  {
                    "date": "2026-07-01",
                    "ventes": 100
                  },
                  {
                    "date": "2026-07-02",
                    "ventes": 112
                  },
                  {
                    "date": "2026-07-03",
                    "ventes": 118
                  },
                  {
                    "date": "2026-07-04",
                    "ventes": 121
                  },
                  {
                    "date": "2026-07-05",
                    "ventes": 130
                  },
                  {
                    "date": "2026-07-06",
                    "ventes": 84
                  },
                  {
                    "date": "2026-07-07",
                    "ventes": 72
                  },
                  {
                    "date": "2026-07-08",
                    "ventes": 105
                  },
                  {
                    "date": "2026-07-09",
                    "ventes": 117
                  },
                  {
                    "date": "2026-07-10",
                    "ventes": 123
                  },
                  {
                    "date": "2026-07-11",
                    "ventes": 126
                  },
                  {
                    "date": "2026-07-12",
                    "ventes": 135
                  },
                  {
                    "date": "2026-07-13",
                    "ventes": 89
                  },
                  {
                    "date": "2026-07-14",
                    "ventes": 77
                  },
                  {
                    "date": "2026-07-15",
                    "ventes": 110
                  },
                  {
                    "date": "2026-07-16",
                    "ventes": 122
                  },
                  {
                    "date": "2026-07-17",
                    "ventes": 128
                  },
                  {
                    "date": "2026-07-18",
                    "ventes": 131
                  },
                  {
                    "date": "2026-07-19",
                    "ventes": 140
                  },
                  {
                    "date": "2026-07-20",
                    "ventes": 94
                  },
                  {
                    "date": "2026-07-21",
                    "ventes": 82
                  },
                  {
                    "date": "2026-07-22",
                    "ventes": 115
                  },
                  {
                    "date": "2026-07-23",
                    "ventes": 127
                  },
                  {
                    "date": "2026-07-24",
                    "ventes": 133
                  },
                  {
                    "date": "2026-07-25",
                    "ventes": 136
                  },
                  {
                    "date": "2026-07-26",
                    "ventes": 145
                  },
                  {
                    "date": "2026-07-27",
                    "ventes": 99
                  },
                  {
                    "date": "2026-07-28",
                    "ventes": 87
                  }
                ]
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "target",
                  "file"
                ],
                "properties": {
                  "engine_family": {
                    "type": "string",
                    "enum": [
                      "flair",
                      "presage",
                      "prophecy"
                    ],
                    "example": "presage",
                    "description": "Engine family for this call. Omitted: the family marked is_default in GET /v1/engine-families. No persisted model is used. engine_version must belong to this family, including when the family is omitted."
                  },
                  "engine_version": {
                    "type": "string",
                    "pattern": "^(flair|presage|prophecy)-(latest|[1-9][0-9]*(\\.[0-9]+)?)$",
                    "maxLength": 64,
                    "example": "presage-2",
                    "description": "Full engine identifier within the selected family, e.g. presage-2 or presage-latest. Omitted or <family>-latest: latest active version of that family. A different family is refused. Responses identify the exact version used."
                  },
                  "backtest": {
                    "type": "boolean",
                    "default": false
                  },
                  "include_backtest_predictions": {
                    "type": "boolean",
                    "default": false,
                    "description": "Return the historical observations already evaluated by the backtest, without additional inference or credits. On POST /v1/forecast, also set backtest=true. Does not force a skipped or disabled backtest to run. Detail is always JSON."
                  },
                  "target": {
                    "type": "string"
                  },
                  "horizon": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 1000
                  },
                  "prediction_length": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 1000
                  },
                  "point_estimate": {
                    "type": "string",
                    "enum": [
                      "mean",
                      "median"
                    ],
                    "default": "mean"
                  },
                  "quantiles": {
                    "type": "string",
                    "description": "Comma-separated quantiles, e.g. 0.1,0.5,0.9."
                  },
                  "item_id": {
                    "type": "string"
                  },
                  "file": {
                    "type": "string",
                    "format": "binary"
                  },
                  "timestamp": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "X-Spavik-Model-Version": {
                "schema": {
                  "type": "string",
                  "example": "presage-2"
                },
                "description": "Exact engine used to produce this result, including on an idempotent replay. Never a latest alias. Distinct from the model data version."
              },
              "X-Spavik-Predictions-Remaining": {
                "schema": {
                  "type": "integer"
                },
                "description": "Predictions still available after this call: monthly allowance left plus balance. An absent header means the balance is unknown, not necessarily unlimited (for example on a replay). GET /v1/usage explicitly reports unlimited_usage and predictions_remaining."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ForecastResult"
                }
              }
            }
          },
          "400": {
            "description": "ENGINE_FAMILY_REQUIRED, ENGINE_FAMILY_MISMATCH, INVALID_ENGINE_VERSION, UNKNOWN_ENGINE_VERSION, or invalid series or horizon (INVALID_INPUT: fewer than ten points, irregular interval, unreadable target).",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Authentication required, or invalid token.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "CREDITS_EXHAUSTED: insufficient credits for the requested horizon; ENGINE_PLAN_REQUIRED: the selected engine requires a higher plan.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This caller is not allowed to perform this action.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "ENGINE_RETIRED: the requested engine version has been retired. Select an active version or omit engine_version.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Payload too large.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "VALIDATION_ERROR: invalid engine request parameters. Older engines may return FORECAST_FAILED for a refused computation.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "ENGINE_BUSY, ENGINE_UNAVAILABLE or USAGE_ACCOUNTING_UNAVAILABLE: follow the retry policy with the original Idempotency-Key. ENGINE_CATALOGUE_MISMATCH: the configured engine price is unavailable or the serving engine differs; no credits charged, contact support.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ]
      }
    },
    "/v1/datasets/evaluate": {
      "parameters": [
        {
          "name": "User-Agent",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 1024
          },
          "description": "Information about the agent originating the request, when known. Browsers supply their own value. Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        },
        {
          "name": "X-Spavik-Client-Name",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 128
          },
          "description": "HTTP caller: SDK, MCP server or direct application (for example spavik-sdk-python, spavik-sdk-javascript or spavik-mcp). Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        },
        {
          "name": "X-Spavik-Client-Version",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 64
          },
          "description": "Version of that SDK, MCP server or direct application. Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        }
      ],
      "post": {
        "x-audience": "client",
        "tags": [
          "Models"
        ],
        "summary": "Validate dataset structure and compare trial scores with baselines",
        "description": "Creates no model and charges nothing. Returns structural validation and an unpersisted engine trial. Input and response retention follows the account data policy; aggregate quality telemetry is retained for up to 90 days. Every HTTP 200 response includes a non-null assessment (status, metric, message). The single assessment describes an observed comparison, not statistical significance or future reliability. Classification uses AUC as the primary discrimination metric, compared with its reference (normally 0.5). Accuracy remains a secondary decision metric; it cannot replace missing AUC or override its comparison. Regression uses MAE and requires baselines.MAE.fit_on = train. Older engines that compute a mean on test labels remain inconclusive until that reference is corrected.\n\nA strict improvement gives better_than_baseline, equality or worse gives not_better_than_baseline. Missing or invalid primary score, required reference or n_test gives inconclusive. No fixed 0.02 margin or 30-observation threshold is used. Small tests report their actual count and only an observed comparison. n_sampled_from is the engine count before sampling, after dropping missing targets; n_rows is the input count. The current engine uses a random holdout; the API cannot verify independence of people or periods. trial.evaluation exposes the engine protocol, original train/test row positions, sampling counts, class counts and the AUC definition or unavailability reason. It is null when not supplied. High accuracy, exact target copies and duplicate rows do not change the numeric comparison; structural warnings separately ask the caller to verify feature availability and independence.\n\nWhen validation fails, trial is null and assessment.status is not_evaluated. The former signal and trial.verdict fields are replaced by assessment. Forward assessment unchanged; an absent or null assessment is a contract violation, not inconclusive. Use POST /v1/forecast/backtest for time series.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data",
                  "target"
                ],
                "properties": {
                  "target": {
                    "type": "string"
                  },
                  "engine_family": {
                    "type": "string",
                    "enum": [
                      "flair",
                      "presage",
                      "prophecy"
                    ],
                    "example": "presage",
                    "description": "Immutable model family. Omitted: the family marked is_default in GET /v1/engine-families. The latest active engine prepares the initial context; predictions can only use versions of this family."
                  },
                  "name": {
                    "type": "string",
                    "description": "Model name."
                  },
                  "explain": {
                    "type": "boolean",
                    "description": "Asks for the most influential features (version.top_factors). Off by default: measuring them roughly triples training time."
                  },
                  "auto_update": {
                    "type": "boolean",
                    "description": "Fold outcomes in without waiting for a call to /refresh. Each post to /outcomes then triggers an integration: send your outcomes in batches."
                  },
                  "retain_outcomes": {
                    "type": "boolean",
                    "deprecated": true,
                    "description": "Deprecated and ignored. Outcomes and refresh do not require a retention opt-in. Inference data retention is unchanged."
                  },
                  "data": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "additionalProperties": true
                    },
                    "description": "Data rows, one object per row."
                  }
                }
              },
              "example": {
                "target": "churn",
                "engine_family": "presage",
                "data": [
                  {
                    "plan": "pro",
                    "seats": 12,
                    "tickets_90d": 3,
                    "churn": 0
                  },
                  {
                    "plan": "free",
                    "seats": 1,
                    "tickets_90d": 9,
                    "churn": 1
                  },
                  {
                    "plan": "pro",
                    "seats": 40,
                    "tickets_90d": 0,
                    "churn": 0
                  },
                  {
                    "plan": "free",
                    "seats": 2,
                    "tickets_90d": 7,
                    "churn": 1
                  },
                  {
                    "plan": "growth",
                    "seats": 120,
                    "tickets_90d": 1,
                    "churn": 0
                  },
                  {
                    "plan": "free",
                    "seats": 1,
                    "tickets_90d": 12,
                    "churn": 1
                  },
                  {
                    "plan": "pro",
                    "seats": 25,
                    "tickets_90d": 2,
                    "churn": 0
                  },
                  {
                    "plan": "free",
                    "seats": 3,
                    "tickets_90d": 5,
                    "churn": 1
                  },
                  {
                    "plan": "growth",
                    "seats": 80,
                    "tickets_90d": 4,
                    "churn": 0
                  },
                  {
                    "plan": "pro",
                    "seats": 8,
                    "tickets_90d": 11,
                    "churn": 1
                  },
                  {
                    "plan": "growth",
                    "seats": 200,
                    "tickets_90d": 0,
                    "churn": 0
                  },
                  {
                    "plan": "free",
                    "seats": 4,
                    "tickets_90d": 2,
                    "churn": 0
                  }
                ]
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file",
                  "target"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "Labelled history (CSV)."
                  },
                  "target": {
                    "type": "string"
                  },
                  "engine_family": {
                    "type": "string",
                    "enum": [
                      "flair",
                      "presage",
                      "prophecy"
                    ],
                    "example": "presage",
                    "description": "Immutable model family. Omitted: the family marked is_default in GET /v1/engine-families. The latest active engine prepares the initial context; predictions can only use versions of this family."
                  },
                  "name": {
                    "type": "string",
                    "description": "Model name."
                  },
                  "explain": {
                    "type": "boolean",
                    "description": "Asks for the most influential features (version.top_factors). Off by default: measuring them roughly triples training time."
                  },
                  "auto_update": {
                    "type": "boolean",
                    "description": "Fold outcomes in without waiting for a call to /refresh. Each post to /outcomes then triggers an integration: send your outcomes in batches."
                  },
                  "retain_outcomes": {
                    "type": "boolean",
                    "deprecated": true,
                    "description": "Deprecated and ignored. Outcomes and refresh do not require a retention opt-in. Inference data retention is unchanged."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "assessment"
                  ],
                  "properties": {
                    "target": {
                      "type": "string"
                    },
                    "task": {
                      "type": "string",
                      "enum": [
                        "classification",
                        "regression"
                      ]
                    },
                    "n_rows": {
                      "type": "integer"
                    },
                    "columns": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "validation": {
                      "type": "object",
                      "additionalProperties": true
                    },
                    "assessment": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "status",
                        "metric",
                        "message"
                      ],
                      "properties": {
                        "status": {
                          "type": "string",
                          "description": "Display better_than_baseline as Better than the reference on this sample, alongside assessment.message and the evaluated observation count. A dataset without signal can beat a baseline by chance. Do not present this as a reliability or deployment approval.",
                          "enum": [
                            "better_than_baseline",
                            "not_better_than_baseline",
                            "inconclusive",
                            "not_evaluated"
                          ]
                        },
                        "metric": {
                          "type": "string",
                          "enum": [
                            "AUC",
                            "MAE",
                            "MASE"
                          ]
                        },
                        "message": {
                          "type": "string"
                        }
                      },
                      "description": "The single API conclusion, always present and non-null in HTTP 200 responses, including when the trial cannot run. Forward status, metric and message without recomputing them from scores. An absent or null assessment is a contract violation, not an inconclusive result. This is a descriptive comparison on the evaluated observations: better means strictly better than every required reference; equality is not better. No fixed gain or sample-size threshold implies significance. Inconclusive means missing or invalid evidence, or a skipped backtest; not_evaluated means validation prevented the trial. No status establishes reliability, future performance or absence of predictive signal."
                    },
                    "hint": {
                      "type": "string",
                      "nullable": true,
                      "description": "The first thing to fix, taken from the most serious issue."
                    },
                    "trial": {
                      "type": "object",
                      "nullable": true,
                      "additionalProperties": true,
                      "description": "What the engine makes of the data. Null when validation failed.",
                      "properties": {
                        "engine": {
                          "type": "string",
                          "nullable": true,
                          "description": "Exact engine that performed the evaluation."
                        },
                        "metrics": {
                          "type": "object",
                          "nullable": true,
                          "additionalProperties": {
                            "type": "number",
                            "nullable": true
                          },
                          "description": "Scores on evaluated observations. Classification: AUC, Accuracy, AveragePrecision (binary only), LogLoss and MacroF1. Regression: MAE, RMSE, R². Forecasting adds MASE, Bias, BiasRatio and Pinball. Undefined scores are null; see metric_context.undefined_reasons. Secondary scores do not change the primary assessment.",
                          "properties": {
                            "AUC": {
                              "type": "number",
                              "nullable": true,
                              "minimum": 0,
                              "maximum": 1,
                              "description": "ROC AUC, binary positive class or multiclass one-vs-rest macro average."
                            },
                            "Accuracy": {
                              "type": "number",
                              "nullable": true,
                              "minimum": 0,
                              "maximum": 1
                            },
                            "AveragePrecision": {
                              "type": "number",
                              "nullable": true,
                              "minimum": 0,
                              "maximum": 1,
                              "description": "Binary non-interpolated average precision, not trapezoidal PR-AUC. Null without positive observations."
                            },
                            "LogLoss": {
                              "type": "number",
                              "nullable": true,
                              "minimum": 0,
                              "description": "Mean negative log probability of the actual class, natural logarithm, float64 epsilon clipping. Lower is better."
                            },
                            "MacroF1": {
                              "type": "number",
                              "nullable": true,
                              "minimum": 0,
                              "maximum": 1,
                              "description": "Unweighted mean of defined per-class F1 scores. Classes absent from both actual and predicted labels are excluded."
                            },
                            "MAE": {
                              "type": "number",
                              "nullable": true,
                              "minimum": 0,
                              "description": "Mean absolute error in target units."
                            },
                            "RMSE": {
                              "type": "number",
                              "nullable": true,
                              "minimum": 0
                            },
                            "R²": {
                              "type": "number",
                              "nullable": true,
                              "description": "Null for fewer than two observations or constant actual values. Can be negative."
                            },
                            "MASE": {
                              "type": "number",
                              "nullable": true,
                              "minimum": 0,
                              "description": "Compare with the scored naive baseline on the same backtest, not with a fixed threshold of 1."
                            },
                            "Bias": {
                              "type": "number",
                              "nullable": true
                            },
                            "BiasRatio": {
                              "type": "number",
                              "nullable": true
                            },
                            "Pinball": {
                              "type": "number",
                              "nullable": true,
                              "minimum": 0
                            }
                          }
                        },
                        "classification_report": {
                          "$ref": "#/components/schemas/ClassificationReport"
                        },
                        "calibration": {
                          "$ref": "#/components/schemas/Calibration"
                        },
                        "baselines": {
                          "type": "object",
                          "nullable": true,
                          "description": "References indexed by metric. Read floor for the numeric score, not the whole object. Fit provenance must come from the engine; a missing fit_on does not mean train.",
                          "additionalProperties": {
                            "type": "object",
                            "properties": {
                              "floor": {
                                "type": "number",
                                "nullable": true
                              },
                              "class_order": {
                                "type": "array",
                                "nullable": true,
                                "items": {
                                  "type": "string"
                                }
                              },
                              "probabilities": {
                                "type": "array",
                                "nullable": true,
                                "items": {
                                  "type": "number",
                                  "minimum": 0,
                                  "maximum": 1
                                }
                              },
                              "undefined_reason": {
                                "type": "string",
                                "nullable": true
                              },
                              "relative_error_reduction": {
                                "type": "number",
                                "nullable": true,
                                "description": "For observed loss metrics only: 1 - model score / baseline score on identical outcomes. Fraction, possibly negative. Null for non-loss metrics, zero baseline or unavailable scores."
                              },
                              "reduction_undefined_reason": {
                                "type": "string",
                                "nullable": true
                              },
                              "label": {
                                "type": "string"
                              },
                              "higher_better": {
                                "type": "boolean"
                              },
                              "fit_on": {
                                "type": "string",
                                "nullable": true,
                                "enum": [
                                  "train",
                                  "test",
                                  "theoretical",
                                  null
                                ],
                                "description": "Where reference parameters were determined. MAE comparison requires train."
                              },
                              "prediction": {
                                "type": [
                                  "number",
                                  "string",
                                  "null"
                                ],
                                "description": "Constant prediction fitted on training data, or null for a probability vector or forecasting reference."
                              },
                              "constant_floor": {
                                "type": "number",
                                "nullable": true,
                                "description": "MASE of the historical-median reference on the same evaluation points."
                              },
                              "constant_label": {
                                "type": "string",
                                "nullable": true
                              }
                            }
                          }
                        },
                        "evaluation": {
                          "type": "object",
                          "nullable": true,
                          "description": "Engine evaluation metadata, forwarded without recalculating the split. Null when not supplied by the engine. Row positions are zero-based in the received data, excluding its header and before dropping missing targets.",
                          "properties": {
                            "metric_context": {
                              "$ref": "#/components/schemas/MetricContext"
                            },
                            "protocol_version": {
                              "type": "integer",
                              "example": 2
                            },
                            "interpretation": {
                              "type": "string",
                              "enum": [
                                "descriptive_only"
                              ]
                            },
                            "kind": {
                              "type": "string",
                              "enum": [
                                "random_holdout"
                              ]
                            },
                            "split": {
                              "type": "object",
                              "properties": {
                                "seed": {
                                  "type": "integer"
                                },
                                "shuffle": {
                                  "type": "boolean"
                                },
                                "test_fraction_requested": {
                                  "type": "number"
                                },
                                "stratified": {
                                  "type": "boolean"
                                },
                                "stratification_reason": {
                                  "type": "string"
                                },
                                "train_rows": {
                                  "type": "array",
                                  "items": {
                                    "type": "integer",
                                    "minimum": 0
                                  }
                                },
                                "test_rows": {
                                  "type": "array",
                                  "items": {
                                    "type": "integer",
                                    "minimum": 0
                                  }
                                }
                              }
                            },
                            "sampling": {
                              "type": "object",
                              "properties": {
                                "method": {
                                  "type": "string",
                                  "enum": [
                                    "none",
                                    "uniform_without_replacement"
                                  ]
                                },
                                "seed": {
                                  "type": "integer"
                                },
                                "n_input": {
                                  "type": "integer",
                                  "minimum": 0
                                },
                                "n_missing_target": {
                                  "type": "integer",
                                  "minimum": 0
                                },
                                "n_eligible": {
                                  "type": "integer",
                                  "minimum": 0
                                },
                                "n_selected": {
                                  "type": "integer",
                                  "minimum": 0
                                },
                                "max_samples_requested": {
                                  "type": "integer",
                                  "minimum": 0
                                },
                                "effective_cap": {
                                  "type": "integer",
                                  "minimum": 0
                                }
                              }
                            },
                            "class_counts": {
                              "type": "object",
                              "nullable": true,
                              "properties": {
                                "eligible": {
                                  "type": "array",
                                  "items": {
                                    "type": "object",
                                    "properties": {
                                      "class": {
                                        "type": "string"
                                      },
                                      "count": {
                                        "type": "integer",
                                        "minimum": 0
                                      }
                                    }
                                  }
                                },
                                "sampled": {
                                  "type": "array",
                                  "items": {
                                    "type": "object",
                                    "properties": {
                                      "class": {
                                        "type": "string"
                                      },
                                      "count": {
                                        "type": "integer",
                                        "minimum": 0
                                      }
                                    }
                                  }
                                },
                                "train": {
                                  "type": "array",
                                  "items": {
                                    "type": "object",
                                    "properties": {
                                      "class": {
                                        "type": "string"
                                      },
                                      "count": {
                                        "type": "integer",
                                        "minimum": 0
                                      }
                                    }
                                  }
                                },
                                "test": {
                                  "type": "array",
                                  "items": {
                                    "type": "object",
                                    "properties": {
                                      "class": {
                                        "type": "string"
                                      },
                                      "count": {
                                        "type": "integer",
                                        "minimum": 0
                                      }
                                    }
                                  }
                                }
                              }
                            },
                            "auc": {
                              "type": "object",
                              "nullable": true,
                              "properties": {
                                "variant": {
                                  "type": "string",
                                  "description": "Binary ROC AUC or multiclass one-vs-rest with an unweighted macro average."
                                },
                                "class_order": {
                                  "type": "array",
                                  "items": {
                                    "type": "string"
                                  }
                                },
                                "positive_class": {
                                  "type": "string",
                                  "nullable": true
                                },
                                "available": {
                                  "type": "boolean"
                                },
                                "unavailable_reason": {
                                  "type": "string",
                                  "nullable": true
                                }
                              }
                            }
                          }
                        },
                        "n_train": {
                          "type": "integer",
                          "nullable": true,
                          "minimum": 1
                        },
                        "n_test": {
                          "type": "integer",
                          "nullable": true,
                          "minimum": 1
                        },
                        "n_sampled_from": {
                          "type": "integer",
                          "nullable": true,
                          "minimum": 1
                        },
                        "warnings": {
                          "type": "array",
                          "nullable": true,
                          "items": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Data missing, target missing, or unreadable CSV.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Authentication required, or invalid token.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This caller is not allowed to perform this action.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "ENGINE_BUSY or ENGINE_UNAVAILABLE: engine capacity or availability failure. Try again shortly.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ]
      }
    },
    "/v1/models": {
      "parameters": [
        {
          "name": "User-Agent",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 1024
          },
          "description": "Information about the agent originating the request, when known. Browsers supply their own value. Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        },
        {
          "name": "X-Spavik-Client-Name",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 128
          },
          "description": "HTTP caller: SDK, MCP server or direct application (for example spavik-sdk-python, spavik-sdk-javascript or spavik-mcp). Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        },
        {
          "name": "X-Spavik-Client-Version",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 64
          },
          "description": "Version of that SDK, MCP server or direct application. Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        }
      ],
      "get": {
        "x-audience": "client",
        "tags": [
          "Models"
        ],
        "summary": "List the workspace models",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 200
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 0
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "archived"
              ]
            },
            "description": "Serving models by default. `archived` lists what was set aside, so it can be found again and restored. Deleted models are never listed."
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Model"
                      }
                    },
                    "pagination": {
                      "$ref": "#/components/schemas/Pagination"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication required, or invalid token.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This caller is not allowed to perform this action.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ]
      },
      "post": {
        "x-audience": "client",
        "tags": [
          "Models"
        ],
        "summary": "Create a model (data + target) and train it",
        "description": "Creating a model is training it: the data goes in the same call, as JSON ('data') or CSV (the 'file' field). The response carries the identifier to use afterwards to predict (POST /v1/models/{id}/predict), without sending the data again.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Same workspace, method, path, key and payload prevent another execution or charge, subject to current model access. Successful responses are replayed for every data policy, including ZDR, from the technical PostgreSQL cache (default retention 24 hours from operation creation). The data policy only controls inference input/output archiving to GCS/BigQuery; it never changes replay behavior. A different key or an expired completed key starts a new operation and may be charged again. REQUEST_IN_PROGRESS: wait Retry-After. REQUEST_INTERRUPTED: outcome uncertain; contact support. IDEMPOTENCY_RESPONSE_UNAVAILABLE: a completed response is missing; do not retry with a new key. Completed keys expire; unresolved keys remain protected. Reusing a key with different input returns 422. A key is required on the original call for safe retries."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "data",
                  "target"
                ],
                "properties": {
                  "target": {
                    "type": "string"
                  },
                  "engine_family": {
                    "type": "string",
                    "enum": [
                      "flair",
                      "presage",
                      "prophecy"
                    ],
                    "example": "presage",
                    "description": "Immutable model family. Omitted: the family marked is_default in GET /v1/engine-families. The latest active engine prepares the initial context; predictions can only use versions of this family."
                  },
                  "name": {
                    "type": "string",
                    "description": "Model name."
                  },
                  "explain": {
                    "type": "boolean",
                    "description": "Asks for the most influential features (version.top_factors). Off by default: measuring them roughly triples training time."
                  },
                  "auto_update": {
                    "type": "boolean",
                    "description": "Fold outcomes in without waiting for a call to /refresh. Each post to /outcomes then triggers an integration: send your outcomes in batches."
                  },
                  "retain_outcomes": {
                    "type": "boolean",
                    "deprecated": true,
                    "description": "Deprecated and ignored. Outcomes and refresh do not require a retention opt-in. Inference data retention is unchanged."
                  },
                  "data": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "additionalProperties": true
                    },
                    "description": "Data rows, one object per row."
                  }
                }
              },
              "example": {
                "target": "churn",
                "engine_family": "presage",
                "data": [
                  {
                    "plan": "pro",
                    "seats": 12,
                    "tickets_90d": 3,
                    "churn": 0
                  },
                  {
                    "plan": "free",
                    "seats": 1,
                    "tickets_90d": 9,
                    "churn": 1
                  },
                  {
                    "plan": "pro",
                    "seats": 40,
                    "tickets_90d": 0,
                    "churn": 0
                  },
                  {
                    "plan": "free",
                    "seats": 2,
                    "tickets_90d": 7,
                    "churn": 1
                  },
                  {
                    "plan": "growth",
                    "seats": 120,
                    "tickets_90d": 1,
                    "churn": 0
                  },
                  {
                    "plan": "free",
                    "seats": 1,
                    "tickets_90d": 12,
                    "churn": 1
                  },
                  {
                    "plan": "pro",
                    "seats": 25,
                    "tickets_90d": 2,
                    "churn": 0
                  },
                  {
                    "plan": "free",
                    "seats": 3,
                    "tickets_90d": 5,
                    "churn": 1
                  },
                  {
                    "plan": "growth",
                    "seats": 80,
                    "tickets_90d": 4,
                    "churn": 0
                  },
                  {
                    "plan": "pro",
                    "seats": 8,
                    "tickets_90d": 11,
                    "churn": 1
                  },
                  {
                    "plan": "growth",
                    "seats": 200,
                    "tickets_90d": 0,
                    "churn": 0
                  },
                  {
                    "plan": "free",
                    "seats": 4,
                    "tickets_90d": 2,
                    "churn": 0
                  }
                ]
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file",
                  "target"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary",
                    "description": "Labelled history (CSV)."
                  },
                  "target": {
                    "type": "string"
                  },
                  "engine_family": {
                    "type": "string",
                    "enum": [
                      "flair",
                      "presage",
                      "prophecy"
                    ],
                    "example": "presage",
                    "description": "Immutable model family. Omitted: the family marked is_default in GET /v1/engine-families. The latest active engine prepares the initial context; predictions can only use versions of this family."
                  },
                  "name": {
                    "type": "string",
                    "description": "Model name."
                  },
                  "explain": {
                    "type": "boolean",
                    "description": "Asks for the most influential features (version.top_factors). Off by default: measuring them roughly triples training time."
                  },
                  "auto_update": {
                    "type": "boolean",
                    "description": "Fold outcomes in without waiting for a call to /refresh. Each post to /outcomes then triggers an integration: send your outcomes in batches."
                  },
                  "retain_outcomes": {
                    "type": "boolean",
                    "deprecated": true,
                    "description": "Deprecated and ignored. Outcomes and refresh do not require a retention opt-in. Inference data retention is unchanged."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Model trained",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "X-Spavik-Model-Version": {
                "schema": {
                  "type": "string",
                  "example": "presage-2"
                },
                "description": "Exact engine used to produce this result, including on an idempotent replay. Never a latest alias. Distinct from the model data version."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TrainingResult"
                }
              }
            }
          },
          "401": {
            "description": "Authentication required, or invalid token.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Out of predictions: monthly allowance and balance are both empty.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This caller is not allowed to perform this action.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Payload too large.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Data validation failed.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "ENGINE_BUSY, ENGINE_UNAVAILABLE or USAGE_ACCOUNTING_UNAVAILABLE: temporary engine or accounting failure. Retry with the same Idempotency-Key.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ]
      }
    },
    "/v1/models/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "Model identifier or slug"
        },
        {
          "name": "User-Agent",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 1024
          },
          "description": "Information about the agent originating the request, when known. Browsers supply their own value. Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        },
        {
          "name": "X-Spavik-Client-Name",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 128
          },
          "description": "HTTP caller: SDK, MCP server or direct application (for example spavik-sdk-python, spavik-sdk-javascript or spavik-mcp). Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        },
        {
          "name": "X-Spavik-Client-Version",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 64
          },
          "description": "Version of that SDK, MCP server or direct application. Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        }
      ],
      "get": {
        "x-audience": "client",
        "tags": [
          "Models"
        ],
        "summary": "Model details",
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Model"
                }
              }
            }
          },
          "401": {
            "description": "Authentication required, or invalid token.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This caller is not allowed to perform this action.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown model.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ]
      },
      "patch": {
        "x-audience": "client",
        "tags": [
          "Models"
        ],
        "summary": "Update a model and its automatic refresh preferences",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "auto_update": {
                    "type": "boolean"
                  },
                  "retain_outcomes": {
                    "type": "boolean",
                    "deprecated": true,
                    "description": "Deprecated and ignored. Outcomes and refresh are always available."
                  }
                },
                "required": []
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Model"
                }
              }
            }
          },
          "401": {
            "description": "Authentication required, or invalid token.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This caller is not allowed to perform this action.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Target and task are frozen once trained.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ]
      },
      "delete": {
        "x-audience": "client",
        "tags": [
          "Models"
        ],
        "summary": "Delete a model for good",
        "description": "Irreversible: the artefacts are purged from the engine and the pending outcomes go with them. To stop serving a model without losing it, archive it instead. If engine purge fails, the model stops serving and deletion returns 503. Retry DELETE to resume cleanup.",
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "401": {
            "description": "Authentication required, or invalid token.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This caller is not allowed to perform this action.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "ENGINE_UNAVAILABLE: model purge could not be confirmed. Retry deletion.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ]
      }
    },
    "/v1/models/{id}/archive": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "Model identifier or slug"
        },
        {
          "name": "User-Agent",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 1024
          },
          "description": "Information about the agent originating the request, when known. Browsers supply their own value. Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        },
        {
          "name": "X-Spavik-Client-Name",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 128
          },
          "description": "HTTP caller: SDK, MCP server or direct application (for example spavik-sdk-python, spavik-sdk-javascript or spavik-mcp). Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        },
        {
          "name": "X-Spavik-Client-Version",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 64
          },
          "description": "Version of that SDK, MCP server or direct application. Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        }
      ],
      "post": {
        "x-audience": "client",
        "tags": [
          "Models"
        ],
        "summary": "Set a model aside",
        "description": "Nothing is destroyed: the trained artefacts are kept, the model simply stops serving and stops counting against the model quota. Restore it whenever you need it again. Predicting on an archived model returns 409 MODEL_ARCHIVED.",
        "responses": {
          "204": {
            "description": "Archived"
          },
          "401": {
            "description": "Authentication required, or invalid token.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This caller is not allowed to perform this action.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown model.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ]
      }
    },
    "/v1/models/{id}/restore": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "Model identifier or slug"
        },
        {
          "name": "User-Agent",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 1024
          },
          "description": "Information about the agent originating the request, when known. Browsers supply their own value. Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        },
        {
          "name": "X-Spavik-Client-Name",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 128
          },
          "description": "HTTP caller: SDK, MCP server or direct application (for example spavik-sdk-python, spavik-sdk-javascript or spavik-mcp). Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        },
        {
          "name": "X-Spavik-Client-Version",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 64
          },
          "description": "Version of that SDK, MCP server or direct application. Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        }
      ],
      "post": {
        "x-audience": "client",
        "tags": [
          "Models"
        ],
        "summary": "Put an archived model back in service",
        "description": "Takes a place in the model quota again, so it can be refused when the plan is full. A model deleted for good cannot be restored (409 MODEL_DELETED). If the engine has changed, predictions prepare the retained context on the selected family engine. An old version without retained context returns CONTEXT_UNAVAILABLE.",
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Model"
                }
              }
            }
          },
          "401": {
            "description": "Authentication required, or invalid token.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "QUOTA_MODELS_EXCEEDED: the plan has no free slot left.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This caller is not allowed to perform this action.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown model.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "MODEL_DELETED: deleted for good, nothing to restore.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ]
      }
    },
    "/v1/models/{id}/versions": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "Model identifier or slug"
        },
        {
          "name": "User-Agent",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 1024
          },
          "description": "Information about the agent originating the request, when known. Browsers supply their own value. Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        },
        {
          "name": "X-Spavik-Client-Name",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 128
          },
          "description": "HTTP caller: SDK, MCP server or direct application (for example spavik-sdk-python, spavik-sdk-javascript or spavik-mcp). Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        },
        {
          "name": "X-Spavik-Client-Version",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 64
          },
          "description": "Version of that SDK, MCP server or direct application. Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        }
      ],
      "get": {
        "x-audience": "client",
        "tags": [
          "Models"
        ],
        "summary": "List versions",
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ModelVersion"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication required, or invalid token.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This caller is not allowed to perform this action.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ]
      }
    },
    "/v1/models/{id}/predict": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "Model identifier or slug"
        },
        {
          "name": "User-Agent",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 1024
          },
          "description": "Information about the agent originating the request, when known. Browsers supply their own value. Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        },
        {
          "name": "X-Spavik-Client-Name",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 128
          },
          "description": "HTTP caller: SDK, MCP server or direct application (for example spavik-sdk-python, spavik-sdk-javascript or spavik-mcp). Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        },
        {
          "name": "X-Spavik-Client-Version",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 64
          },
          "description": "Version of that SDK, MCP server or direct application. Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        }
      ],
      "post": {
        "x-audience": "client",
        "tags": [
          "Prediction"
        ],
        "summary": "Score rows with a trained model",
        "description": "The model family is fixed at creation. engine_version accepts a full identifier such as presage-2 or presage-latest. Omission and <family>-latest select the latest active version within the model family. Engines from another family are refused before computation or billing. Save results and their request_id values in your application if you need them later: no prediction-history endpoint is available on any plan. The request log contains technical metadata, not prediction values.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Same workspace, method, path, key and payload prevent another execution or charge, subject to current model access. Successful responses are replayed for every data policy, including ZDR, from the technical PostgreSQL cache (default retention 24 hours from operation creation). The data policy only controls inference input/output archiving to GCS/BigQuery; it never changes replay behavior. A different key or an expired completed key starts a new operation and may be charged again. REQUEST_IN_PROGRESS: wait Retry-After. REQUEST_INTERRUPTED: outcome uncertain; contact support. IDEMPOTENCY_RESPONSE_UNAVAILABLE: a completed response is missing; do not retry with a new key. Completed keys expire; unresolved keys remain protected. Reusing a key with different input returns 422. A key is required on the original call for safe retries."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "rows": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "additionalProperties": true
                    },
                    "description": "Data rows, one object per row."
                  },
                  "engine_version": {
                    "type": "string",
                    "pattern": "^(flair|presage|prophecy)-(latest|[1-9][0-9]*(\\.[0-9]+)?)$",
                    "maxLength": 64,
                    "example": "presage-2",
                    "description": "Full engine identifier within the selected family, e.g. presage-2 or presage-latest. Omitted or <family>-latest: latest active version of that family. A different family is refused. Responses identify the exact version used."
                  }
                },
                "required": [
                  "rows"
                ]
              },
              "example": {
                "rows": [
                  {
                    "plan": "free",
                    "seats": 2,
                    "tickets_90d": 7
                  }
                ]
              }
            },
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary"
                  },
                  "engine_version": {
                    "type": "string",
                    "pattern": "^(flair|presage|prophecy)-(latest|[1-9][0-9]*(\\.[0-9]+)?)$",
                    "maxLength": 64,
                    "example": "presage-2",
                    "description": "Full engine identifier within the selected family, e.g. presage-2 or presage-latest. Omitted or <family>-latest: latest active version of that family. A different family is refused. Responses identify the exact version used."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "X-Spavik-Model-Version": {
                "schema": {
                  "type": "string",
                  "example": "presage-2"
                },
                "description": "Exact engine used to produce this result, including on an idempotent replay. Never a latest alias. Distinct from the model data version."
              },
              "X-Spavik-Predictions-Remaining": {
                "schema": {
                  "type": "integer"
                },
                "description": "Predictions still available after this call: monthly allowance left plus balance. An absent header means the balance is unknown, not necessarily unlimited (for example on a replay). GET /v1/usage explicitly reports unlimited_usage and predictions_remaining."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ScoringResult"
                }
              }
            }
          },
          "400": {
            "description": "INVALID_ENGINE_VERSION, UNKNOWN_ENGINE_VERSION, or ENGINE_FAMILY_MISMATCH: the requested engine must belong to the model family.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Authentication required, or invalid token.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "Out of predictions: monthly allowance and balance are both empty.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This caller is not allowed to perform this action.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "Model not trained or original context unavailable for the selected engine.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "ENGINE_RETIRED: the explicitly requested engine version has been withdrawn.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "description": "Payload too large.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "ENGINE_BUSY, ENGINE_UNAVAILABLE or USAGE_ACCOUNTING_UNAVAILABLE: temporary engine or accounting failure. Retry with the same Idempotency-Key.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ]
      }
    },
    "/v1/models/{id}/outcomes": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "Model identifier or slug"
        },
        {
          "name": "User-Agent",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 1024
          },
          "description": "Information about the agent originating the request, when known. Browsers supply their own value. Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        },
        {
          "name": "X-Spavik-Client-Name",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 128
          },
          "description": "HTTP caller: SDK, MCP server or direct application (for example spavik-sdk-python, spavik-sdk-javascript or spavik-mcp). Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        },
        {
          "name": "X-Spavik-Client-Version",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 64
          },
          "description": "Version of that SDK, MCP server or direct application. Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        }
      ],
      "post": {
        "x-audience": "client",
        "tags": [
          "Models"
        ],
        "summary": "Report what actually happened",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Same workspace, method, path, key and payload prevent another execution or charge, subject to current model access. Successful responses are replayed for every data policy, including ZDR, from the technical PostgreSQL cache (default retention 24 hours from operation creation). The data policy only controls inference input/output archiving to GCS/BigQuery; it never changes replay behavior. A different key or an expired completed key starts a new operation and may be charged again. REQUEST_IN_PROGRESS: wait Retry-After. REQUEST_INTERRUPTED: outcome uncertain; contact support. IDEMPOTENCY_RESPONSE_UNAVAILABLE: a completed response is missing; do not retry with a new key. Completed keys expire; unresolved keys remain protected. Reusing a key with different input returns 422. A key is required on the original call for safe retries."
          }
        ],
        "description": "Every outcome requires features and actual, on every plan and under every data policy. features must be a non-empty object. request_id is optional correlation metadata and never replaces features; inference inputs are not looked up during ingestion. Outcomes are functional model data and require no retention opt-in. Pending outcomes are kept until integration or model deletion. Optional client-supplied predictions populate observed performance and aggregate quality reports. Outcomes pile up, then join the context data of the model (POST /v1/models/{id}/refresh), and serve the predictions that follow. Each outcome carries its full set of features, optionally with the original request_id. **Send outcomes in batches**: one call is one batch, and on a model with automatic updates (auto_update) a batch triggers an integration. Sending outcomes one by one would therefore trigger one training run per outcome.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "outcomes"
                ],
                "properties": {
                  "outcomes": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 1000,
                    "items": {
                      "type": "object",
                      "properties": {
                        "request_id": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 64,
                          "description": "Optional original prediction identifier for correlation; never replaces features.",
                          "example": "req_4Nx7Bm2VtQd9Lp3Kwr"
                        },
                        "features": {
                          "type": "object",
                          "minProperties": 1,
                          "additionalProperties": true,
                          "description": "Observed feature row, required for every outcome regardless of plan or data policy."
                        },
                        "actual": {
                          "oneOf": [
                            {
                              "type": "string"
                            },
                            {
                              "type": "number"
                            },
                            {
                              "type": "boolean"
                            }
                          ],
                          "description": "Value actually observed for the target."
                        },
                        "prediction": {
                          "oneOf": [
                            {
                              "type": "string"
                            },
                            {
                              "type": "number"
                            },
                            {
                              "type": "boolean"
                            }
                          ],
                          "description": "Optional original prediction supplied by the client for performance measurement. Requires version and engine."
                        },
                        "probabilities": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "number",
                            "minimum": 0,
                            "maximum": 1
                          }
                        },
                        "version": {
                          "type": "integer",
                          "minimum": 1,
                          "description": "Model version that produced the supplied prediction."
                        },
                        "engine": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 64,
                          "description": "Exact engine that produced the supplied prediction; aliases are not accepted."
                        }
                      },
                      "required": [
                        "features",
                        "actual"
                      ],
                      "dependentRequired": {
                        "prediction": [
                          "version",
                          "engine"
                        ]
                      }
                    }
                  }
                }
              },
              "example": {
                "outcomes": [
                  {
                    "request_id": "req_4Nx7Bm2VtQd9Lp3Kwr",
                    "features": {
                      "plan": "pro",
                      "seats": 12,
                      "tickets_90d": 3
                    },
                    "actual": 1
                  },
                  {
                    "features": {
                      "plan": "free",
                      "seats": 2,
                      "tickets_90d": 10
                    },
                    "actual": 0
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Success",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "accepted": {
                      "type": "integer"
                    },
                    "pending_outcomes": {
                      "type": "integer"
                    },
                    "unresolved": {
                      "type": "array",
                      "deprecated": true,
                      "maxItems": 0,
                      "description": "Compatibility field, always empty. Every outcome supplies its features; no request_id lookup is performed.",
                      "items": {
                        "type": "string"
                      }
                    },
                    "refresh_triggered": {
                      "type": "boolean",
                      "description": "Model on auto_update: this batch started an integration."
                    },
                    "refresh_suggested": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "VALIDATION_ERROR: every outcome requires a non-empty features object and an actual value. The whole batch is rejected before any outcome is stored.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Authentication required, or invalid token.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This caller is not allowed to perform this action.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "Unknown model.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "REQUEST_IN_PROGRESS or REQUEST_INTERRUPTED. Completed idempotency records without a saved response return IDEMPOTENCY_RESPONSE_UNAVAILABLE.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "OUTCOME_PREDICTION_INVALID.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ]
      }
    },
    "/v1/models/{id}/performance": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "Model identifier or slug"
        },
        {
          "name": "User-Agent",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 1024
          },
          "description": "Information about the agent originating the request, when known. Browsers supply their own value. Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        },
        {
          "name": "X-Spavik-Client-Name",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 128
          },
          "description": "HTTP caller: SDK, MCP server or direct application (for example spavik-sdk-python, spavik-sdk-javascript or spavik-mcp). Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        },
        {
          "name": "X-Spavik-Client-Version",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 64
          },
          "description": "Version of that SDK, MCP server or direct application. Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        }
      ],
      "get": {
        "x-audience": "client",
        "tags": [
          "Models"
        ],
        "summary": "Real-world performance, against the training metrics",
        "description": "Compares the original predictions supplied with outcomes to their observed actual values, identically for every plan and data policy. To measure performance, include prediction, version and exact engine with each outcome, and probabilities when available. Inference archives are never consulted. source is client_reported_predictions: these measurements are client-reported, not independently verified. quality_reports exposes aggregate batch diagnostics; small-sample quality scores are null. `observed` stays null until an outcome includes a readable original prediction and its version and engine. Probability scores require client-supplied class probabilities for every compared row and compatible class orders. Missing or invalid probabilities produce null scores with reasons; label-based scores remain available. References use training parameters for each prediction version, scored on these same outcomes. No reference is fitted to the outcomes. Old versions without saved reference parameters have null baselines.",
        "parameters": [
          {
            "name": "days",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 30,
              "maximum": 365
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "source": {
                      "type": "string",
                      "enum": [
                        "client_reported_predictions"
                      ]
                    },
                    "quality_reports": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "version": {
                            "type": "integer",
                            "nullable": true
                          },
                          "engine": {
                            "type": "string",
                            "nullable": true
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "report": {
                            "type": "object",
                            "additionalProperties": true
                          }
                        }
                      }
                    },
                    "model_id": {
                      "type": "string",
                      "example": "mdl_7Kq2Vn5RtWz3Bx8Ldm"
                    },
                    "version": {
                      "type": "integer"
                    },
                    "task": {
                      "type": "string",
                      "enum": [
                        "classification",
                        "regression"
                      ]
                    },
                    "pending_outcomes": {
                      "type": "integer"
                    },
                    "refresh_suggested": {
                      "type": "boolean"
                    },
                    "training_engine": {
                      "type": "string",
                      "nullable": true
                    },
                    "training_metrics": {
                      "type": "object",
                      "nullable": true,
                      "additionalProperties": {
                        "type": "number",
                        "nullable": true
                      },
                      "description": "Scores on evaluated observations. Classification: AUC, Accuracy, AveragePrecision (binary only), LogLoss and MacroF1. Regression: MAE, RMSE, R². Forecasting adds MASE, Bias, BiasRatio and Pinball. Undefined scores are null; see metric_context.undefined_reasons. Secondary scores do not change the primary assessment.",
                      "properties": {
                        "AUC": {
                          "type": "number",
                          "nullable": true,
                          "minimum": 0,
                          "maximum": 1,
                          "description": "ROC AUC, binary positive class or multiclass one-vs-rest macro average."
                        },
                        "Accuracy": {
                          "type": "number",
                          "nullable": true,
                          "minimum": 0,
                          "maximum": 1
                        },
                        "AveragePrecision": {
                          "type": "number",
                          "nullable": true,
                          "minimum": 0,
                          "maximum": 1,
                          "description": "Binary non-interpolated average precision, not trapezoidal PR-AUC. Null without positive observations."
                        },
                        "LogLoss": {
                          "type": "number",
                          "nullable": true,
                          "minimum": 0,
                          "description": "Mean negative log probability of the actual class, natural logarithm, float64 epsilon clipping. Lower is better."
                        },
                        "MacroF1": {
                          "type": "number",
                          "nullable": true,
                          "minimum": 0,
                          "maximum": 1,
                          "description": "Unweighted mean of defined per-class F1 scores. Classes absent from both actual and predicted labels are excluded."
                        },
                        "MAE": {
                          "type": "number",
                          "nullable": true,
                          "minimum": 0,
                          "description": "Mean absolute error in target units."
                        },
                        "RMSE": {
                          "type": "number",
                          "nullable": true,
                          "minimum": 0
                        },
                        "R²": {
                          "type": "number",
                          "nullable": true,
                          "description": "Null for fewer than two observations or constant actual values. Can be negative."
                        },
                        "MASE": {
                          "type": "number",
                          "nullable": true,
                          "minimum": 0,
                          "description": "Compare with the scored naive baseline on the same backtest, not with a fixed threshold of 1."
                        },
                        "Bias": {
                          "type": "number",
                          "nullable": true
                        },
                        "BiasRatio": {
                          "type": "number",
                          "nullable": true
                        },
                        "Pinball": {
                          "type": "number",
                          "nullable": true,
                          "minimum": 0
                        }
                      }
                    },
                    "training_evaluation": {
                      "type": "object",
                      "nullable": true,
                      "description": "Engine evaluation metadata, forwarded without recalculating the split. Null when not supplied by the engine. Row positions are zero-based in the received data, excluding its header and before dropping missing targets.",
                      "properties": {
                        "metric_context": {
                          "$ref": "#/components/schemas/MetricContext"
                        },
                        "protocol_version": {
                          "type": "integer",
                          "example": 2
                        },
                        "interpretation": {
                          "type": "string",
                          "enum": [
                            "descriptive_only"
                          ]
                        },
                        "kind": {
                          "type": "string",
                          "enum": [
                            "random_holdout"
                          ]
                        },
                        "split": {
                          "type": "object",
                          "properties": {
                            "seed": {
                              "type": "integer"
                            },
                            "shuffle": {
                              "type": "boolean"
                            },
                            "test_fraction_requested": {
                              "type": "number"
                            },
                            "stratified": {
                              "type": "boolean"
                            },
                            "stratification_reason": {
                              "type": "string"
                            },
                            "train_rows": {
                              "type": "array",
                              "items": {
                                "type": "integer",
                                "minimum": 0
                              }
                            },
                            "test_rows": {
                              "type": "array",
                              "items": {
                                "type": "integer",
                                "minimum": 0
                              }
                            }
                          }
                        },
                        "sampling": {
                          "type": "object",
                          "properties": {
                            "method": {
                              "type": "string",
                              "enum": [
                                "none",
                                "uniform_without_replacement"
                              ]
                            },
                            "seed": {
                              "type": "integer"
                            },
                            "n_input": {
                              "type": "integer",
                              "minimum": 0
                            },
                            "n_missing_target": {
                              "type": "integer",
                              "minimum": 0
                            },
                            "n_eligible": {
                              "type": "integer",
                              "minimum": 0
                            },
                            "n_selected": {
                              "type": "integer",
                              "minimum": 0
                            },
                            "max_samples_requested": {
                              "type": "integer",
                              "minimum": 0
                            },
                            "effective_cap": {
                              "type": "integer",
                              "minimum": 0
                            }
                          }
                        },
                        "class_counts": {
                          "type": "object",
                          "nullable": true,
                          "properties": {
                            "eligible": {
                              "type": "array",
                              "items": {
                                "type": "object",
                                "properties": {
                                  "class": {
                                    "type": "string"
                                  },
                                  "count": {
                                    "type": "integer",
                                    "minimum": 0
                                  }
                                }
                              }
                            },
                            "sampled": {
                              "type": "array",
                              "items": {
                                "type": "object",
                                "properties": {
                                  "class": {
                                    "type": "string"
                                  },
                                  "count": {
                                    "type": "integer",
                                    "minimum": 0
                                  }
                                }
                              }
                            },
                            "train": {
                              "type": "array",
                              "items": {
                                "type": "object",
                                "properties": {
                                  "class": {
                                    "type": "string"
                                  },
                                  "count": {
                                    "type": "integer",
                                    "minimum": 0
                                  }
                                }
                              }
                            },
                            "test": {
                              "type": "array",
                              "items": {
                                "type": "object",
                                "properties": {
                                  "class": {
                                    "type": "string"
                                  },
                                  "count": {
                                    "type": "integer",
                                    "minimum": 0
                                  }
                                }
                              }
                            }
                          }
                        },
                        "auc": {
                          "type": "object",
                          "nullable": true,
                          "properties": {
                            "variant": {
                              "type": "string",
                              "description": "Binary ROC AUC or multiclass one-vs-rest with an unweighted macro average."
                            },
                            "class_order": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              }
                            },
                            "positive_class": {
                              "type": "string",
                              "nullable": true
                            },
                            "available": {
                              "type": "boolean"
                            },
                            "unavailable_reason": {
                              "type": "string",
                              "nullable": true
                            }
                          }
                        }
                      }
                    },
                    "evaluation": {
                      "type": "object",
                      "properties": {
                        "kind": {
                          "type": "string",
                          "enum": [
                            "matched_outcomes"
                          ]
                        },
                        "since": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "limit": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "fetched_count": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "readable_count": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "evaluated_count": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "limit_reached": {
                          "type": "boolean"
                        }
                      }
                    },
                    "training_classification_report": {
                      "$ref": "#/components/schemas/ClassificationReport"
                    },
                    "training_calibration": {
                      "$ref": "#/components/schemas/Calibration"
                    },
                    "observed": {
                      "type": "object",
                      "nullable": true,
                      "description": "Measure suited to the task. For classification, `metric` is accuracy and `value` the share of exact answers (higher is better). For regression, `metric` is mean_absolute_error and `value` the average gap between predicted and actual (lower is better). `baseline` is what the training reference scores on the same outcomes, using each prediction version. Missing training parameters give a null baseline. Read metrics, baselines, metric_context and assessment for the complete task-specific evaluation.",
                      "properties": {
                        "metric": {
                          "type": "string",
                          "enum": [
                            "accuracy",
                            "mean_absolute_error"
                          ]
                        },
                        "compared": {
                          "type": "integer"
                        },
                        "value": {
                          "type": "number",
                          "nullable": true
                        },
                        "baseline": {
                          "type": "number",
                          "nullable": true
                        },
                        "better_than_baseline": {
                          "type": "boolean",
                          "nullable": true
                        },
                        "classification_report": {
                          "$ref": "#/components/schemas/ClassificationReport"
                        },
                        "metrics": {
                          "type": "object",
                          "nullable": true,
                          "additionalProperties": {
                            "type": "number",
                            "nullable": true
                          },
                          "description": "Scores on evaluated observations. Classification: AUC, Accuracy, AveragePrecision (binary only), LogLoss and MacroF1. Regression: MAE, RMSE, R². Forecasting adds MASE, Bias, BiasRatio and Pinball. Undefined scores are null; see metric_context.undefined_reasons. Secondary scores do not change the primary assessment.",
                          "properties": {
                            "AUC": {
                              "type": "number",
                              "nullable": true,
                              "minimum": 0,
                              "maximum": 1,
                              "description": "ROC AUC, binary positive class or multiclass one-vs-rest macro average."
                            },
                            "Accuracy": {
                              "type": "number",
                              "nullable": true,
                              "minimum": 0,
                              "maximum": 1
                            },
                            "AveragePrecision": {
                              "type": "number",
                              "nullable": true,
                              "minimum": 0,
                              "maximum": 1,
                              "description": "Binary non-interpolated average precision, not trapezoidal PR-AUC. Null without positive observations."
                            },
                            "LogLoss": {
                              "type": "number",
                              "nullable": true,
                              "minimum": 0,
                              "description": "Mean negative log probability of the actual class, natural logarithm, float64 epsilon clipping. Lower is better."
                            },
                            "MacroF1": {
                              "type": "number",
                              "nullable": true,
                              "minimum": 0,
                              "maximum": 1,
                              "description": "Unweighted mean of defined per-class F1 scores. Classes absent from both actual and predicted labels are excluded."
                            },
                            "MAE": {
                              "type": "number",
                              "nullable": true,
                              "minimum": 0,
                              "description": "Mean absolute error in target units."
                            },
                            "RMSE": {
                              "type": "number",
                              "nullable": true,
                              "minimum": 0
                            },
                            "R²": {
                              "type": "number",
                              "nullable": true,
                              "description": "Null for fewer than two observations or constant actual values. Can be negative."
                            },
                            "MASE": {
                              "type": "number",
                              "nullable": true,
                              "minimum": 0,
                              "description": "Compare with the scored naive baseline on the same backtest, not with a fixed threshold of 1."
                            },
                            "Bias": {
                              "type": "number",
                              "nullable": true
                            },
                            "BiasRatio": {
                              "type": "number",
                              "nullable": true
                            },
                            "Pinball": {
                              "type": "number",
                              "nullable": true,
                              "minimum": 0
                            }
                          }
                        },
                        "metric_context": {
                          "$ref": "#/components/schemas/MetricContext"
                        },
                        "baselines": {
                          "type": "object",
                          "nullable": true,
                          "description": "References indexed by metric. Read floor for the numeric score, not the whole object. Fit provenance must come from the engine; a missing fit_on does not mean train.",
                          "additionalProperties": {
                            "type": "object",
                            "properties": {
                              "floor": {
                                "type": "number",
                                "nullable": true
                              },
                              "class_order": {
                                "type": "array",
                                "nullable": true,
                                "items": {
                                  "type": "string"
                                }
                              },
                              "probabilities": {
                                "type": "array",
                                "nullable": true,
                                "items": {
                                  "type": "number",
                                  "minimum": 0,
                                  "maximum": 1
                                }
                              },
                              "undefined_reason": {
                                "type": "string",
                                "nullable": true
                              },
                              "relative_error_reduction": {
                                "type": "number",
                                "nullable": true,
                                "description": "For observed loss metrics only: 1 - model score / baseline score on identical outcomes. Fraction, possibly negative. Null for non-loss metrics, zero baseline or unavailable scores."
                              },
                              "reduction_undefined_reason": {
                                "type": "string",
                                "nullable": true
                              },
                              "label": {
                                "type": "string"
                              },
                              "higher_better": {
                                "type": "boolean"
                              },
                              "fit_on": {
                                "type": "string",
                                "nullable": true,
                                "enum": [
                                  "train",
                                  "test",
                                  "theoretical",
                                  null
                                ],
                                "description": "Where reference parameters were determined. MAE comparison requires train."
                              },
                              "prediction": {
                                "type": [
                                  "number",
                                  "string",
                                  "null"
                                ],
                                "description": "Constant prediction fitted on training data, or null for a probability vector or forecasting reference."
                              },
                              "constant_floor": {
                                "type": "number",
                                "nullable": true,
                                "description": "MASE of the historical-median reference on the same evaluation points."
                              },
                              "constant_label": {
                                "type": "string",
                                "nullable": true
                              }
                            }
                          }
                        },
                        "assessment": {
                          "type": "object",
                          "additionalProperties": false,
                          "required": [
                            "status",
                            "metric",
                            "message"
                          ],
                          "properties": {
                            "status": {
                              "type": "string",
                              "description": "Display better_than_baseline as Better than the reference on this sample, alongside assessment.message and the evaluated observation count. A dataset without signal can beat a baseline by chance. Do not present this as a reliability or deployment approval.",
                              "enum": [
                                "better_than_baseline",
                                "not_better_than_baseline",
                                "inconclusive",
                                "not_evaluated"
                              ]
                            },
                            "metric": {
                              "type": "string",
                              "enum": [
                                "AUC",
                                "MAE",
                                "MASE"
                              ]
                            },
                            "message": {
                              "type": "string"
                            }
                          },
                          "description": "The single API conclusion, always present and non-null in HTTP 200 responses, including when the trial cannot run. Forward status, metric and message without recomputing them from scores. An absent or null assessment is a contract violation, not an inconclusive result. This is a descriptive comparison on the evaluated observations: better means strictly better than every required reference; equality is not better. No fixed gain or sample-size threshold implies significance. Inconclusive means missing or invalid evidence, or a skipped backtest; not_evaluated means validation prevented the trial. No status establishes reliability, future performance or absence of predictive signal."
                        },
                        "by_engine": {
                          "type": "array",
                          "description": "Observed scores grouped by data version and exact engine; historical events inherit their preparation engine.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "version": {
                                "type": "integer"
                              },
                              "engine": {
                                "type": "string",
                                "nullable": true
                              },
                              "compared": {
                                "type": "integer"
                              },
                              "metrics": {
                                "type": "object",
                                "nullable": true,
                                "additionalProperties": {
                                  "type": "number",
                                  "nullable": true
                                },
                                "description": "Scores on evaluated observations. Classification: AUC, Accuracy, AveragePrecision (binary only), LogLoss and MacroF1. Regression: MAE, RMSE, R². Forecasting adds MASE, Bias, BiasRatio and Pinball. Undefined scores are null; see metric_context.undefined_reasons. Secondary scores do not change the primary assessment.",
                                "properties": {
                                  "AUC": {
                                    "type": "number",
                                    "nullable": true,
                                    "minimum": 0,
                                    "maximum": 1,
                                    "description": "ROC AUC, binary positive class or multiclass one-vs-rest macro average."
                                  },
                                  "Accuracy": {
                                    "type": "number",
                                    "nullable": true,
                                    "minimum": 0,
                                    "maximum": 1
                                  },
                                  "AveragePrecision": {
                                    "type": "number",
                                    "nullable": true,
                                    "minimum": 0,
                                    "maximum": 1,
                                    "description": "Binary non-interpolated average precision, not trapezoidal PR-AUC. Null without positive observations."
                                  },
                                  "LogLoss": {
                                    "type": "number",
                                    "nullable": true,
                                    "minimum": 0,
                                    "description": "Mean negative log probability of the actual class, natural logarithm, float64 epsilon clipping. Lower is better."
                                  },
                                  "MacroF1": {
                                    "type": "number",
                                    "nullable": true,
                                    "minimum": 0,
                                    "maximum": 1,
                                    "description": "Unweighted mean of defined per-class F1 scores. Classes absent from both actual and predicted labels are excluded."
                                  },
                                  "MAE": {
                                    "type": "number",
                                    "nullable": true,
                                    "minimum": 0,
                                    "description": "Mean absolute error in target units."
                                  },
                                  "RMSE": {
                                    "type": "number",
                                    "nullable": true,
                                    "minimum": 0
                                  },
                                  "R²": {
                                    "type": "number",
                                    "nullable": true,
                                    "description": "Null for fewer than two observations or constant actual values. Can be negative."
                                  },
                                  "MASE": {
                                    "type": "number",
                                    "nullable": true,
                                    "minimum": 0,
                                    "description": "Compare with the scored naive baseline on the same backtest, not with a fixed threshold of 1."
                                  },
                                  "Bias": {
                                    "type": "number",
                                    "nullable": true
                                  },
                                  "BiasRatio": {
                                    "type": "number",
                                    "nullable": true
                                  },
                                  "Pinball": {
                                    "type": "number",
                                    "nullable": true,
                                    "minimum": 0
                                  }
                                }
                              },
                              "metric_context": {
                                "$ref": "#/components/schemas/MetricContext"
                              },
                              "baselines": {
                                "type": "object",
                                "nullable": true,
                                "description": "References indexed by metric. Read floor for the numeric score, not the whole object. Fit provenance must come from the engine; a missing fit_on does not mean train.",
                                "additionalProperties": {
                                  "type": "object",
                                  "properties": {
                                    "floor": {
                                      "type": "number",
                                      "nullable": true
                                    },
                                    "class_order": {
                                      "type": "array",
                                      "nullable": true,
                                      "items": {
                                        "type": "string"
                                      }
                                    },
                                    "probabilities": {
                                      "type": "array",
                                      "nullable": true,
                                      "items": {
                                        "type": "number",
                                        "minimum": 0,
                                        "maximum": 1
                                      }
                                    },
                                    "undefined_reason": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "relative_error_reduction": {
                                      "type": "number",
                                      "nullable": true,
                                      "description": "For observed loss metrics only: 1 - model score / baseline score on identical outcomes. Fraction, possibly negative. Null for non-loss metrics, zero baseline or unavailable scores."
                                    },
                                    "reduction_undefined_reason": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "label": {
                                      "type": "string"
                                    },
                                    "higher_better": {
                                      "type": "boolean"
                                    },
                                    "fit_on": {
                                      "type": "string",
                                      "nullable": true,
                                      "enum": [
                                        "train",
                                        "test",
                                        "theoretical",
                                        null
                                      ],
                                      "description": "Where reference parameters were determined. MAE comparison requires train."
                                    },
                                    "prediction": {
                                      "type": [
                                        "number",
                                        "string",
                                        "null"
                                      ],
                                      "description": "Constant prediction fitted on training data, or null for a probability vector or forecasting reference."
                                    },
                                    "constant_floor": {
                                      "type": "number",
                                      "nullable": true,
                                      "description": "MASE of the historical-median reference on the same evaluation points."
                                    },
                                    "constant_label": {
                                      "type": "string",
                                      "nullable": true
                                    }
                                  }
                                }
                              },
                              "assessment": {
                                "type": "object",
                                "additionalProperties": false,
                                "required": [
                                  "status",
                                  "metric",
                                  "message"
                                ],
                                "properties": {
                                  "status": {
                                    "type": "string",
                                    "description": "Display better_than_baseline as Better than the reference on this sample, alongside assessment.message and the evaluated observation count. A dataset without signal can beat a baseline by chance. Do not present this as a reliability or deployment approval.",
                                    "enum": [
                                      "better_than_baseline",
                                      "not_better_than_baseline",
                                      "inconclusive",
                                      "not_evaluated"
                                    ]
                                  },
                                  "metric": {
                                    "type": "string",
                                    "enum": [
                                      "AUC",
                                      "MAE",
                                      "MASE"
                                    ]
                                  },
                                  "message": {
                                    "type": "string"
                                  }
                                },
                                "description": "The single API conclusion, always present and non-null in HTTP 200 responses, including when the trial cannot run. Forward status, metric and message without recomputing them from scores. An absent or null assessment is a contract violation, not an inconclusive result. This is a descriptive comparison on the evaluated observations: better means strictly better than every required reference; equality is not better. No fixed gain or sample-size threshold implies significance. Inconclusive means missing or invalid evidence, or a skipped backtest; not_evaluated means validation prevented the trial. No status establishes reliability, future performance or absence of predictive signal."
                              },
                              "classification_report": {
                                "$ref": "#/components/schemas/ClassificationReport"
                              }
                            }
                          }
                        },
                        "by_version": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "version": {
                                "type": "integer"
                              },
                              "compared": {
                                "type": "integer",
                                "minimum": 0
                              },
                              "value": {
                                "type": [
                                  "number",
                                  "null"
                                ]
                              },
                              "classification_report": {
                                "$ref": "#/components/schemas/ClassificationReport"
                              },
                              "metrics": {
                                "type": "object",
                                "nullable": true,
                                "additionalProperties": {
                                  "type": "number",
                                  "nullable": true
                                },
                                "description": "Scores on evaluated observations. Classification: AUC, Accuracy, AveragePrecision (binary only), LogLoss and MacroF1. Regression: MAE, RMSE, R². Forecasting adds MASE, Bias, BiasRatio and Pinball. Undefined scores are null; see metric_context.undefined_reasons. Secondary scores do not change the primary assessment.",
                                "properties": {
                                  "AUC": {
                                    "type": "number",
                                    "nullable": true,
                                    "minimum": 0,
                                    "maximum": 1,
                                    "description": "ROC AUC, binary positive class or multiclass one-vs-rest macro average."
                                  },
                                  "Accuracy": {
                                    "type": "number",
                                    "nullable": true,
                                    "minimum": 0,
                                    "maximum": 1
                                  },
                                  "AveragePrecision": {
                                    "type": "number",
                                    "nullable": true,
                                    "minimum": 0,
                                    "maximum": 1,
                                    "description": "Binary non-interpolated average precision, not trapezoidal PR-AUC. Null without positive observations."
                                  },
                                  "LogLoss": {
                                    "type": "number",
                                    "nullable": true,
                                    "minimum": 0,
                                    "description": "Mean negative log probability of the actual class, natural logarithm, float64 epsilon clipping. Lower is better."
                                  },
                                  "MacroF1": {
                                    "type": "number",
                                    "nullable": true,
                                    "minimum": 0,
                                    "maximum": 1,
                                    "description": "Unweighted mean of defined per-class F1 scores. Classes absent from both actual and predicted labels are excluded."
                                  },
                                  "MAE": {
                                    "type": "number",
                                    "nullable": true,
                                    "minimum": 0,
                                    "description": "Mean absolute error in target units."
                                  },
                                  "RMSE": {
                                    "type": "number",
                                    "nullable": true,
                                    "minimum": 0
                                  },
                                  "R²": {
                                    "type": "number",
                                    "nullable": true,
                                    "description": "Null for fewer than two observations or constant actual values. Can be negative."
                                  },
                                  "MASE": {
                                    "type": "number",
                                    "nullable": true,
                                    "minimum": 0,
                                    "description": "Compare with the scored naive baseline on the same backtest, not with a fixed threshold of 1."
                                  },
                                  "Bias": {
                                    "type": "number",
                                    "nullable": true
                                  },
                                  "BiasRatio": {
                                    "type": "number",
                                    "nullable": true
                                  },
                                  "Pinball": {
                                    "type": "number",
                                    "nullable": true,
                                    "minimum": 0
                                  }
                                }
                              },
                              "metric_context": {
                                "$ref": "#/components/schemas/MetricContext"
                              },
                              "baselines": {
                                "type": "object",
                                "nullable": true,
                                "description": "References indexed by metric. Read floor for the numeric score, not the whole object. Fit provenance must come from the engine; a missing fit_on does not mean train.",
                                "additionalProperties": {
                                  "type": "object",
                                  "properties": {
                                    "floor": {
                                      "type": "number",
                                      "nullable": true
                                    },
                                    "class_order": {
                                      "type": "array",
                                      "nullable": true,
                                      "items": {
                                        "type": "string"
                                      }
                                    },
                                    "probabilities": {
                                      "type": "array",
                                      "nullable": true,
                                      "items": {
                                        "type": "number",
                                        "minimum": 0,
                                        "maximum": 1
                                      }
                                    },
                                    "undefined_reason": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "relative_error_reduction": {
                                      "type": "number",
                                      "nullable": true,
                                      "description": "For observed loss metrics only: 1 - model score / baseline score on identical outcomes. Fraction, possibly negative. Null for non-loss metrics, zero baseline or unavailable scores."
                                    },
                                    "reduction_undefined_reason": {
                                      "type": "string",
                                      "nullable": true
                                    },
                                    "label": {
                                      "type": "string"
                                    },
                                    "higher_better": {
                                      "type": "boolean"
                                    },
                                    "fit_on": {
                                      "type": "string",
                                      "nullable": true,
                                      "enum": [
                                        "train",
                                        "test",
                                        "theoretical",
                                        null
                                      ],
                                      "description": "Where reference parameters were determined. MAE comparison requires train."
                                    },
                                    "prediction": {
                                      "type": [
                                        "number",
                                        "string",
                                        "null"
                                      ],
                                      "description": "Constant prediction fitted on training data, or null for a probability vector or forecasting reference."
                                    },
                                    "constant_floor": {
                                      "type": "number",
                                      "nullable": true,
                                      "description": "MASE of the historical-median reference on the same evaluation points."
                                    },
                                    "constant_label": {
                                      "type": "string",
                                      "nullable": true
                                    }
                                  }
                                }
                              },
                              "assessment": {
                                "type": "object",
                                "additionalProperties": false,
                                "required": [
                                  "status",
                                  "metric",
                                  "message"
                                ],
                                "properties": {
                                  "status": {
                                    "type": "string",
                                    "description": "Display better_than_baseline as Better than the reference on this sample, alongside assessment.message and the evaluated observation count. A dataset without signal can beat a baseline by chance. Do not present this as a reliability or deployment approval.",
                                    "enum": [
                                      "better_than_baseline",
                                      "not_better_than_baseline",
                                      "inconclusive",
                                      "not_evaluated"
                                    ]
                                  },
                                  "metric": {
                                    "type": "string",
                                    "enum": [
                                      "AUC",
                                      "MAE",
                                      "MASE"
                                    ]
                                  },
                                  "message": {
                                    "type": "string"
                                  }
                                },
                                "description": "The single API conclusion, always present and non-null in HTTP 200 responses, including when the trial cannot run. Forward status, metric and message without recomputing them from scores. An absent or null assessment is a contract violation, not an inconclusive result. This is a descriptive comparison on the evaluated observations: better means strictly better than every required reference; equality is not better. No fixed gain or sample-size threshold implies significance. Inconclusive means missing or invalid evidence, or a skipped backtest; not_evaluated means validation prevented the trial. No status establishes reliability, future performance or absence of predictive signal."
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication required, or invalid token.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This caller is not allowed to perform this action.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "PERFORMANCE_UNAVAILABLE: the log (BigQuery) could not be read.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ]
      }
    },
    "/v1/models/{id}/refresh": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "Model identifier or slug"
        },
        {
          "name": "User-Agent",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 1024
          },
          "description": "Information about the agent originating the request, when known. Browsers supply their own value. Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        },
        {
          "name": "X-Spavik-Client-Name",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 128
          },
          "description": "HTTP caller: SDK, MCP server or direct application (for example spavik-sdk-python, spavik-sdk-javascript or spavik-mcp). Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        },
        {
          "name": "X-Spavik-Client-Version",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 64
          },
          "description": "Version of that SDK, MCP server or direct application. Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        }
      ],
      "post": {
        "x-audience": "client",
        "tags": [
          "Models"
        ],
        "summary": "Fold pending outcomes into the context data",
        "description": "Produces a NEW version, which serves predictions right away; the previous one stays available, so rolling back is possible. No weights change: what grows is the set of examples the model looks at.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Same workspace, method, path, key and payload prevent another execution or charge, subject to current model access. Successful responses are replayed for every data policy, including ZDR, from the technical PostgreSQL cache (default retention 24 hours from operation creation). The data policy only controls inference input/output archiving to GCS/BigQuery; it never changes replay behavior. A different key or an expired completed key starts a new operation and may be charged again. REQUEST_IN_PROGRESS: wait Retry-After. REQUEST_INTERRUPTED: outcome uncertain; contact support. IDEMPOTENCY_RESPONSE_UNAVAILABLE: a completed response is missing; do not retry with a new key. Completed keys expire; unresolved keys remain protected. Reusing a key with different input returns 422. A key is required on the original call for safe retries."
          }
        ],
        "responses": {
          "201": {
            "description": "New version",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "X-Spavik-Model-Version": {
                "schema": {
                  "type": "string",
                  "example": "presage-2"
                },
                "description": "Exact engine used to produce this result, including on an idempotent replay. Never a latest alias. Distinct from the model data version."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "model": {
                      "$ref": "#/components/schemas/Model"
                    },
                    "version": {
                      "$ref": "#/components/schemas/ModelVersion"
                    },
                    "outcomes_applied": {
                      "type": "integer"
                    },
                    "incomplete_outcomes": {
                      "type": "integer",
                      "description": "Outcomes that were missing at least one column of the model: folded in with an empty value, but they teach less."
                    },
                    "context_size": {
                      "type": "integer",
                      "nullable": true,
                      "description": "Rows kept in the context after integration. The engine samples beyond its own limit, so this number can be lower than the sum of the rows sent."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication required, or invalid token.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This caller is not allowed to perform this action.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "No pending outcomes, or an update is already running.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ]
      }
    },
    "/v1/engine-families": {
      "parameters": [
        {
          "name": "User-Agent",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 1024
          },
          "description": "Information about the agent originating the request, when known. Browsers supply their own value. Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        },
        {
          "name": "X-Spavik-Client-Name",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 128
          },
          "description": "HTTP caller: SDK, MCP server or direct application (for example spavik-sdk-python, spavik-sdk-javascript or spavik-mcp). Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        },
        {
          "name": "X-Spavik-Client-Version",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 64
          },
          "description": "Version of that SDK, MCP server or direct application. Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        }
      ],
      "get": {
        "x-audience": "client",
        "tags": [
          "Models"
        ],
        "summary": "Engine families and configured default",
        "security": [],
        "description": "The family marked is_default is used when engine_family is omitted on model creation, forecasting or backtesting. Existing models retain their original family.",
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "required": [
                      "id",
                      "label",
                      "is_default"
                    ],
                    "properties": {
                      "id": {
                        "type": "string",
                        "example": "presage"
                      },
                      "label": {
                        "type": "string",
                        "example": "Presage"
                      },
                      "is_default": {
                        "type": "boolean"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/engines": {
      "parameters": [
        {
          "name": "User-Agent",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 1024
          },
          "description": "Information about the agent originating the request, when known. Browsers supply their own value. Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        },
        {
          "name": "X-Spavik-Client-Name",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 128
          },
          "description": "HTTP caller: SDK, MCP server or direct application (for example spavik-sdk-python, spavik-sdk-javascript or spavik-mcp). Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        },
        {
          "name": "X-Spavik-Client-Version",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 64
          },
          "description": "Version of that SDK, MCP server or direct application. Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        }
      ],
      "get": {
        "x-audience": "client",
        "tags": [
          "Models"
        ],
        "summary": "Available prediction engines",
        "description": "Choose engine_family (flair, presage, prophecy) at model creation; omission selects the family marked is_default in GET /v1/engine-families. The model remains bound to that family. At prediction time, engine_version accepts a full identifier (e.g. presage-2 or presage-latest): omission or <family>-latest selects the highest active version in that family. Preview and deprecated engines require an explicit version. Retired engines are unavailable.",
        "security": [],
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "string",
                        "example": "presage-2"
                      },
                      "family": {
                        "type": "string",
                        "enum": [
                          "flair",
                          "presage",
                          "prophecy"
                        ],
                        "example": "presage"
                      },
                      "version": {
                        "type": "string",
                        "example": "2"
                      },
                      "label": {
                        "type": "string"
                      },
                      "status": {
                        "type": "string",
                        "enum": [
                          "default",
                          "preview",
                          "deprecated",
                          "retired"
                        ]
                      },
                      "description": {
                        "type": "string"
                      },
                      "tasks": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        },
                        "example": [
                          "classification",
                          "regression",
                          "forecast"
                        ]
                      },
                      "rank": {
                        "type": "integer",
                        "example": 1,
                        "description": "Place in the range, ascending from the lightest to the most capable. The list is already sorted by it: sorting by identifier would show a wrong range."
                      },
                      "credits": {
                        "type": "integer",
                        "example": 1,
                        "description": "Credits charged per scored row, or per requested horizon step for forecasting. The default engine costs 1 credit per row. Historical rows and quantiles are not billed."
                      },
                      "min_plan": {
                        "type": "string",
                        "nullable": true,
                        "example": null,
                        "description": "Lowest plan that may declare this engine. Null when open to every plan, Free included."
                      },
                      "retires_at": {
                        "type": "string",
                        "format": "date",
                        "nullable": true
                      }
                    }
                  }
                }
              }
            }
          },
          "503": {
            "description": "ENGINE_UNAVAILABLE: the engine did not respond within the metadata timeout, could not be reached, or returned a server error. ENGINE_BUSY: engine capacity is temporarily exhausted. ENGINE_CATALOGUE_MISMATCH: an offered engine is missing from the engine catalogue. Try again shortly.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/usage": {
      "parameters": [
        {
          "name": "User-Agent",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 1024
          },
          "description": "Information about the agent originating the request, when known. Browsers supply their own value. Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        },
        {
          "name": "X-Spavik-Client-Name",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 128
          },
          "description": "HTTP caller: SDK, MCP server or direct application (for example spavik-sdk-python, spavik-sdk-javascript or spavik-mcp). Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        },
        {
          "name": "X-Spavik-Client-Version",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 64
          },
          "description": "Version of that SDK, MCP server or direct application. Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        }
      ],
      "get": {
        "x-audience": "client",
        "tags": [
          "Usage and billing"
        ],
        "summary": "Account usage",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "workspace_id",
            "in": "query",
            "schema": {
              "type": "string",
              "example": "ws_3v8QpZ2mKdRt7Lc9Xw"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Usage"
                }
              }
            }
          },
          "401": {
            "description": "Authentication required, or invalid token.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This caller is not allowed to perform this action.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "USAGE_UNAVAILABLE: the usage log (BigQuery) could not be read.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ]
      }
    },
    "/v1/requests": {
      "parameters": [
        {
          "name": "User-Agent",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 1024
          },
          "description": "Information about the agent originating the request, when known. Browsers supply their own value. Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        },
        {
          "name": "X-Spavik-Client-Name",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 128
          },
          "description": "HTTP caller: SDK, MCP server or direct application (for example spavik-sdk-python, spavik-sdk-javascript or spavik-mcp). Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        },
        {
          "name": "X-Spavik-Client-Version",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 64
          },
          "description": "Version of that SDK, MCP server or direct application. Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        }
      ],
      "get": {
        "x-audience": "client",
        "tags": [
          "Usage and billing"
        ],
        "summary": "Request log (successful and failed calls)",
        "description": "Calls made by the active workspace, most recent first. `status=error` keeps only the failures: enough to look up a rejection that happened in production instead of reproducing it. Default window: 7 days (30 at most). Includes the billed account, authenticated actor, resolved IP, User-Agent and optional caller name and version (SDK, MCP server or direct application). Absent metadata and historical fields are null. Only calls with a resolved workspace identity enter this journal; administration, internal calls, provider webhooks and journal reads are excluded.",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "all",
                "error"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RequestLog"
                }
              }
            }
          },
          "401": {
            "description": "Authentication required, or invalid token.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "This caller is not allowed to perform this action.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "REQUEST_LOG_UNAVAILABLE: the request log (Elasticsearch) could not be read.",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              },
              "Retry-After": {
                "schema": {
                  "type": "integer",
                  "minimum": 1
                },
                "description": "Optional suggested delay in seconds. See x-retry-policies by error code; this header alone does not authorize replaying a mutation without its original idempotency key."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "security": [
          {
            "apiKey": []
          }
        ]
      }
    },
    "/v1/plans": {
      "parameters": [
        {
          "name": "User-Agent",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 1024
          },
          "description": "Information about the agent originating the request, when known. Browsers supply their own value. Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        },
        {
          "name": "X-Spavik-Client-Name",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 128
          },
          "description": "HTTP caller: SDK, MCP server or direct application (for example spavik-sdk-python, spavik-sdk-javascript or spavik-mcp). Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        },
        {
          "name": "X-Spavik-Client-Version",
          "in": "header",
          "required": false,
          "schema": {
            "type": "string",
            "maxLength": 64
          },
          "description": "Version of that SDK, MCP server or direct application. Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
        }
      ],
      "get": {
        "x-audience": "client",
        "tags": [
          "Usage and billing"
        ],
        "summary": "Public pricing",
        "security": [],
        "responses": {
          "200": {
            "description": "Success",
            "headers": {
              "X-Spavik-Request-Id": {
                "schema": {
                  "type": "string",
                  "example": "req_..."
                },
                "description": "HTTP request identifier for correlation with logs. Generated for this call or taken from its X-Spavik-Request-Id header; never restored from a stored idempotent result."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Plan"
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "webhooks": {
    "event": {
      "post": {
        "x-audience": "client",
        "description": "POST application/json. Spavik-Signature: t=<Unix seconds>,v1=<hex HMAC-SHA256>. Sign the timestamp, a dot, and the exact raw request bytes with the webhook secret as a UTF-8 string. Use constant-time comparison and reject timestamps more than 300 seconds away from your clock. Each attempt has a fresh signature; event.id and payload remain unchanged. Persist/deduplicate event.id before acknowledging with any 2xx. Duplicate deliveries and unordered events are possible. Default timeout: 5 seconds. Default maximum: 8 attempts; exponential delays from 30 seconds, capped at 1 hour. Network errors, timeouts, HTTP 408, 429 and 5xx are retried. Other non-2xx responses are terminal; redirects are not followed. Retry-After from the receiver is honored up to 24 hours. Scheduling may add delay. These limits are deployment-configurable. Disabling or changing a webhook URL cancels its pending deliveries when processed; deleting it removes their operational records. Delivery history is available under /v1/webhooks/{id}/deliveries. Elasticsearch visibility is eventual; attempts_complete indicates whether all attempts are visible.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "type": "object",
                    "required": [
                      "id",
                      "type",
                      "created_at",
                      "workspace_id",
                      "data"
                    ],
                    "properties": {
                      "id": {
                        "type": "string",
                        "description": "Stable event identifier. Deduplicate by this id for each receiver."
                      },
                      "type": {
                        "type": "string",
                        "enum": [
                          "model.trained"
                        ]
                      },
                      "created_at": {
                        "type": "string",
                        "format": "date-time"
                      },
                      "workspace_id": {
                        "type": "string"
                      },
                      "data": {
                        "type": "object",
                        "required": [
                          "model_id",
                          "version",
                          "target",
                          "task",
                          "n_train",
                          "metrics"
                        ],
                        "properties": {
                          "model_id": {
                            "type": "string"
                          },
                          "version": {
                            "type": "integer"
                          },
                          "target": {
                            "type": "string"
                          },
                          "task": {
                            "type": "string",
                            "enum": [
                              "classification",
                              "regression"
                            ]
                          },
                          "n_train": {
                            "type": "integer"
                          },
                          "metrics": {
                            "type": "object",
                            "nullable": true,
                            "additionalProperties": true
                          }
                        }
                      }
                    },
                    "example": {
                      "id": "evt_example",
                      "type": "model.trained",
                      "created_at": "2026-10-11T12:00:00.000Z",
                      "workspace_id": "ws_example",
                      "data": {
                        "model_id": "mdl_example",
                        "version": 1,
                        "target": "churn",
                        "task": "classification",
                        "n_train": 100,
                        "metrics": {
                          "AUC": 0.81
                        }
                      }
                    }
                  },
                  {
                    "type": "object",
                    "required": [
                      "id",
                      "type",
                      "created_at",
                      "workspace_id",
                      "data"
                    ],
                    "properties": {
                      "id": {
                        "type": "string",
                        "description": "Stable event identifier. Deduplicate by this id for each receiver."
                      },
                      "type": {
                        "type": "string",
                        "enum": [
                          "model.refreshed"
                        ]
                      },
                      "created_at": {
                        "type": "string",
                        "format": "date-time"
                      },
                      "workspace_id": {
                        "type": "string"
                      },
                      "data": {
                        "type": "object",
                        "required": [
                          "model_id",
                          "version",
                          "outcomes_applied"
                        ],
                        "properties": {
                          "model_id": {
                            "type": "string"
                          },
                          "version": {
                            "type": "integer"
                          },
                          "outcomes_applied": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "example": {
                      "id": "evt_example",
                      "type": "model.refreshed",
                      "created_at": "2026-10-11T12:00:00.000Z",
                      "workspace_id": "ws_example",
                      "data": {
                        "model_id": "mdl_example",
                        "version": 2,
                        "outcomes_applied": 12
                      }
                    }
                  },
                  {
                    "type": "object",
                    "required": [
                      "id",
                      "type",
                      "created_at",
                      "workspace_id",
                      "data"
                    ],
                    "properties": {
                      "id": {
                        "type": "string",
                        "description": "Stable event identifier. Deduplicate by this id for each receiver."
                      },
                      "type": {
                        "type": "string",
                        "enum": [
                          "model.stale"
                        ]
                      },
                      "created_at": {
                        "type": "string",
                        "format": "date-time"
                      },
                      "workspace_id": {
                        "type": "string"
                      },
                      "data": {
                        "type": "object",
                        "required": [
                          "model_id",
                          "pending_outcomes",
                          "hint"
                        ],
                        "properties": {
                          "model_id": {
                            "type": "string"
                          },
                          "pending_outcomes": {
                            "type": "integer"
                          },
                          "hint": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "example": {
                      "id": "evt_example",
                      "type": "model.stale",
                      "created_at": "2026-10-11T12:00:00.000Z",
                      "workspace_id": "ws_example",
                      "data": {
                        "model_id": "mdl_example",
                        "pending_outcomes": 50,
                        "hint": "Refresh the model to integrate these outcomes."
                      }
                    }
                  },
                  {
                    "type": "object",
                    "required": [
                      "id",
                      "type",
                      "created_at",
                      "workspace_id",
                      "data"
                    ],
                    "properties": {
                      "id": {
                        "type": "string",
                        "description": "Stable event identifier. Deduplicate by this id for each receiver."
                      },
                      "type": {
                        "type": "string",
                        "enum": [
                          "credits.low"
                        ]
                      },
                      "created_at": {
                        "type": "string",
                        "format": "date-time"
                      },
                      "workspace_id": {
                        "type": "string"
                      },
                      "data": {
                        "type": "object",
                        "required": [
                          "credit_balance"
                        ],
                        "properties": {
                          "credit_balance": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "example": {
                      "id": "evt_example",
                      "type": "credits.low",
                      "created_at": "2026-10-11T12:00:00.000Z",
                      "workspace_id": "ws_example",
                      "data": {
                        "credit_balance": 10
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Event durably accepted or already processed."
          }
        }
      }
    }
  },
  "x-retry-policies": {
    "TOO_MANY_REQUESTS": {
      "after_seconds": null,
      "action": "Wait for Retry-After. Keep the same Idempotency-Key and payload if one was supplied."
    },
    "REQUEST_IN_PROGRESS": {
      "after_seconds": 2,
      "action": "Retry with the same Idempotency-Key and payload after Retry-After."
    },
    "ENGINE_BUSY": {
      "after_seconds": 5,
      "action": "For predictions and forecasts, wait for Retry-After and keep the original Idempotency-Key if supplied. Model creation or refresh may have completed: retry only with the original key and payload; REQUEST_INTERRUPTED requires inspection. Without an original key, inspect model mutations before retrying."
    },
    "ENGINE_UNAVAILABLE": {
      "after_seconds": 5,
      "action": "The engine outcome may be unknown. Retry only with the original Idempotency-Key and payload; otherwise inspect the operation before retrying."
    },
    "USAGE_ACCOUNTING_UNAVAILABLE": {
      "after_seconds": 5,
      "action": "Retry only with the original Idempotency-Key and payload; otherwise contact support before retrying."
    },
    "RATE_LIMIT_UNAVAILABLE": {
      "after_seconds": 5,
      "action": "Retry after Retry-After; keep the same Idempotency-Key if supplied."
    },
    "REQUEST_LOG_UNAVAILABLE": {
      "after_seconds": 5,
      "action": "Retry this GET after Retry-After."
    },
    "REQUEST_INTERRUPTED": {
      "after_seconds": null,
      "action": "The outcome is uncertain. Do not switch to a new key or retry automatically. Inspect the request log and contact support with x-spavik-request-id."
    },
    "IDEMPOTENCY_RESPONSE_UNAVAILABLE": {
      "after_seconds": null,
      "action": "The response could not be saved. Do not switch to a new key or retry automatically. Contact support with x-spavik-request-id."
    },
    "IDEMPOTENCY_KEY_REUSED": {
      "after_seconds": null,
      "action": "Use the original payload for a retry. Use a new key only for a deliberately new operation."
    },
    "CREDITS_EXHAUSTED": {
      "after_seconds": null,
      "action": "Buy credits or change plan before retrying."
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Spavik-Api-Key",
        "description": "Spavik integration API key (sk-spv-api-...), sent in X-Spavik-Api-Key."
      }
    },
    "schemas": {
      "MetricContext": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "undefined_reasons"
        ],
        "properties": {
          "undefined_reasons": {
            "type": "object",
            "additionalProperties": {
              "type": "string",
              "nullable": true
            },
            "description": "One reason per undefined score; null when the score is defined. Missing probabilities are never reconstructed from hard labels."
          },
          "class_order": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "positive_class": {
            "type": "string",
            "nullable": true
          },
          "auc_average": {
            "type": "string",
            "enum": [
              "binary",
              "macro_ovr"
            ]
          },
          "average_precision_method": {
            "type": "string",
            "enum": [
              "non_interpolated"
            ]
          },
          "macro_f1_average": {
            "type": "string",
            "enum": [
              "unweighted_defined_classes"
            ]
          },
          "log_loss_epsilon": {
            "type": "number",
            "example": 2.220446049250313e-16
          }
        }
      },
      "IntervalMetrics": {
        "type": "object",
        "nullable": true,
        "additionalProperties": false,
        "description": "Coverage and width on the same backtest observations, using the widest available quantile pair. Fractions use 0 to 1. Width is in target units, never an implicit percentage. Descriptive measurements do not establish calibration.",
        "required": [
          "nominal",
          "observed",
          "lower",
          "upper",
          "evaluated_count",
          "average_width",
          "width_unit",
          "undefined_reason"
        ],
        "properties": {
          "nominal": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          },
          "observed": {
            "type": "number",
            "nullable": true,
            "minimum": 0,
            "maximum": 1
          },
          "lower": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          },
          "upper": {
            "type": "number",
            "minimum": 0,
            "maximum": 1
          },
          "evaluated_count": {
            "type": "integer",
            "minimum": 0
          },
          "average_width": {
            "type": "number",
            "nullable": true,
            "minimum": 0
          },
          "width_unit": {
            "type": "string",
            "enum": [
              "target"
            ]
          },
          "undefined_reason": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "BacktestPredictions": {
        "type": [
          "array",
          "null"
        ],
        "items": {
          "$ref": "#/components/schemas/BacktestPrediction"
        },
        "description": "Historical observations already evaluated; null unless include_backtest_predictions=true and a backtest ran. One row per n_eval point, ordered oldest window first, then series and timestamp. For MASE, also use evaluation.series[].mase_scale and equal weighting per eligible series. These are not future forecasts."
      },
      "BacktestPrediction": {
        "type": "object",
        "required": [
          "series",
          "window",
          "timestamp",
          "actual",
          "prediction",
          "naive_prediction",
          "constant_prediction",
          "quantiles"
        ],
        "properties": {
          "series": {
            "type": "string",
            "description": "Original series identifier, or series when no item_id was supplied."
          },
          "window": {
            "type": "integer",
            "minimum": 0,
            "description": "0 is the most recent window. (series, window) identifies the matching evaluation.windows entry."
          },
          "timestamp": {
            "type": "string",
            "description": "ISO timestamp in UTC without a timezone suffix."
          },
          "actual": {
            "type": "number"
          },
          "prediction": {
            "type": "number",
            "description": "Point estimate selected by point_estimate."
          },
          "naive_prediction": {
            "type": "number"
          },
          "constant_prediction": {
            "type": "number",
            "description": "Median of the history before this window."
          },
          "quantiles": {
            "type": "object",
            "additionalProperties": {
              "type": "number"
            },
            "description": "Quantile level to prediction, e.g. 0.1 and 0.9."
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "example": "error"
          },
          "message": {
            "type": "string",
            "enum": [
              "ACCOUNT_NOT_FOUND",
              "ADMIN_KEY_CANNOT_PREDICT",
              "API_KEY_EXPIRED",
              "API_KEY_INVALID",
              "AUTH_REQUIRED",
              "BAD_REQUEST",
              "COLUMN_MISMATCH",
              "CONTEXT_UNAVAILABLE",
              "CREDITS_EXHAUSTED",
              "CSV_UNREADABLE",
              "DATASET_TOO_LARGE",
              "DATA_KEK_INVALID",
              "DATA_KEK_MISSING",
              "DATA_KEY_MALFORMED",
              "DATA_KEY_MISMATCH",
              "DATA_KEY_NOT_SENT",
              "DATA_KEY_UNREADABLE",
              "DATA_KEY_VERSION_UNKNOWN",
              "DATA_REQUIRED",
              "ENGINE_BUSY",
              "ENGINE_CATALOGUE_MISMATCH",
              "ENGINE_ERROR",
              "ENGINE_FAMILY_MISMATCH",
              "ENGINE_FAMILY_ONLY",
              "ENGINE_FAMILY_REQUIRED",
              "ENGINE_MISMATCH",
              "ENGINE_NO_MODEL",
              "ENGINE_PLAN_REQUIRED",
              "ENGINE_RETIRED",
              "ENGINE_UNAVAILABLE",
              "FILE_TOO_LARGE",
              "FORECAST_FAILED",
              "HORIZON_REQUIRED",
              "IDEMPOTENCY_KEY_REUSED",
              "IDEMPOTENCY_KEY_TOO_LONG",
              "IDEMPOTENCY_RESPONSE_UNAVAILABLE",
              "INPUT_REQUIRED",
              "INTERNAL_ERROR",
              "INVALID_DATE",
              "INVALID_ENGINE_VERSION",
              "INVALID_INPUT",
              "INVALID_RANGE",
              "MALFORMED_PATH",
              "MODEL_ARCHIVED",
              "MODEL_DELETED",
              "MODEL_KEY_MISSING",
              "MODEL_NOT_FOUND",
              "MODEL_NOT_REFRESHABLE",
              "MODEL_NOT_TRAINED",
              "NO_PENDING_OUTCOMES",
              "NO_ROWS",
              "OUTCOME_PREDICTION_INVALID",
              "PERFORMANCE_UNAVAILABLE",
              "QUOTA_MODELS_EXCEEDED",
              "RANGE_TOO_WIDE",
              "RATE_LIMIT_UNAVAILABLE",
              "REFRESH_IN_PROGRESS",
              "REQUEST_INTERRUPTED",
              "REQUEST_IN_PROGRESS",
              "REQUEST_LOG_UNAVAILABLE",
              "ROUTE_NOT_FOUND",
              "SCHEMA_MISMATCH",
              "TARGET_COLUMN_MISSING",
              "TARGET_REQUIRED",
              "TARGET_TASK_FROZEN",
              "TIMESTAMP_COLUMN_MISSING",
              "TIMESTAMP_COLUMN_NOT_FOUND",
              "TOO_MANY_REQUESTS",
              "TOO_MANY_ROWS",
              "UNKNOWN_ENGINE_VERSION",
              "UNKNOWN_TASK",
              "UPLOAD_INVALID",
              "USAGE_ACCOUNTING_UNAVAILABLE",
              "USAGE_UNAVAILABLE",
              "VALIDATION_ERROR",
              "VALIDATION_FAILED",
              "WORKSPACE_NOT_FOUND"
            ],
            "example": "QUOTA_MODELS_EXCEEDED",
            "description": "Machine-readable code (UPPER_SNAKE), stable: this is what a client branches on."
          },
          "messageDetail": {
            "type": "string",
            "example": "Model limit reached (3/3) on the build plan. Archive a model you no longer serve, or upgrade to the pro plan.",
            "description": "The whole story, for a human: what was refused and what to do about it."
          }
        },
        "required": [
          "status",
          "message",
          "messageDetail"
        ]
      },
      "ForecastResult": {
        "type": "object",
        "properties": {
          "forecast_csv": {
            "type": "string"
          },
          "target": {
            "type": "string",
            "nullable": true
          },
          "prediction_length": {
            "type": "integer",
            "nullable": true
          },
          "n_series": {
            "type": "integer",
            "nullable": true
          },
          "n_context": {
            "type": "integer",
            "nullable": true
          },
          "point_estimate": {
            "type": "string",
            "nullable": true,
            "enum": [
              "mean",
              "median"
            ],
            "description": "Statistic served in the <target>_prediction column. With median it matches quantile_0.5 exactly; with mean it differs on a skewed distribution."
          },
          "metrics": {
            "type": "object",
            "nullable": true,
            "additionalProperties": {
              "type": "number",
              "nullable": true
            },
            "description": "Scores on evaluated observations. Classification: AUC, Accuracy, AveragePrecision (binary only), LogLoss and MacroF1. Regression: MAE, RMSE, R². Forecasting adds MASE, Bias, BiasRatio and Pinball. Undefined scores are null; see metric_context.undefined_reasons. Secondary scores do not change the primary assessment.",
            "properties": {
              "AUC": {
                "type": "number",
                "nullable": true,
                "minimum": 0,
                "maximum": 1,
                "description": "ROC AUC, binary positive class or multiclass one-vs-rest macro average."
              },
              "Accuracy": {
                "type": "number",
                "nullable": true,
                "minimum": 0,
                "maximum": 1
              },
              "AveragePrecision": {
                "type": "number",
                "nullable": true,
                "minimum": 0,
                "maximum": 1,
                "description": "Binary non-interpolated average precision, not trapezoidal PR-AUC. Null without positive observations."
              },
              "LogLoss": {
                "type": "number",
                "nullable": true,
                "minimum": 0,
                "description": "Mean negative log probability of the actual class, natural logarithm, float64 epsilon clipping. Lower is better."
              },
              "MacroF1": {
                "type": "number",
                "nullable": true,
                "minimum": 0,
                "maximum": 1,
                "description": "Unweighted mean of defined per-class F1 scores. Classes absent from both actual and predicted labels are excluded."
              },
              "MAE": {
                "type": "number",
                "nullable": true,
                "minimum": 0,
                "description": "Mean absolute error in target units."
              },
              "RMSE": {
                "type": "number",
                "nullable": true,
                "minimum": 0
              },
              "R²": {
                "type": "number",
                "nullable": true,
                "description": "Null for fewer than two observations or constant actual values. Can be negative."
              },
              "MASE": {
                "type": "number",
                "nullable": true,
                "minimum": 0,
                "description": "Compare with the scored naive baseline on the same backtest, not with a fixed threshold of 1."
              },
              "Bias": {
                "type": "number",
                "nullable": true
              },
              "BiasRatio": {
                "type": "number",
                "nullable": true
              },
              "Pinball": {
                "type": "number",
                "nullable": true,
                "minimum": 0
              }
            }
          },
          "baselines": {
            "type": "object",
            "additionalProperties": true,
            "nullable": true,
            "description": "Naive baselines (seasonal when selected from the initial training context) measured on the same evaluated points as metrics. MASE also includes constant_floor and constant_label for the historical median estimated before each window. Model, naive and constant share the same scale computed before the earliest evaluation window. Compare MASE to these scores, never to the fixed threshold of 1."
          },
          "seasonal_period": {
            "type": "integer",
            "nullable": true
          },
          "backtest": {
            "type": "object",
            "additionalProperties": true,
            "nullable": true,
            "description": "Score per origin: mase_by_window gives the MASE of each window, naive_mase_by_window and constant_mase_by_window the matching baseline scores. Window indices are relative to each series end and may represent different dates. Protocol v2 MASE weights eligible series equally, then points within each series. Other metrics weight points equally. Do not reconstruct the aggregate by averaging window scores.",
            "properties": {
              "predictions": {
                "$ref": "#/components/schemas/BacktestPredictions"
              }
            }
          },
          "backtest_skipped": {
            "type": "string",
            "nullable": true,
            "description": "Set when a requested backtest could not run (series too short)."
          },
          "intervals": {
            "$ref": "#/components/schemas/IntervalMetrics"
          },
          "evaluation": {
            "type": "object",
            "nullable": true,
            "additionalProperties": true,
            "properties": {
              "metric_context": {
                "$ref": "#/components/schemas/MetricContext"
              }
            }
          },
          "warnings": {
            "type": "array",
            "nullable": true,
            "items": {
              "type": "string"
            }
          },
          "engine": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "ClassificationReport": {
        "type": [
          "object",
          "null"
        ],
        "additionalProperties": false,
        "description": "Per-class scores on actual predicted labels. Null for regression, older engines or unavailable evaluation. At training these describe the holdout; an augmented version retains its initial evaluation. In performance these describe only predictions matched to received outcomes, not the entire prediction population. All classes known to the evaluated versions and all observed/predicted labels are included. The AUC assessment is unchanged.",
        "required": [
          "evaluated_count",
          "decision_rule",
          "per_class"
        ],
        "properties": {
          "evaluated_count": {
            "type": "integer",
            "minimum": 0,
            "description": "Number of rows evaluated. Each row contributes once to support and once to predicted_count."
          },
          "decision_rule": {
            "$ref": "#/components/schemas/ClassificationDecisionRule"
          },
          "per_class": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ClassificationClassMetrics"
            }
          }
        }
      },
      "ClassificationDecisionRule": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "kind",
          "description",
          "class_order",
          "threshold",
          "tie_break"
        ],
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "argmax",
              "model_predict",
              "recorded_prediction"
            ]
          },
          "description": {
            "type": "string",
            "description": "Actual decision rule. TabICL uses the largest class probability, first class in class_order on ties, without a custom threshold. model_predict means estimator details were unavailable. recorded_prediction uses the class originally announced; no threshold is reapplied to historical outcomes."
          },
          "class_order": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Probability column order when known; empty for reports combining recorded decisions."
          },
          "threshold": {
            "type": [
              "number",
              "null"
            ],
            "description": "Custom probability threshold if used; null for the current argmax rule or unavailable historical rule."
          },
          "tie_break": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "first_in_class_order",
              null
            ]
          }
        }
      },
      "ClassificationClassMetrics": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "class",
          "support",
          "predicted_count",
          "true_positive",
          "precision",
          "recall",
          "f1",
          "undefined_reasons"
        ],
        "properties": {
          "class": {
            "type": "string",
            "description": "Class label serialized as a string."
          },
          "support": {
            "type": "integer",
            "minimum": 0,
            "description": "Actual occurrences of this class in the evaluated rows."
          },
          "predicted_count": {
            "type": "integer",
            "minimum": 0,
            "description": "Evaluated rows predicted as this class."
          },
          "true_positive": {
            "type": "integer",
            "minimum": 0,
            "description": "Rows where the actual and predicted labels both equal this class."
          },
          "precision": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 1,
            "description": "true_positive / predicted_count; null when predicted_count is zero."
          },
          "recall": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 1,
            "description": "true_positive / support; null when support is zero."
          },
          "f1": {
            "type": [
              "number",
              "null"
            ],
            "minimum": 0,
            "maximum": 1,
            "description": "2 * true_positive / (support + predicted_count); null only if both counts are zero. A present but never predicted class has F1 zero."
          },
          "undefined_reasons": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "precision",
              "recall",
              "f1"
            ],
            "description": "Null reason means the corresponding score is defined, including a valid zero.",
            "properties": {
              "precision": {
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "no_predicted_samples",
                  null
                ]
              },
              "recall": {
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "no_actual_samples",
                  null
                ]
              },
              "f1": {
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "no_actual_or_predicted_samples",
                  null
                ]
              }
            }
          }
        }
      },
      "Calibration": {
        "type": [
          "object",
          "null"
        ],
        "description": "Descriptive calibration on held-out test rows, before the final persistence fit. Does not recalibrate probabilities. Null for regression or missing historical diagnostics. Retained as the initial evaluation on same-engine refreshes; cleared when the engine changes.",
        "required": [
          "available",
          "unavailable_reason",
          "evaluated_count",
          "evaluated_on",
          "kind",
          "interpretation",
          "binning",
          "n_bins",
          "bin_boundaries",
          "per_class"
        ],
        "properties": {
          "available": {
            "type": "boolean",
            "description": "Whether the diagnostic can be computed, not whether probabilities are reliable."
          },
          "unavailable_reason": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "no_evaluated_samples",
              "insufficient_classes",
              "missing_probabilities",
              "invalid_probabilities",
              "unknown_actual_class",
              null
            ]
          },
          "evaluated_count": {
            "type": "integer",
            "minimum": 0
          },
          "evaluated_on": {
            "type": "string",
            "enum": [
              "test"
            ]
          },
          "kind": {
            "type": "string",
            "enum": [
              "one_vs_rest"
            ]
          },
          "interpretation": {
            "type": "string",
            "enum": [
              "descriptive_only"
            ]
          },
          "binning": {
            "type": "string",
            "enum": [
              "uniform"
            ]
          },
          "n_bins": {
            "type": "integer",
            "enum": [
              10
            ]
          },
          "bin_boundaries": {
            "type": "string",
            "enum": [
              "left_closed_right_open_last_closed"
            ]
          },
          "per_class": {
            "type": "array",
            "description": "Model class order. Each class uses every test row. Empty when unavailable.",
            "items": {
              "type": "object",
              "required": [
                "class",
                "bins"
              ],
              "properties": {
                "class": {
                  "type": "string"
                },
                "bins": {
                  "type": "array",
                  "minItems": 10,
                  "maxItems": 10,
                  "items": {
                    "type": "object",
                    "required": [
                      "lower",
                      "upper",
                      "count",
                      "positive_count",
                      "mean_predicted_probability",
                      "observed_frequency"
                    ],
                    "properties": {
                      "lower": {
                        "type": "number",
                        "minimum": 0,
                        "maximum": 1
                      },
                      "upper": {
                        "type": "number",
                        "minimum": 0,
                        "maximum": 1
                      },
                      "count": {
                        "type": "integer",
                        "minimum": 0
                      },
                      "positive_count": {
                        "type": "integer",
                        "minimum": 0
                      },
                      "mean_predicted_probability": {
                        "type": [
                          "number",
                          "null"
                        ],
                        "minimum": 0,
                        "maximum": 1,
                        "description": "Null for an empty bin."
                      },
                      "observed_frequency": {
                        "type": [
                          "number",
                          "null"
                        ],
                        "minimum": 0,
                        "maximum": 1,
                        "description": "positive_count / count; null for an empty bin."
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "Model": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "mdl_7Kq2Vn5RtWz3Bx8Ldm"
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string",
            "nullable": true
          },
          "engine_family": {
            "type": "string",
            "enum": [
              "flair",
              "presage",
              "prophecy"
            ]
          },
          "target": {
            "type": "string",
            "nullable": true
          },
          "task": {
            "type": "string",
            "enum": [
              "classification",
              "regression"
            ],
            "nullable": true
          },
          "engine": {
            "type": "string",
            "nullable": true,
            "description": "Engine that prepared and evaluated this data version; not a prediction engine lock."
          },
          "feature_columns": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "nullable": true,
            "description": "Columns the served version expects in each row sent to predict. Null until the first training."
          },
          "status": {
            "type": "string"
          },
          "auto_update": {
            "type": "boolean",
            "description": "Automatically folds outcomes back into the model."
          },
          "retain_outcomes": {
            "type": "boolean",
            "deprecated": true,
            "description": "Deprecated compatibility field, always true. Outcomes are functional model data and require no retention opt-in."
          },
          "latest_version": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Pagination": {
        "type": "object",
        "properties": {
          "total": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          },
          "has_more": {
            "type": "boolean"
          }
        }
      },
      "TrainingResult": {
        "type": "object",
        "description": "The model created, the version produced (n_train, metrics, warnings) and the data validation report. The version is not repeated inside the model.",
        "properties": {
          "model": {
            "$ref": "#/components/schemas/Model"
          },
          "version": {
            "$ref": "#/components/schemas/ModelVersion"
          },
          "validation": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "ModelVersion": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "ver_2Hj9PxLm4Nc6Tq8Rvb"
          },
          "version": {
            "type": "integer"
          },
          "status": {
            "type": "string"
          },
          "target": {
            "type": "string"
          },
          "task": {
            "type": "string"
          },
          "n_train": {
            "type": "integer",
            "nullable": true
          },
          "n_sampled_from": {
            "type": "integer",
            "nullable": true,
            "description": "Usable rows in the uploaded file. Higher than n_train, it reveals sub-sampling (max_samples, or engine memory)."
          },
          "metrics": {
            "type": "object",
            "nullable": true,
            "additionalProperties": {
              "type": "number",
              "nullable": true
            },
            "description": "Scores on evaluated observations. Classification: AUC, Accuracy, AveragePrecision (binary only), LogLoss and MacroF1. Regression: MAE, RMSE, R². Forecasting adds MASE, Bias, BiasRatio and Pinball. Undefined scores are null; see metric_context.undefined_reasons. Secondary scores do not change the primary assessment.",
            "properties": {
              "AUC": {
                "type": "number",
                "nullable": true,
                "minimum": 0,
                "maximum": 1,
                "description": "ROC AUC, binary positive class or multiclass one-vs-rest macro average."
              },
              "Accuracy": {
                "type": "number",
                "nullable": true,
                "minimum": 0,
                "maximum": 1
              },
              "AveragePrecision": {
                "type": "number",
                "nullable": true,
                "minimum": 0,
                "maximum": 1,
                "description": "Binary non-interpolated average precision, not trapezoidal PR-AUC. Null without positive observations."
              },
              "LogLoss": {
                "type": "number",
                "nullable": true,
                "minimum": 0,
                "description": "Mean negative log probability of the actual class, natural logarithm, float64 epsilon clipping. Lower is better."
              },
              "MacroF1": {
                "type": "number",
                "nullable": true,
                "minimum": 0,
                "maximum": 1,
                "description": "Unweighted mean of defined per-class F1 scores. Classes absent from both actual and predicted labels are excluded."
              },
              "MAE": {
                "type": "number",
                "nullable": true,
                "minimum": 0,
                "description": "Mean absolute error in target units."
              },
              "RMSE": {
                "type": "number",
                "nullable": true,
                "minimum": 0
              },
              "R²": {
                "type": "number",
                "nullable": true,
                "description": "Null for fewer than two observations or constant actual values. Can be negative."
              },
              "MASE": {
                "type": "number",
                "nullable": true,
                "minimum": 0,
                "description": "Compare with the scored naive baseline on the same backtest, not with a fixed threshold of 1."
              },
              "Bias": {
                "type": "number",
                "nullable": true
              },
              "BiasRatio": {
                "type": "number",
                "nullable": true
              },
              "Pinball": {
                "type": "number",
                "nullable": true,
                "minimum": 0
              }
            }
          },
          "baselines": {
            "type": "object",
            "additionalProperties": true,
            "nullable": true
          },
          "classification_report": {
            "$ref": "#/components/schemas/ClassificationReport"
          },
          "calibration": {
            "$ref": "#/components/schemas/Calibration"
          },
          "evaluation": {
            "type": "object",
            "nullable": true,
            "additionalProperties": true,
            "description": "Initial holdout protocol, split and sample context. Retained across context refreshes; not a new evaluation.",
            "properties": {
              "metric_context": {
                "$ref": "#/components/schemas/MetricContext"
              }
            }
          },
          "warnings": {
            "type": "array",
            "nullable": true,
            "items": {
              "type": "string"
            },
            "description": "What the engine agreed to do but flagged: context trimmed to fit in memory, classes or columns outside the optimised range. Worth reading when a model disappoints and nothing else explains it."
          },
          "top_factors": {
            "type": "array",
            "nullable": true,
            "items": {
              "type": "object",
              "additionalProperties": true
            },
            "description": "Most influential features, if training asked for them."
          },
          "engine": {
            "type": "string",
            "nullable": true,
            "description": "Engine that prepared this context and produced its evaluation (e.g. presage-2). Predictions may select another available version in the same family."
          }
        }
      },
      "ScoringResult": {
        "type": "object",
        "properties": {
          "model_id": {
            "type": "string",
            "example": "mdl_7Kq2Vn5RtWz3Bx8Ldm"
          },
          "version": {
            "type": "integer"
          },
          "engine": {
            "type": "string",
            "example": "presage-2",
            "description": "Exact engine used for this prediction."
          },
          "n_scored": {
            "type": "integer"
          },
          "took_ms": {
            "type": "integer",
            "description": "Total time on the Spavik side."
          },
          "predictions": {
            "type": "array",
            "description": "One entry per row sent, in the same order. `prediction` is a number for regression, and for classification the predicted class (a number when the classes are numeric, text otherwise). Classification also returns one probability per class (`proba_<class>`).",
            "items": {
              "type": "object",
              "properties": {
                "request_id": {
                  "type": "string",
                  "example": "req_5Zt8Wn3QmXd7Bp2Kfh",
                  "description": "Prediction identifier to send with outcomes. Distinct from the HTTP x-spavik-request-id header."
                },
                "prediction": {
                  "oneOf": [
                    {
                      "type": "number"
                    },
                    {
                      "type": "string"
                    }
                  ],
                  "example": 1
                }
              },
              "additionalProperties": {
                "type": "number",
                "description": "proba_<class>"
              },
              "example": {
                "request_id": "req_5Zt8Wn3QmXd7Bp2Kfh",
                "prediction": 1,
                "proba_0": 0.2,
                "proba_1": 0.8
              }
            }
          }
        }
      },
      "Usage": {
        "type": "object",
        "properties": {
          "plan": {
            "type": "string",
            "enum": [
              "free",
              "build",
              "pro",
              "growth",
              "enterprise"
            ]
          },
          "credit_balance": {
            "type": "integer",
            "description": "Prediction balance: granted on sign-up, granted by staff, purchased and refunded, as a single number. Spent after the monthly allowance, and never expires."
          },
          "credits_low": {
            "type": "boolean"
          },
          "predictions_remaining": {
            "type": "integer",
            "nullable": true,
            "description": "What can still be consumed: monthly allowance left plus balance. The same number as the X-Spavik-Predictions-Remaining header on billable responses. Null on an unlimited plan."
          },
          "unlimited_usage": {
            "type": "boolean",
            "description": "True only when unlimited usage is explicitly configured. An absent balance alone does not imply unlimited usage."
          },
          "included_units_per_month": {
            "type": "integer",
            "nullable": true
          },
          "period": {
            "type": "object",
            "properties": {
              "key": {
                "type": "string",
                "example": "2026-09"
              },
              "reset_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "units_used": {
                "type": "integer",
                "description": "Monthly allowance already spent."
              }
            }
          },
          "window": {
            "type": "object",
            "properties": {
              "from": {
                "type": "string",
                "format": "date"
              },
              "to": {
                "type": "string",
                "format": "date"
              }
            }
          },
          "totals": {
            "$ref": "#/components/schemas/UsageCounts"
          },
          "daily": {
            "type": "array",
            "items": {
              "allOf": [
                {
                  "type": "object",
                  "properties": {
                    "date": {
                      "type": "string",
                      "format": "date"
                    }
                  }
                },
                {
                  "$ref": "#/components/schemas/UsageCounts"
                }
              ]
            }
          },
          "by_kind": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UsageBreakdown"
            }
          },
          "by_workspace": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UsageBreakdown"
            }
          },
          "by_api_key": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/UsageBreakdown"
            }
          }
        }
      },
      "UsageCounts": {
        "type": "object",
        "properties": {
          "units": {
            "type": "integer",
            "description": "Credits billed: scored rows times engine price for model predictions, or requested horizon per forecast call with all series included. This is the billed usage.",
            "example": 8
          },
          "predictions": {
            "type": "integer",
            "description": "Predictions produced: rows scored, or horizon x series forecast.",
            "example": 2
          }
        },
        "required": [
          "units",
          "predictions"
        ]
      },
      "UsageBreakdown": {
        "allOf": [
          {
            "type": "object",
            "properties": {
              "key": {
                "type": "string",
                "description": "Identifier of the row: kind, workspace or API key."
              },
              "label": {
                "type": "string",
                "nullable": true
              }
            }
          },
          {
            "$ref": "#/components/schemas/UsageCounts"
          }
        ]
      },
      "RequestLog": {
        "type": "object",
        "properties": {
          "window": {
            "type": "object",
            "additionalProperties": true
          },
          "n_requests": {
            "type": "integer"
          },
          "requests": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "request_id": {
                  "type": "string"
                },
                "at": {
                  "type": "string",
                  "format": "date-time"
                },
                "method": {
                  "type": "string",
                  "example": "POST"
                },
                "path": {
                  "type": "string",
                  "example": "/v1/models/mdl_7Kq2Vn5RtWz3Bx8Ldm/predict"
                },
                "status": {
                  "type": "integer",
                  "example": 402
                },
                "error": {
                  "type": "object",
                  "nullable": true,
                  "properties": {
                    "code": {
                      "type": "string",
                      "example": "CREDITS_EXHAUSTED"
                    },
                    "detail": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                },
                "duration_ms": {
                  "type": "integer",
                  "description": "API processing time from request journaling middleware to response preparation, including engine and accounting. Excludes request body parsing, final Elasticsearch journal write and network transit; not isolated inference time."
                },
                "workspace_id": {
                  "type": "string",
                  "example": "ws_3v8QpZ2mKdRt7Lc9Xw"
                },
                "api_key_id": {
                  "type": "string",
                  "example": "key_5Zt8Wn3QmXd7Bp2Kfh",
                  "nullable": true
                },
                "billing_user_id": {
                  "type": "string",
                  "description": "Account billed for this workspace, resolved by the API."
                },
                "actor_user_id": {
                  "type": "string",
                  "nullable": true,
                  "description": "Authenticated user for a JWT session. Null for API keys and historical entries without attribution; not the key creator."
                },
                "auth_method": {
                  "type": "string",
                  "enum": [
                    "jwt",
                    "api_key"
                  ],
                  "nullable": true
                },
                "client_ip": {
                  "type": "string",
                  "nullable": true,
                  "description": "IP resolved from the connection and configured trusted proxies. A gateway, relay or MCP server may be the observed caller. Null if unavailable or invalid."
                },
                "user_agent": {
                  "type": "string",
                  "nullable": true,
                  "maxLength": 1024,
                  "description": "Information about the agent originating the request, when known. Browsers supply their own value. Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
                },
                "client_name": {
                  "type": "string",
                  "nullable": true,
                  "maxLength": 128,
                  "description": "HTTP caller: SDK, MCP server or direct application (for example spavik-sdk-python, spavik-sdk-javascript or spavik-mcp). Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
                },
                "client_version": {
                  "type": "string",
                  "nullable": true,
                  "maxLength": 64,
                  "description": "Version of that SDK, MCP server or direct application. Optional diagnostic metadata, declared by the caller and never used for authorization. Omit unknown values. Values are trimmed and truncated to the documented length."
                }
              }
            }
          }
        }
      },
      "Plan": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "pro"
          },
          "name": {
            "type": "string",
            "example": "Pro"
          },
          "prices": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PlanPrice"
            },
            "description": "Active tariffs only. Empty for Free, negotiated plans, or an offer awaiting Stripe configuration."
          },
          "currency": {
            "type": "string",
            "enum": [
              "USD"
            ]
          },
          "price_excludes_tax": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "price_per_thousand_cents": {
            "type": "integer",
            "nullable": true,
            "example": 597,
            "description": "Top-up price for 1,000 credits, in cents and excluding tax. Null for a negotiated contract."
          },
          "description": {
            "type": "string",
            "example": "For products that rely on prediction every day. Higher limits, advanced production capabilities, and Zero Data Retention."
          },
          "data_policy": {
            "$ref": "#/components/schemas/PlanDataPolicy"
          },
          "advanced_data_controls": {
            "type": "boolean",
            "nullable": true,
            "description": "Advanced governance controls in the offer. Null for negotiated terms."
          },
          "support_tier": {
            "type": "string",
            "nullable": true,
            "example": "email"
          },
          "signup_credits": {
            "type": "integer",
            "example": 0,
            "description": "One-time signup credits offered by Free. Zero for paid plans: subscribing does not grant another signup bonus."
          },
          "limits": {
            "type": "object",
            "properties": {
              "max_context_rows": {
                "type": "integer",
                "nullable": true,
                "example": 1500,
                "description": "Maximum learned context rows. Null for a negotiated contract."
              },
              "max_predict_rows": {
                "type": "integer",
                "nullable": true,
                "example": 1000,
                "description": "Maximum rows per prediction request. Null for a negotiated contract."
              },
              "max_models": {
                "type": "integer",
                "nullable": true,
                "example": 10,
                "description": "Maximum active models across all workspaces of the account. Null for a negotiated contract."
              },
              "included_units_per_month": {
                "type": "integer",
                "nullable": true,
                "example": 30000,
                "description": "Monthly included credits. Zero for Free; null means no configured allowance and does not by itself grant unlimited usage."
              },
              "unlimited_usage": {
                "type": "boolean",
                "description": "Whether usage is explicitly unlimited. False by default, including Enterprise."
              }
            },
            "required": [
              "max_context_rows",
              "max_predict_rows",
              "max_models",
              "included_units_per_month",
              "unlimited_usage"
            ]
          }
        },
        "required": [
          "id",
          "name",
          "prices",
          "currency",
          "price_excludes_tax",
          "price_per_thousand_cents",
          "description",
          "data_policy",
          "advanced_data_controls",
          "support_tier",
          "signup_credits",
          "limits"
        ]
      },
      "PlanPrice": {
        "type": "object",
        "required": [
          "id",
          "billing_interval",
          "amount_cents",
          "currency",
          "price_excludes_tax"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "billing_interval": {
            "type": "string",
            "enum": [
              "month",
              "year"
            ]
          },
          "amount_cents": {
            "type": "integer",
            "minimum": 1,
            "description": "Full amount charged per billing interval, excluding tax. An annual amount is not a monthly equivalent."
          },
          "currency": {
            "type": "string",
            "enum": [
              "USD"
            ]
          },
          "price_excludes_tax": {
            "type": "boolean",
            "enum": [
              true
            ]
          }
        }
      },
      "PlanDataPolicy": {
        "type": "string",
        "enum": [
          "training_allowed",
          "no_training",
          "zero_data_retention",
          "custom"
        ],
        "description": "Data handling policy associated with the offer. training_allowed permits data reuse for global Spavik training; no_training prohibits that reuse; zero_data_retention disables GCS/BigQuery archiving of inputs and outputs for evaluate, backtest, forecast and predict; routes, request bodies, responses, idempotency, model state and outcomes behave identically under every data policy; custom requires negotiated terms."
      }
    }
  }
}
