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.
GET /api/v1/algorithm-models
List all algorithm models. Supports cursor-based pagination.
Response
{
"data": [
{
"id": "model_001",
"key": "propensity-credit-card",
"name": "Credit Card Propensity",
"modelType": "bayesian",
"status": "active",
"version": 3,
"trainingSamples": 12500,
"metrics": { "auc": 0.78, "accuracy": 0.82 },
"lastTrainedAt": "2026-03-14T06:00:00.000Z",
"createdAt": "2026-01-20T10:00:00.000Z"
}
],
"pagination": {
"total": 5,
"hasMore": false,
"limit": 50,
"cursor": null
}
}
POST /api/v1/algorithm-models
Create a new algorithm model.
Request Body
| Field | Type | Required | Description |
|---|
key | string | Yes | Unique model key |
name | string | Yes | Display name |
modelType | string | Yes | One of: scorecard, bayesian, logistic_regression, gradient_boosted, thompson_bandit, epsilon_greedy, neural_cf, online_learner, external_endpoint. |
description | string | No | Description |
status | string | No | Default: "draft" |
config | object | No | Model-specific configuration |
targetField | string | No | Target variable field name |
targetSchemaKey | string | No | Schema key containing the target |
predictors | array | No | Predictor feature definitions |
metrics | object | No | Current model metrics (e.g., { auc, accuracy }) |
metricsHistory | array | No | Historical metrics snapshots |
modelState | object | No | Model state (weights, priors, embeddings, etc.) |
learningConfig | object | No | Online learning configuration |
Validation
All fields are validated via Zod schemas:
key: 1-255 characters, must be unique per tenant.
name: 1-255 characters.
modelType: Must be one of the eight enum values listed above.
status: Default draft. One of: draft, active, paused, archived.
config, metrics, modelState, learningConfig: JSON objects (max 100 keys each).
predictors, metricsHistory: JSON arrays (max 500 items each).
Example
curl -X POST https://playground.kaireonai.com/api/v1/algorithm-models \
-H "Content-Type: application/json" \
-H "X-Tenant-Id: my-tenant" \
-d '{
"key": "propensity-credit-card",
"name": "Credit Card Propensity",
"modelType": "scorecard",
"targetField": "converted",
"targetSchemaKey": "customers",
"predictors": ["income", "credit_score", "tenure_months"]
}'
Error codes
| Code | Reason |
|---|
400 | Validation error (missing key or name, invalid modelType), or duplicate key (a model with this key and type already exists). |
415 | Content-Type is not application/json. |
Response: 201 Created
GET /api/v1/algorithm-models/
Get model details including version history.
Response: 200 OK with the model object and versions array.
PUT /api/v1/algorithm-models/
Update a model’s configuration, status, metrics, or learning config.
Request Body
All fields are optional. Only provided fields are updated.
| Field | Type | Description |
|---|
name | string | Updated name |
description | string | Updated description |
status | string | Updated status |
config | object | Updated model config |
targetField | string | Updated target field |
predictors | array | Updated predictor list |
metrics | object | Updated metrics |
modelState | object | Updated model state (weights, coefficients) |
learningConfig | object | Updated learning config |
outcomeWeights | object | Per-outcome scoring weights |
interactionFeatures | object | Interaction feature configuration |
evolutionConfig | object | Auto-evolution tier thresholds |
Response: 200 OK
DELETE /api/v1/algorithm-models?id=
Hard-deletes a model permanently. Unlike qualification rules and contact policies, algorithm models use hard delete (the record is physically removed from the database).
Query parameters
| Parameter | Required | Type | Description |
|---|
id | Yes | string | Model ID to delete. |
Error codes
| Code | Reason |
|---|
400 | Missing id query parameter. |
404 | Algorithm model not found. |
Response: 204 No Content
POST /api/v1/algorithm-models//score
Score a single customer against the model.
Request Body
| Field | Type | Required | Description |
|---|
attributes | object | No | Customer attribute vector |
Response
The response shape depends on model type. The common field is score:
Additional fields may be present depending on model type:
| Field | Type | Present for | Description |
|---|
score | number | All types | The computed score (0-1) |
confidence | number | bayesian | Confidence level |
explanations | array | scorecard, bayesian, gradient_boosted, thompson_bandit, epsilon_greedy | Per-predictor or per-offer breakdowns |
There is no contributions array in the response. Use explanations for per-predictor breakdowns (available for scorecard, bayesian, logistic_regression, and gradient_boosted model types).
POST /api/v1/algorithm-models//score-offer-set
Score a set of offers for a customer. Returns per-offer propensity scores with optional interaction history features.
Request Body
| Field | Type | Required | Description |
|---|
customerAttributes | object | No | Customer feature vector |
offers | array | Yes | Array of { id, name?, attributes } |
customerId | string | No | If provided, fetches real interaction summaries from DB |
context | object | No | Real-time context features |
Response
Scores are returned sorted by score descending — the array index itself is the rank (index 0 = highest score).
{
"modelId": "model_001",
"modelKey": "propensity-credit-card",
"modelType": "bayesian",
"customerId": "CUST001",
"count": 3,
"scores": [
{ "offerId": "offer_001", "offerName": "Credit Card Premium", "score": 0.87, "interactionFeatures": { "impressions": 5, "positiveRate": 0.6 } },
{ "offerId": "offer_002", "offerName": "Savings Account", "score": 0.65 },
{ "offerId": "offer_003", "offerName": "Home Loan", "score": 0.42 }
]
}
There is no rank field in the response. The array order IS the ranking — index 0 is the highest-scored offer. If customerId is provided, interactionFeatures will be populated from real InteractionSummary data.
POST /api/v1/algorithm-models//train
Train (or retrain) a model using the per-model training route. Creates a new version snapshot and updates metrics history. Requires at least 50 interaction records in the tenant.
Response
200 OK — Returns the updated model with incremented version, refreshed metrics, and status set to "active".
422 Unprocessable Entity — Returned when there are fewer than 50 interaction records to train from.
Training requires interaction data recorded via the Respond API. New tenants with no interaction history will receive a 422 until enough outcomes are recorded. Models can still be used for scoring without training — scorecard models use bin-based rules, and other model types use default priors.
POST /api/v1/algorithm-models/train
Bulk-train a model from real interaction outcomes. This is the recommended training endpoint — it calls trainModelFromOutcomes, updates lastTrainedAt, increments version, and creates a ModelVersion snapshot.
Supported model types
All built-in model types are supported:
| Type | Training method |
|---|
scorecard | Evaluates rule config against outcome data, computes confusion matrix metrics |
bayesian | Replays outcomes through Bayesian updater (Naive Bayes with Laplace smoothing) |
logistic_regression | Linear classifier trained in-process with SGD + L1/L2 regularization |
gradient_boosted | LightGBM tree ensemble trained in the Python ML Worker; scored in-process in Node. Requires ML_WORKER_URL to be configured |
thompson_bandit | Computes per-offer conversion rates as evaluation metrics |
epsilon_greedy | Computes per-offer conversion rates as evaluation metrics |
neural_cf | Trains two-tower embedding model with mini-batch SGD (binary cross-entropy) |
online_learner | Streams through interactions one-by-one with SGD logistic regression |
Request Body
| Field | Type | Required | Description |
|---|
modelId | string | Yes | Algorithm model ID to train |
Response
{
"modelId": "model_001",
"modelType": "bayesian",
"sampleCount": 1250,
"metrics": {
"accuracy": 0.74,
"precision": 0.68,
"recall": 0.71,
"f1": 0.69,
"auc": 0.76
},
"status": "success"
}
Error responses
| Status | Condition |
|---|
400 | Missing modelId, model not found, or training error |
400 | status: "insufficient_data" — fewer than 50 interactions in tenant |
POST /api/v1/algorithm-models//upgrade
Upgrade a model to the next tier along the progression scorecard → bayesian → logistic_regression → gradient_boosted. Optionally creates a champion/challenger experiment. Upgrading to gradient_boosted requires the ML Worker to be reachable.
Request Body
| Field | Type | Required | Description |
|---|
createExperiment | boolean | No | If true, auto-creates a 50/50 experiment. Default: false |
Response
{
"upgraded": { "id": "model_002", "modelType": "bayesian", "status": "draft" },
"experiment": { "id": "exp_001", "name": "Credit Card Propensity: Champion vs Bayesian Upgrade" }
}
Response: 201 Created
GET /api/v1/algorithm-models//evolution-history
View model evolution config, progress toward the next tier, and a timeline of version transitions.
Response
{
"modelId": "model_001",
"modelKey": "propensity-credit-card",
"currentModelType": "bayesian",
"evolutionConfig": {
"autoEvolve": true,
"currentTier": "bayesian",
"targetTier": "gradient_boosted",
"thresholds": {
"gradientBoostedSamples": 20000,
"gradientBoostedAuc": 0.80
}
},
"progress": {
"nextTier": "gradient_boosted",
"progressPct": 83,
"readyToEvolve": false,
"details": {
"samplesRequired": 20000,
"samplesActual": 13600,
"aucRequired": 0.80,
"aucActual": 0.78
}
},
"timeline": [
{ "version": 3, "modelType": "bayesian", "metrics": { "auc": 0.78 }, "createdAt": "2026-03-14T06:00:00.000Z" }
]
}
POST /api/v1/algorithm-models//reset-offer
Reset or pause adaptive learning for a specific offer, category, channel, or globally.
Request Body
| Field | Type | Required | Description |
|---|
action | string | No | "reset" (default), "pause", or "resume" |
scope | string | No | "offer" (default), "category", "channel", or "global" |
offerId | string | No | Offer to reset/pause (convenience alias for scopeId) |
categoryId | string | No | Category to reset (resets all offers in category) |
channelId | string | No | Channel to reset |
resetTo | string | No | "category_prior" (default), "global_prior", or "zero" |
reason | string | No | Audit trail reason |
Response
{
"modelId": "model_001",
"scope": "offer",
"scopeId": "offer_auto_renewal",
"action": "reset",
"resetTo": "category_prior",
"previousEvidence": 3241,
"newPrior": 0.281,
"reason": "Fixed qualification rules"
}
See Adaptive Learning for full details on hierarchical learning, evidence decay, and PRIE scoring.
POST /api/v1/algorithm-models//reset-learning
Reset a model’s learned state back to fresh defaults. Creates a pre-reset version snapshot, clears metrics/metricsHistory, resets trainingSamples to 0, and sets status to "draft".
Scorecard models have no learned state — calling reset-learning on a scorecard returns 400 Bad Request.
Response
200 OK — Returns the model with cleared state:
{
"id": "model_001",
"status": "draft",
"version": 4,
"metrics": {},
"metricsHistory": [],
"trainingSamples": 0,
"lastTrainedAt": null
}
GET /api/v1/models//registry
Read the model’s current registry metadata: status, family label, last promotion timestamp, and snapshot metrics. Backed by getRegistryMetadata from src/lib/ml/registry.ts:353 and reads the row from prisma.algorithmModel.findFirst filtered by tenant (registry/route.ts:48-52).
This endpoint lives at /api/v1/models/[id]/registry — note the models prefix (not algorithm-models). It is the model-registry promotion surface, separate from the model CRUD routes documented above.
Path Parameters
| Parameter | Type | Description |
|---|
id | string | AlgorithmModel.id — must belong to the tenant. |
Response
Returned at registry/route.ts:54-60.
{
"modelId": "model_001",
"modelName": "Credit Card Propensity",
"modelType": "bayesian",
"registry": {
"status": "champion",
"family": "credit-card-propensity",
"promotedAt": "2026-04-22T10:00:00.000Z",
"promotedBy": "alice@example.com",
"previousStatus": "challenger",
"metricsAtPromotion": { "auc": 0.79, "accuracy": 0.83 }
}
}
One of draft, challenger, champion, archived. Type alias RegistryStatus at src/lib/ml/registry.ts:38.
Family label that groups models for the “one champion per family” invariant. Defaults to the model name when not explicitly set.
Status codes
| Code | When |
|---|
| 200 | Returns metadata |
| 400 | Missing id path segment |
| 401 | Caller is not authenticated |
| 403 | Caller is not viewer, editor, or admin |
| 404 | Model not found for tenant |
POST /api/v1/models//registry
Promote or demote a model. Enforces the legal-transition matrix declared at src/lib/ml/registry.ts:122-127 and the “one champion per family” invariant — promoting a model to champion demotes any existing champion in the same family inside the same prisma.$transaction. Every status change writes one AuditLog row (best-effort, after the core transaction commits).
Legal status transitions
From ALLOWED_TRANSITIONS at lib/ml/registry.ts:122-127:
| From | Allowed to |
|---|
draft | challenger, archived |
challenger | champion, archived, draft |
champion | archived, challenger |
archived | draft |
Any other transition raises ModelRegistryError with code invalid_transition and the route returns 400.
Request Body
Validated by BodySchema at registry/route.ts:30-34.
Target status. One of draft, challenger, champion, archived.
Operator id or system label for the audit row. Defaults to "system" when omitted (registry/route.ts:92).
Map of metric → number recorded alongside the promotion (e.g., { auc: 0.79, accuracy: 0.83 }). Used by the W6.2 auto-rollback guard to compare a candidate champion against the incumbent.
Response
{
"modelId": "model_001",
"previousStatus": "challenger",
"newStatus": "champion",
"demotedChampionId": "model_old_001"
}
Present only when the promotion to champion displaced an incumbent in the same family. The incumbent’s status is set to challenger in the same transaction.
Present when the W6.2 rollback guard reported breached thresholds but the promotion was still permitted (e.g., bypassRollbackGuard: true). Each entry: { signal, value, threshold }.
Status codes
| Code | When | Source |
|---|
| 200 | Promotion succeeded | registry/route.ts:95 |
| 400 | Invalid body, illegal transition, or ModelRegistryError other than not_found | registry/route.ts:99 |
| 401 | Caller is not authenticated | requireRole |
| 403 | Caller is not admin | requireRole(request, "admin") at registry/route.ts:67 |
| 404 | Model not found for tenant, or ModelRegistryError code not_found | registry/route.ts:98 |
| 429 | Rate limit exceeded — 60 promotions per 60 seconds per tenant | checkLimit at registry/route.ts:75 |
| 500 | Unexpected error | registry/route.ts:101 |
Roles
POST: admin only. GET: admin, editor, viewer.
Roles
| Endpoint | Allowed Roles |
|---|
GET /algorithm-models | admin, editor, viewer |
POST /algorithm-models | admin, editor |
PUT /algorithm-models/{id} | admin, editor |
DELETE /algorithm-models/{id} | admin, editor |
POST /{id}/score | any authenticated |
POST /{id}/score-offer-set | any authenticated |
POST /{id}/train | admin, editor |
POST /algorithm-models/train | admin, editor |
POST /{id}/upgrade | admin, editor |
POST /{id}/reset-offer | admin, editor |
POST /{id}/reset-learning | admin, editor |
GET /{id}/evolution-history | any authenticated |
GET /api/v1/models/{id}/registry | admin, editor, viewer |
POST /api/v1/models/{id}/registry | admin |
See also: Algorithms & Models