Aller au contenu

Risk models API

Ce contenu n’est pas encore disponible dans votre langue.

The Risk models API is what the Models page uses: it lists your workspace’s risk model versions, queues trainings, and moves a version between draft, shadow, live and retired.

It doesn’t score anything. Scoring happens inside real-time decisions and post-event monitoring, and the score reaches you through rules: see Using model scores in rules.

Who can call it A signed-in person. Reading needs models.view; everything else needs models.manage. API keys can’t call it
How to sign in with curl As described in Rules API: before you start
Going live Never applied directly. POST …/promote always creates a change request that someone else holding models.approve approves
Scope Your own workspace only. Nothing is shared between workspaces
Endpoint What it does Needs Answers
GET /api/v1/models The registry: every version, newest first models.view 200
GET /api/v1/models/options Limits and the standard features models.view 200
GET /api/v1/models/schedule The retraining schedule models.view 200
PUT /api/v1/models/schedule Set the retraining schedule models.manage 200
GET /api/v1/models/{id} One version in full, with its card, metrics, drift and activity models.view 200
POST /api/v1/models Queue a training models.manage 202 with the new id
POST /api/v1/models/{id}/cancel Cancel a queued or running training models.manage 200
DELETE /api/v1/models/{id} Delete a version whose training failed or was cancelled models.manage 204
POST /api/v1/models/{id}/shadow Move a trained draft to shadow models.manage 204
POST /api/v1/models/{id}/unshadow Move a shadow model back to draft models.manage 204
POST /api/v1/models/{id}/promote Propose that a shadow model goes live models.manage 428, then 202
POST /api/v1/models/{id}/retire Retire a model models.manage 204, or 428 then 202 for the live model when approval applies

Up to 200 versions, newest first.

Terminal window
curl -s -b ~/.sp-cookies "$SP/api/v1/models"
[
{
"id": "0192f3a1-7c1e-7b52-9a55-2f2f0f6f4d11",
"version": 3,
"kind": "isolation_forest",
"state": "shadow",
"train_status": "succeeded",
"progress": 1,
"window_from": "2026-07-14T00:00:00Z",
"window_to": "2026-10-12T00:00:00Z",
"row_count": 184220,
"sample_rows": 50000,
"feature_count": 19,
"drift": {
"computed_at": "2026-10-14T09:00:02Z",
"days": 7,
"scored": 15310,
"score_psi": 0.03,
"features": [{ "key": "customer_sum_24h", "label": "Customer: total value of transactions, last 24 hours", "psi": 0.06 }],
"max_psi": 0.06,
"level": "stable"
},
"created_at": "2026-10-12T08:14:09Z",
"finished_at": "2026-10-12T08:16:40Z",
"shadow_at": "2026-10-12T08:31:00Z",
"trained_by": "Kwesi Appiah"
}
]
Field Meaning
version 1, 2, 3 … per workspace
kind Always isolation_forest today
state draft (trained or training; scores nothing), shadow (scores recorded, hidden from rules), live (rules read its score), retired
train_status queued, running, succeeded, failed or cancelled
train_error Why the training failed or was refused, in a sentence
progress Training progress, 0 to 1
window_from, window_to The training window (window_to is exclusive)
row_count Transactions the window held
sample_rows Transactions kept, an even random sample, to grow the trees and measure the baselines
feature_count Inputs the model reads
drift The latest stored drift measurement, only while the version is shadow or live
created_at, finished_at, shadow_at, live_at, retired_at When each step happened
trained_by, approved_by Names. trained_by is absent for a version queued from the command line or by the retraining schedule
scheduled true when the workspace’s retraining schedule asked for this version, not a person

What a training needs, for building a form.

Field Meaning
min_rows The fewest transactions a training window may hold on this installation (default 5,000)
drift_days The window drift is measured over (default 7)
default_window_days, max_window_days 90 and 180
standard_features The 16 features every model reads

Everything in a registry row, plus:

Field Meaning
params trees, sample_size, max_rows, and min_rows (the minimum in force when the training was asked for)
seed The same window, features, parameters and seed give the same model, byte for byte
features The model’s inputs, in order: see Features
metrics Absent until trained. score_quantiles (101 values: the minimum, each percentile, the maximum), score_histogram (the share of training scores in each of 20 equal bins over 0 to 1), score_mean, and for each feature its min, median, max and decile baseline
card Absent until trained. The model card
artifact_format, artifact_sha256, artifact_bytes The stored model’s format (sp-iforest/1), SHA-256 fingerprint and size. The stored model itself is never returned
note The note given when training was asked for
shadow_by, promoted_by, retired_by, retire_reason Who moved it to shadow, who proposed going live, who retired it and why
observed_histogram The share of the scores given over the drift window in each of the 20 bins; empty when nothing was scored
observed_scored How many transactions that is
activity Transactions scored per day over the last 30 days: day (YYYY-MM-DD, UTC), state (shadow or live), scored
drift Drift worked out as of now, while the version is shadow or live
min_rows The installation’s current minimum

404 model not found if there is no such version in your workspace.

Field Meaning
key Stable identifier. model.reasons in rules lists these
label The feature in plain words, in English. Shown as a reason
kind aggregate (a feature-store aggregate, in spec), variable (a field of the transaction, in variable, such as txn.amount) or ratio (numerator divided by denominator, both feature keys)
scale linear, or log: the model sees sign(x) · ln(1 + |x|), so a few very large amounts don’t hide everything else
source standard (part of every model) or rule (an aggregate one of the workspace’s live rules computes)

card is the document for auditors and regulators, written when training finishes and never changed. Its prose is in English.

Field Meaning
title, version, model_type, generated_at, summary What the model is
data_from, data_to, row_count, sample_rows, features What it was trained on
parameters, seed How
score_median, score_p90, score_p99, score_max The spread of its scores on its training sample
intended_use, not_for, limits Lists of sentences
explanations How a score’s reasons are worked out
artifact_format, artifact_sha256, artifact_contains, artifact_excludes What the stored model holds and never holds
governance Shadow before live, a second person to go live, one live model, repeatable training
Field Meaning
computed_at, days When it was measured, and over the last how many days
scored Transactions scored in that window
score_psi Population stability index of the score against training
features The five most shifted features, most shifted first: key, label, psi
max_psi The highest of the score’s and the features’ PSI
level insufficient (fewer than 500 transactions scored: too few to say), stable (under 0.1), moderate (0.1 to 0.25) or significant (above 0.25: train again)

Queues a training on your workspace’s own transactions. It runs in the background on a worker. Poll GET /api/v1/models/{id} for train_status and progress.

Every field is optional.

Field Default Meaning
from 90 days before to Start of the training window
to Now End of the window, exclusive. Not in the future
trees 100 Trees in the forest, 10 to 500
sample_size 256 Transactions each tree is grown on, 32 to 4096
max_rows 50000 Transactions kept from the window, 1000 to 500000
seed Random 0 to 9007199254740991. Give a version’s seed to train the same model again
rule_features true Also read the aggregates the workspace’s live rules compute (up to 16)
note Why this version is being trained, up to 500 characters
Terminal window
curl -s -b ~/.sp-cookies -X POST "$SP/api/v1/models" \
-H 'Content-Type: application/json' \
-d '{"from": "2026-07-14T00:00:00Z", "to": "2026-10-12T00:00:00Z", "note": "After the merchant product launch"}'
{ "id": "0192f3a1-7c1e-7b52-9a55-2f2f0f6f4d11" }

The answer is 202. The new version is a draft with train_status: "queued".

Status When
422 The window ends before it starts, ends in the future, is longer than 180 days or shorter than 7; a parameter is out of range; or not enough data: the window from … to … holds N transactions, and a model needs at least … to mean anything. Choose a longer window, or train once more transactions have arrived.
409 a training is already queued or running in this workspace; wait for it to finish or cancel it

A training that is queued can still end as failed, with train_error saying why: too many transactions to replay, too much feature memory, the time limit, or the worker stopping. The worker also checks the minimum again on what it actually reads.

{ "status": "cancelled" }

status is cancelled when the training was still queued, or running when it had started: the training then stops within a few seconds. 409 this training has already finished otherwise.

Removes a version whose training failed or was cancelled. 409 for any other: only a version whose training failed or was cancelled can be deleted; retire a trained one instead. A trained version is never deleted.

A workspace can have its model trained again weekly or monthly. It is off by default. A scheduled training is queued and limited exactly like one asked for with POST /api/v1/models, and it produces a draft only: shadow and going live stay as they are. See The retraining schedule.

Needs models.view. A workspace that never set a schedule answers {"frequency": "off", "window_days": 90}.

{
"frequency": "monthly",
"window_days": 90,
"next_run_at": "2026-11-12T09:30:00Z",
"last_run_at": "2026-10-12T09:30:41Z",
"last_result": "queued",
"last_model_id": "0199e0a1-…",
"last_model_version": 4,
"updated_at": "2026-09-12T09:30:00Z",
"updated_by": "Kwesi Appiah"
}
Field Meaning
frequency off, weekly or monthly
window_days The training window of each scheduled training: this many days up to the moment it is queued. 7 to 180
next_run_at When the next training is queued. Absent while off
last_run_at When the schedule last came due
last_result queued: a training was queued. skipped: none was, and the next occasion is tried. deferred: another training of the workspace was queued or running, and it is tried again in an hour
last_detail Why the last occasion was skipped or deferred, in a sentence
last_model_id, last_model_version The version the schedule last queued
updated_at, updated_by When the schedule was last set, and by whom

Needs models.manage. Answers 200 with the schedule as GET returns it.

{"frequency": "weekly", "window_days": 60}
Field Meaning
frequency Required. off, weekly or monthly. Anything else is 422
window_days 7 to 180; 90 when left out. Outside that range is 422
  • Switching the schedule on, or to another frequency, sets next_run_at one period from now: seven days for weekly, the same day of the next month for monthly.
  • Changing only window_days keeps next_run_at.
  • off clears next_run_at.

Audited as model.schedule on entity type risk_model_schedule, with the frequency and window before and after; a PUT that changes neither writes nothing. Setting the schedule does not go through approval. Each training the schedule queues is audited as model.train by the system, with schedule set to the frequency.

Moves a trained draft to shadow. The model starts scoring every transaction; its scores are counted for drift and the Models page and never reach a rule. 204.

409 when the version has no trained model, isn’t a draft, or another version is already in shadow: a workspace runs one shadow model at a time. Move it back to draft or retire it first.

Moves a shadow model back to draft. It stops scoring. 204; 409 if it isn’t in shadow.

Proposes that a shadow model becomes the workspace’s live model. This is always a change request, whatever the workspace’s approval policy.

  1. Call it without a reason. The answer is 428 Reason required. Nothing is stored. (If the version isn’t in shadow you get 409 here instead: a model goes live from shadow, after its scores have been looked at.)
  2. Call it again with the X-Change-Reason header, 3 to 2,000 characters. The answer is 202 with the change request.
Terminal window
curl -s -b ~/.sp-cookies -X POST "$SP/api/v1/models/$ID/promote" \
-H 'X-Change-Reason: Two weeks in shadow, drift stable, score spread matches training. MR-212'

Someone else holding models.approve then approves or rejects it through the Change requests API (action type models.lifecycle, operation model.promote). On approval:

  • the version becomes live, with promoted_by the proposer and approved_by the approver;
  • the version that was live is retired, with retire_reason Replaced by version N.

The database refuses a live model with no approver, or whose approver is its proposer. If the version left shadow, or the live version changed, while the request waited, the request is closed as stale and nothing is applied.

Field Meaning
reason Required, 3 to 500 characters. Kept in the registry and the audit log

A draft (trained) or shadow version is retired straight away: 204.

The live model is retired straight away too, unless the workspace requires approval for Risk models (models.lifecycle, on by default once two people hold models.approve). Then it is a change request: 428 without X-Change-Reason, 202 with it. Once the live model is retired, rules read model.available == false and a score of 0 until another version goes live.

409 if the version is already retired, or was never trained (delete it instead). 422 if the reason is missing or too long.

A score is stored only for the live model, and only with its reasons.

Where Field
GET /api/v1/decisions/{id} model: the live model’s score when the decision was made
An alert’s hits model on a hit: the score of the transaction that hit
"model": {
"model_id": "0192f3a1-7c1e-7b52-9a55-2f2f0f6f4d11",
"version": 3,
"score": 0.71,
"percentile": 99.2,
"reasons": [
{ "feature": "amount_vs_avg_30d", "label": "Amount compared with the customer's 30-day average", "value": 14.2, "contribution": 0.31 },
{ "feature": "customer_count_24h", "label": "Customer: number of transactions, last 24 hours", "value": 23, "contribution": 0.12 }
]
}

contribution is a feature’s share of the score: over all the model’s features the shares add up to log2(score) + 1, and a positive one pushed the score up. Up to three reasons are kept.

The answer to POST /api/v1/decisions does not carry the score: your payment system gets the outcome and the rules that fired, as before.

model.train, model.train_cancel, model.delete, model.shadow, model.unshadow, model.promote and model.retire, on entity type risk_model, and model.schedule on entity type risk_model_schedule. Reads are not audited. See the audit log.