> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kaireonai.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Model Governance

> Model approval workflows, drift detection, and fairness parity checks.

The Model Governance API provides controls for responsible AI deployment: approval workflows before production promotion, automated drift detection, and fairness parity checks across customer segments.

## GET /api/v1/model-governance

Retrieve governance status for a model including approval history and drift check results.

### Query Parameters

| Parameter | Type   | Required | Description        |
| --------- | ------ | -------- | ------------------ |
| `modelId` | string | Yes      | Algorithm model ID |

### Response

```json theme={null}
{
  "modelId": "clx...",
  "approvals": [
    {
      "id": "clx...",
      "modelId": "clx...",
      "version": 3,
      "requestedBy": "data-scientist@example.com",
      "status": "approved",
      "reviewedBy": "admin@example.com",
      "reason": "AUC improved from 0.82 to 0.87",
      "createdAt": "2026-03-18T10:00:00.000Z"
    }
  ],
  "driftChecks": [
    {
      "id": "clx...",
      "modelId": "clx...",
      "version": 3,
      "driftDetected": false,
      "psiScore": 0.04,
      "aucDelta": -0.01,
      "checkedAt": "2026-03-18T12:00:00.000Z"
    }
  ]
}
```

***

## POST /api/v1/model-governance

Perform governance actions. **Editor or Admin** (review requires Admin).

### Actions

#### `request_approval` — Request approval to promote a model version

```json theme={null}
{
  "action": "request_approval",
  "modelId": "clx...",
  "version": 3,
  "metrics": { "auc": 0.87, "precision": 0.82, "recall": 0.79 }
}
```

**Response (201):**

```json theme={null}
{ "approvalId": "clx...", "status": "pending" }
```

#### `review` — Approve or reject a model promotion (Admin only)

```json theme={null}
{
  "action": "review",
  "approvalId": "clx...",
  "decision": "approved",
  "reason": "AUC meets threshold, no drift detected"
}
```

**Response:**

```json theme={null}
{ "approvalId": "clx...", "status": "approved" }
```

#### `drift_check` — Run a drift detection check

Compares baseline vs. current score distributions using three methods:

* **PSI** (Population Stability Index): measures shift between score distributions (\< 0.1 OK, 0.1–0.25 monitor, > 0.25 retrain)
* **KS** (Kolmogorov-Smirnov): max CDF difference between distributions
* **AUC decay**: drop in AUC from baseline

Each check is persisted to the model drift-check history.

```json theme={null}
{
  "action": "drift_check",
  "modelId": "clx...",
  "version": 3,
  "baselineScores": [0.1, 0.3, 0.5, 0.7, 0.9],
  "currentScores": [0.15, 0.35, 0.45, 0.65, 0.85],
  "baselineAuc": 0.85,
  "currentAuc": 0.82,
  "thresholds": { "psi": 0.25, "ks": 0.1, "aucDecay": 0.05 }
}
```

**Response (200):**

```json theme={null}
{
  "psi": { "score": 0.03, "drifted": false },
  "ks": { "score": 0.05, "drifted": false },
  "aucDecay": { "score": 0.03, "drifted": false },
  "anyDrift": false
}
```

| Field              | Type    | Description                                              |
| ------------------ | ------- | -------------------------------------------------------- |
| `psi.score`        | number  | Population Stability Index (0 = identical distributions) |
| `psi.drifted`      | boolean | `true` if PSI exceeds threshold (default 0.25)           |
| `ks.score`         | number  | Kolmogorov-Smirnov statistic (0–1)                       |
| `ks.drifted`       | boolean | `true` if KS exceeds threshold (default 0.1)             |
| `aucDecay.score`   | number  | `baselineAuc - currentAuc`, floored at zero.             |
| `aucDecay.drifted` | boolean | `true` if AUC dropped more than threshold (default 0.05) |
| `anyDrift`         | boolean | `true` if any of the three checks flagged drift          |

#### `parity_check` — Check fairness across customer segments

Compares mean scores across segments and flags those deviating more than `maxDeviation` from the overall mean.

```json theme={null}
{
  "action": "parity_check",
  "segmentScores": {
    "young_adults": [0.8, 0.7, 0.9, 0.85],
    "seniors": [0.3, 0.2, 0.4, 0.25],
    "middle_aged": [0.5, 0.6, 0.55, 0.5]
  },
  "maxDeviation": 0.15
}
```

**Response (200):**

```json theme={null}
{
  "results": [
    { "segment": "young_adults", "sampleSize": 4, "meanScore": 0.813, "stdDev": 0.075 },
    { "segment": "seniors", "sampleSize": 4, "meanScore": 0.288, "stdDev": 0.073 },
    { "segment": "middle_aged", "sampleSize": 4, "meanScore": 0.538, "stdDev": 0.041 }
  ],
  "disparityDetected": true,
  "flaggedSegments": ["young_adults", "seniors"]
}
```

| Field               | Type      | Description                                                       |
| ------------------- | --------- | ----------------------------------------------------------------- |
| `results`           | array     | Per-segment statistics (sampleSize, meanScore, stdDev)            |
| `disparityDetected` | boolean   | `true` if any segment deviates > `maxDeviation` from overall mean |
| `flaggedSegments`   | string\[] | Segments exceeding the deviation threshold                        |

***

## POST /api/v1/models/{id}/drift

Feature-distribution drift check. Unlike the `drift_check` action above (which compares **score** distributions), this endpoint compares two **feature-value** snapshots keyed by feature name and returns PSI + KS per feature plus an overall severity verdict. The model `id` is used only for audit-log attribution — the trained model itself is not read.

<Note>
  This route lives at `/api/v1/models/[id]/drift` (the `models` prefix, not `algorithm-models`), parallel to the model registry surface. It is tenant-scoped, open to any authenticated caller, and rate-limited to **30 requests per minute per tenant**.
</Note>

### Path Parameters

| Parameter | Type   | Description                                     |
| --------- | ------ | ----------------------------------------------- |
| `id`      | string | Algorithm model ID — must belong to the tenant. |

### Request Body

| Field       | Type   | Required | Description                                              |
| ----------- | ------ | -------- | -------------------------------------------------------- |
| `reference` | object | Yes      | Map of `featureName → number[]` (baseline distribution). |
| `current`   | object | Yes      | Map of `featureName → number[]` (current distribution).  |

```json theme={null}
{
  "reference": { "age": [22, 34, 41, 55], "income": [40000, 52000, 61000] },
  "current":   { "age": [23, 35, 44, 58], "income": [41000, 53000, 63000] }
}
```

### Response

```json theme={null}
{
  "modelId": "clx...",
  "modelName": "Credit Card Propensity",
  "features": [
    {
      "feature": "age",
      "psi": { "psi": 0.04, "severity": "none", "bins": [] },
      "ks": { "d": 0.08, "pValue": 0.71, "significant": false }
    }
  ],
  "overallSeverity": "none",
  "alertFeatures": []
}
```

| Field                     | Type      | Description                                                                                   |
| ------------------------- | --------- | --------------------------------------------------------------------------------------------- |
| `features[].psi.psi`      | number    | Population Stability Index for the feature.                                                   |
| `features[].psi.severity` | string    | `none` (\< 0.1), `monitor` (0.1–0.25), or `alert` (≥ 0.25).                                   |
| `features[].ks`           | object    | Kolmogorov-Smirnov result: `{ d, pValue, significant }` (`significant` when `pValue < 0.05`). |
| `overallSeverity`         | string    | `alert` if any feature alerts, else `monitor` if any is monitored, else `none`.               |
| `alertFeatures`           | string\[] | Feature names whose PSI alerted or whose KS is significant with `d > 0.1`.                    |

### Error codes

| Code  | Reason                                          |
| ----- | ----------------------------------------------- |
| `400` | Missing `id`, or request body fails validation. |
| `404` | Algorithm model not found for tenant.           |
| `429` | Rate limit exceeded (30/min/tenant).            |

### Roles

any authenticated

***

## POST /api/v1/models/import

Bring-your-own-model import. Accepts a **multipart/form-data** upload of an ONNX model, persists it as an `AlgorithmModel` with `modelType: "onnx_imported"`, hashes the bytes (sha256), and stores them inline in `modelState` (or offloads to a configured blob store above the inline threshold).

<Note>
  Only ONNX is supported in V1. Single-file models only; multi-file bundles are rejected. File size cap: **100 MB**. Admin role required.
</Note>

### Form fields

| Part              | Type   | Required | Description                                                       |
| ----------------- | ------ | -------- | ----------------------------------------------------------------- |
| `file`            | file   | Yes      | ONNX model bytes.                                                 |
| `name`            | string | Yes      | Display name for the created model.                               |
| `family`          | string | Yes      | Must be `"onnx_imported"` (the only supported value).             |
| `featureNames`    | string | Yes      | JSON-encoded ordered `string[]` matching the ONNX input shape.    |
| `featureDefaults` | string | No       | JSON-encoded `Record<string, number>` for missing-input handling. |

### Response

```json theme={null}
{
  "id": "clx...",
  "name": "Fraud Propensity ONNX",
  "modelType": "onnx_imported",
  "bytesHashSha256": "9f86d081...",
  "size": 248192,
  "featureCount": 12,
  "createdAt": "2026-04-22T10:00:00.000Z"
}
```

**Response:** `201 Created`

### Error codes

| Code  | Reason                                                                                                                                                  |
| ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400` | Missing/invalid `file`, `name`, or `featureNames`; `family` not `onnx_imported`; `featureNames`/`featureDefaults` JSON invalid; or file exceeds 100 MB. |
| `403` | Caller is not `admin`.                                                                                                                                  |

### Roles

admin
