Skip to main content

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

FieldTypeRequiredDescription
keystringYesUnique model key
namestringYesDisplay name
modelTypestringYesOne of: scorecard, bayesian, logistic_regression, gradient_boosted, thompson_bandit, epsilon_greedy, neural_cf, online_learner, external_endpoint.
descriptionstringNoDescription
statusstringNoDefault: "draft"
configobjectNoModel-specific configuration
targetFieldstringNoTarget variable field name
targetSchemaKeystringNoSchema key containing the target
predictorsarrayNoPredictor feature definitions
metricsobjectNoCurrent model metrics (e.g., { auc, accuracy })
metricsHistoryarrayNoHistorical metrics snapshots
modelStateobjectNoModel state (weights, priors, embeddings, etc.)
learningConfigobjectNoOnline 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

CodeReason
400Validation error (missing key or name, invalid modelType), or duplicate key (a model with this key and type already exists).
415Content-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.
FieldTypeDescription
namestringUpdated name
descriptionstringUpdated description
statusstringUpdated status
configobjectUpdated model config
targetFieldstringUpdated target field
predictorsarrayUpdated predictor list
metricsobjectUpdated metrics
modelStateobjectUpdated model state (weights, coefficients)
learningConfigobjectUpdated learning config
outcomeWeightsobjectPer-outcome scoring weights
interactionFeaturesobjectInteraction feature configuration
evolutionConfigobjectAuto-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

ParameterRequiredTypeDescription
idYesstringModel ID to delete.

Error codes

CodeReason
400Missing id query parameter.
404Algorithm model not found.
Response: 204 No Content

POST /api/v1/algorithm-models//score

Score a single customer against the model.

Request Body

FieldTypeRequiredDescription
attributesobjectNoCustomer attribute vector

Response

The response shape depends on model type. The common field is score:
{
  "score": 0.73
}
Additional fields may be present depending on model type:
FieldTypePresent forDescription
scorenumberAll typesThe computed score (0-1)
confidencenumberbayesianConfidence level
explanationsarrayscorecard, bayesian, gradient_boosted, thompson_bandit, epsilon_greedyPer-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

FieldTypeRequiredDescription
customerAttributesobjectNoCustomer feature vector
offersarrayYesArray of { id, name?, attributes }
customerIdstringNoIf provided, fetches real interaction summaries from DB
contextobjectNoReal-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:
TypeTraining method
scorecardEvaluates rule config against outcome data, computes confusion matrix metrics
bayesianReplays outcomes through Bayesian updater (Naive Bayes with Laplace smoothing)
logistic_regressionLinear classifier trained in-process with SGD + L1/L2 regularization
gradient_boostedLightGBM tree ensemble trained in the Python ML Worker; scored in-process in Node. Requires ML_WORKER_URL to be configured
thompson_banditComputes per-offer conversion rates as evaluation metrics
epsilon_greedyComputes per-offer conversion rates as evaluation metrics
neural_cfTrains two-tower embedding model with mini-batch SGD (binary cross-entropy)
online_learnerStreams through interactions one-by-one with SGD logistic regression

Request Body

FieldTypeRequiredDescription
modelIdstringYesAlgorithm 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

StatusCondition
400Missing modelId, model not found, or training error
400status: "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

FieldTypeRequiredDescription
createExperimentbooleanNoIf 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

FieldTypeRequiredDescription
actionstringNo"reset" (default), "pause", or "resume"
scopestringNo"offer" (default), "category", "channel", or "global"
offerIdstringNoOffer to reset/pause (convenience alias for scopeId)
categoryIdstringNoCategory to reset (resets all offers in category)
channelIdstringNoChannel to reset
resetTostringNo"category_prior" (default), "global_prior", or "zero"
reasonstringNoAudit 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

ParameterTypeDescription
idstringAlgorithmModel.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 }
  }
}
registry.status
string
One of draft, challenger, champion, archived. Type alias RegistryStatus at src/lib/ml/registry.ts:38.
registry.family
string
Family label that groups models for the “one champion per family” invariant. Defaults to the model name when not explicitly set.

Status codes

CodeWhen
200Returns metadata
400Missing id path segment
401Caller is not authenticated
403Caller is not viewer, editor, or admin
404Model 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). From ALLOWED_TRANSITIONS at lib/ml/registry.ts:122-127:
FromAllowed to
draftchallenger, archived
challengerchampion, archived, draft
championarchived, challenger
archiveddraft
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.
status
string
required
Target status. One of draft, challenger, champion, archived.
actor
string
Operator id or system label for the audit row. Defaults to "system" when omitted (registry/route.ts:92).
metricsSnapshot
object
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"
}
demotedChampionId
string
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.
rollbackGuardBreaches
array
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

CodeWhenSource
200Promotion succeededregistry/route.ts:95
400Invalid body, illegal transition, or ModelRegistryError other than not_foundregistry/route.ts:99
401Caller is not authenticatedrequireRole
403Caller is not adminrequireRole(request, "admin") at registry/route.ts:67
404Model not found for tenant, or ModelRegistryError code not_foundregistry/route.ts:98
429Rate limit exceeded — 60 promotions per 60 seconds per tenantcheckLimit at registry/route.ts:75
500Unexpected errorregistry/route.ts:101

Roles

POST: admin only. GET: admin, editor, viewer.

Roles

EndpointAllowed Roles
GET /algorithm-modelsadmin, editor, viewer
POST /algorithm-modelsadmin, editor
PUT /algorithm-models/{id}admin, editor
DELETE /algorithm-models/{id}admin, editor
POST /{id}/scoreany authenticated
POST /{id}/score-offer-setany authenticated
POST /{id}/trainadmin, editor
POST /algorithm-models/trainadmin, editor
POST /{id}/upgradeadmin, editor
POST /{id}/reset-offeradmin, editor
POST /{id}/reset-learningadmin, editor
GET /{id}/evolution-historyany authenticated
GET /api/v1/models/{id}/registryadmin, editor, viewer
POST /api/v1/models/{id}/registryadmin
See also: Algorithms & Models