Skip to main content

Drift Detection

Log production predictions and ground truth, create reference baselines, run drift analysis, and configure alerts. The drift detection subsystem powers the MLOps observability layer with CBPE-calibrated confidence tracking.

All endpoints require authentication via X-API-Key header and the appropriate scope.


Prediction Object

{
"predictionId": "pred_abc123",
"modelId": "model_xyz",
"modelVersion": "1.2.0",
"features": { "age": 42, "income": 75000 },
"prediction": "approved",
"probabilities": [0.12, 0.88],
"entityId": "customer_456",
"latencyMs": 23,
"timestamp": "2026-05-16T10:30:00Z"
}

Baseline Object

{
"baselineId": "baseline_001",
"modelId": "model_xyz",
"version": "v1-training",
"active": true,
"sampleSize": 10000,
"featureData": { "age": [25, 30, 42], "income": [50000, 60000, 75000] },
"targetData": { "type": "classification", "values": ["approved", "denied"] },
"createdAt": "2026-05-01T08:00:00Z"
}

POST /api/v1/drift/predictions

Log a production prediction for drift detection. Classification predictions MUST include a probabilities array — its max value is stored as the canonical confidence score used by CBPE. Regression predictions skip confidence capture.

Scope: mlops:write

Request Body

{
"modelId": "model_xyz",
"features": { "age": 42, "income": 75000 },
"prediction": "approved",
"probabilities": [0.12, 0.88],
"entityId": "customer_456",
"modelVersion": "1.2.0",
"latencyMs": 23
}
FieldTypeRequiredDescription
modelIdstringYesModel ID from the model registry
featuresobjectYesInput feature values as key-value pairs
predictionstringYesModel prediction output
entityIdstringNoBusiness entity ID for ground truth matching
probabilitiesarrayNoPer-class probability scores (required for classification)
modelVersionstringNoModel version string
latencyMsnumberNoPrediction latency in milliseconds

Response 201 Created

{
"predictionId": "pred_abc123"
}

GET /api/v1/drift/predictions

List prediction logs for a model.

Scope: mlops:read

Query Parameters

ParameterTypeRequiredDescription
modelIdstringYesModel ID
limitnumberNoMax results (default 100)
offsetnumberNoPagination offset
startDatestringNoFilter start date (ISO 8601)
endDatestringNoFilter end date (ISO 8601)
hasGroundTruthstringNoFilter by ground truth match status (true/false)

Response 200 OK

{
"predictions": [
{
"predictionId": "pred_abc123",
"modelId": "model_xyz",
"features": { "age": 42 },
"prediction": "approved",
"timestamp": "2026-05-16T10:30:00Z"
}
],
"total": 248
}

GET /api/v1/drift/predictions/unmatched

List predictions that have not yet been matched with ground truth.

Scope: mlops:read

Query Parameters

ParameterTypeRequiredDescription
modelIdstringYesModel ID
limitnumberNoMax results (default 100)

Response 200 OK

{
"predictions": [
{
"predictionId": "pred_abc123",
"entityId": "customer_456",
"timestamp": "2026-05-16T10:30:00Z"
}
]
}

POST /api/v1/drift/ground-truth

Add a single ground truth record, matched with a prediction by entityId.

Scope: mlops:write

Request Body

{
"modelId": "model_xyz",
"entityId": "customer_456",
"actualOutcome": "approved",
"outcomeTimestamp": "2026-05-16T12:00:00Z"
}
FieldTypeRequiredDescription
modelIdstringYesModel ID
entityIdstringYesBusiness entity ID
actualOutcomestringYesThe actual outcome/label
outcomeTimestampstringNoWhen the outcome was observed (ISO 8601)

Response 201 Created

{
"groundTruthId": "gt_def456"
}

POST /api/v1/drift/ground-truth/batch

Bulk upload ground truth records.

Scope: mlops:write

Request Body

{
"modelId": "model_xyz",
"records": [
{ "entityId": "customer_456", "actualOutcome": "approved" },
{ "entityId": "customer_789", "actualOutcome": "denied", "outcomeTimestamp": "2026-05-16T12:00:00Z" }
]
}
FieldTypeRequiredDescription
modelIdstringYesModel ID
recordsarrayYesArray of {entityId, actualOutcome, outcomeTimestamp?} objects

Response 200 OK

{
"inserted": 2,
"matched": 2
}

POST /api/v1/drift/baselines

Create a reference baseline from training data. Provide labeledPredictions for classification models to capture real baseline confidences, labels and accuracy — this unlocks calibrated CBPE and prediction-drift comparison. No synthetic fallbacks.

Scope: mlops:write

Request Body

{
"modelId": "model_xyz",
"version": "v1-training",
"featureData": { "age": [25, 30, 42], "income": [50000, 60000, 75000] },
"targetData": { "type": "classification", "values": ["approved", "denied"] },
"labeledPredictions": {
"probabilities": [[0.1, 0.9], [0.7, 0.3]],
"predictedLabels": ["approved", "denied"],
"actualLabels": ["approved", "denied"]
}
}
FieldTypeRequiredDescription
modelIdstringYesModel ID
versionstringYesBaseline version tag
featureDataobjectYes{ featureName: [values...] } — training samples per feature
targetDataobjectNo{ type: "classification"|"regression", values: [...] }
labeledPredictionsobjectNo{ probabilities, predictedLabels, actualLabels } aligned 1:1 — enables CBPE calibration and baseline accuracy

Response 201 Created

{
"baselineId": "baseline_001"
}

GET /api/v1/drift/baselines

List all reference baselines for a model.

Scope: mlops:read

Query Parameters

ParameterTypeRequiredDescription
modelIdstringYesModel ID

Response 200 OK

{
"baselines": [
{
"baselineId": "baseline_001",
"version": "v1-training",
"active": true,
"sampleSize": 10000
}
]
}

GET /api/v1/drift/baselines/active

Get the currently active reference baseline for a model.

Scope: mlops:read

Query Parameters

ParameterTypeRequiredDescription
modelIdstringYesModel ID

Response 200 OK

Returns the active Baseline object.


PUT /api/v1/drift/baselines/:id/activate

Set a specific baseline as the active one for drift analysis.

Scope: mlops:write

Path Parameters

ParameterTypeRequiredDescription
idstringYesBaseline ID to activate

Request Body

{
"modelId": "model_xyz"
}
FieldTypeRequiredDescription
modelIdstringYesModel ID

Response 200 OK

{
"activated": true,
"baselineId": "baseline_001"
}

POST /api/v1/drift/analyze

Run drift analysis for a model. Spawns a K8s job that runs the full algorithm registry (univariate, multivariate, performance, streaming, custom) against recent predictions.

Scope: mlops:write

Request Body

{
"modelId": "model_xyz",
"windowDays": 7,
"windowStart": "2026-05-09T00:00:00Z",
"windowEnd": "2026-05-16T00:00:00Z"
}
FieldTypeRequiredDescription
modelIdstringYesModel ID
windowDaysnumberNoDays of predictions to analyze
windowStartstringNoAnalysis window start (ISO 8601)
windowEndstringNoAnalysis window end (ISO 8601)

Response 200 OK

{
"jobId": "drift_job_001",
"status": "running"
}

GET /api/v1/drift/analyze/:id/status

Check the status of a drift analysis job.

Scope: mlops:read

Path Parameters

ParameterTypeRequiredDescription
idstringYesJob ID

Query Parameters

ParameterTypeRequiredDescription
modelIdstringYesModel ID

Response 200 OK

{
"jobId": "drift_job_001",
"status": "succeeded",
"startedAt": "2026-05-16T10:00:00Z",
"completedAt": "2026-05-16T10:05:00Z"
}

GET /api/v1/drift/results/latest

Get the most recent drift analysis result for a model.

Scope: mlops:read

Query Parameters

ParameterTypeRequiredDescription
modelIdstringYesModel ID

Response 200 OK

{
"resultId": "result_001",
"modelId": "model_xyz",
"status": "warning",
"algorithms": {
"ks_test": { "drift": true, "score": 0.18 },
"psi": { "drift": false, "score": 0.05 }
},
"createdAt": "2026-05-16T10:05:00Z"
}

GET /api/v1/drift/results

List drift analysis result history for a model.

Scope: mlops:read

Query Parameters

ParameterTypeRequiredDescription
modelIdstringYesModel ID
limitnumberNoMax results (default 50)
statusstringNoFilter by drift status: ok, warning, alert

Response 200 OK

{
"results": [
{
"resultId": "result_001",
"status": "warning",
"createdAt": "2026-05-16T10:05:00Z"
}
]
}

GET /api/v1/drift/alerts

Get the drift alert configuration for a model, including per-algorithm overrides and schedule.

Scope: mlops:read

Query Parameters

ParameterTypeRequiredDescription
modelIdstringYesModel ID

Response 200 OK

{
"modelId": "model_xyz",
"enabled": true,
"algorithms": {
"enabled": ["ks_test", "psi"],
"overrides": { "ks_test": { "warningThreshold": 0.1, "alertThreshold": 0.2 } }
},
"accuracyDropWarning": 0.05,
"accuracyDropAlert": 0.10,
"notifications": { "email": true, "recipients": ["mlops@example.com"] },
"schedule": { "enabled": true, "frequency": "daily", "timeWindowDays": 7 }
}

PUT /api/v1/drift/alerts

Update drift alert configuration. Enable/disable algorithms, override thresholds per algorithm, configure accuracy-drop thresholds, notifications, and schedule. The backend scheduler reads schedule.enabled/frequency directly — no separate schedule endpoint.

Scope: mlops:write

Request Body

{
"modelId": "model_xyz",
"enabled": true,
"algorithms": {
"enabled": ["ks_test", "psi"],
"overrides": { "ks_test": { "warningThreshold": 0.1, "alertThreshold": 0.2 } }
},
"accuracyDropWarning": 0.05,
"accuracyDropAlert": 0.10,
"minBaselineSampleSize": 30,
"notifications": { "email": true, "recipients": ["mlops@example.com"] },
"schedule": { "enabled": true, "frequency": "daily", "timeWindowDays": 7 }
}
FieldTypeRequiredDescription
modelIdstringYesModel ID
enabledbooleanNoEnable/disable alerts
algorithmsobjectNo{ enabled: string[], overrides: { [algo]: { warningThreshold, alertThreshold, params } } }
accuracyDropWarningnumberNoWarning threshold for accuracy drop (default 0.05)
accuracyDropAlertnumberNoAlert threshold for accuracy drop (default 0.10)
minBaselineSampleSizenumberNoMinimum required baseline sampleSize (default 30)
notificationsobjectNo{ email, slack?, recipients[] }
scheduleobjectNo{ enabled, frequency: hourly|daily|weekly, timeWindowDays }

Response 200 OK

{
"updated": true
}

GET /api/v1/drift/performance

Get model performance history over time.

Scope: mlops:read

Query Parameters

ParameterTypeRequiredDescription
modelIdstringYesModel ID
windowDaysnumberNoLookback window in days (default 30)
granularitystringNoTime bucket: hourly, daily, weekly

Response 200 OK

{
"history": [
{ "timestamp": "2026-05-15T00:00:00Z", "accuracy": 0.92, "sampleSize": 480 },
{ "timestamp": "2026-05-16T00:00:00Z", "accuracy": 0.89, "sampleSize": 512 }
]
}