User guideDeveloper guideModel metrics ingestion
Developer guide

Model metrics ingestion

Push model monitoring metrics with an ingestion token — payloads, dedup, errors and retries.

Push model metrics

Use the metric ingestion API to push model monitoring readings — drift, performance, stability — from your own pipelines. You send raw values; VerifyWise evaluates each point against the model’s thresholds, records an immutable evaluation, and notifies the model’s assigned stakeholders on a breach.

Prerequisites

  • A model in the model inventory with an external key. The key identifies the model in the URL and is set on the model record.
  • An ingestion token, created under Model risk management → Settings → Metrics feed & tokens. The token is shown once at creation and only a hash is stored — keep it in your secrets manager. Tokens are org-wide or scoped to a single model, and can be rotated or revoked at any time. Rotation revokes the old token immediately — there is no overlap window, so switch every consumer to the new token as part of the same change.

Endpoint

bash
POST https://your-server/api/mrm/models/{externalModelKey}/metrics

externalModelKey is your model’s external key, matched within your organization. An unknown key returns 404.

Authentication

Send the ingestion token (it starts with mrm_) as a bearer token on every request. The token identifies your organization — no user session is involved.

bash
Authorization: Bearer mrm_...
  • An unknown token and a revoked token both return 401 and are deliberately indistinguishable.
  • A model-scoped token used against a different model returns 403.

Send a single point

bash
curl -X POST https://your-server/api/mrm/models/retail-pd-scorecard/metrics \
  -H "Authorization: Bearer mrm_..." \
  -H "Content-Type: application/json" \
  -d '{
    "metric": "psi",
    "value": 0.24,
    "at": "2026-07-02T14:00:00Z",
    "window": "daily",
    "segment": "subprime"
  }'
FieldRequiredDescription
metricYesMetric name, up to 100 characters. Free-form: a name with no matching threshold is accepted and returns no_threshold.
valueYesA finite number. Booleans, NaN and Infinity are rejected.
atYesISO-8601 timestamp of the reading. At most 1 hour in the future; backfilling past readings is allowed. Truncated to the second for deduplication.
windowNoAggregation window label, e.g. "daily". Defaults to none.
segmentNoPopulation segment, e.g. "subprime". Defaults to "overall".
contextNoObject stored with the point for audit context. Never evaluated.

Send a batch

Wrap multiple points in a points array. The same fields apply to every point.

json
{
  "points": [
    { "metric": "psi", "value": 0.24, "at": "2026-07-02T14:00:00Z", "segment": "subprime" },
    { "metric": "auc", "value": 0.81, "at": "2026-07-02T14:00:00Z" }
  ]
}
  • Validation is all-or-nothing: if any point is invalid, the whole request is rejected with 422 and per-index errors, and nothing is written.
  • There is no fixed cap on batch size; the per-token rate limit (5000 requests per 15 minutes in production) is the volume guard. Keep batches modest so a validation error is easy to locate.

Response

A successful request returns 200 with one result per point. accepted counts newly stored points; duplicates are not included.

json
{
  "message": "OK",
  "data": {
    "accepted": 1,
    "results": [
      {
        "metric": "psi",
        "at": "2026-07-02T14:00:00.000Z",
        "status": "breach",
        "pointId": 812,
        "threshold": { "op": "gt", "value_num": 0.25, "severity": "high" }
      },
      {
        "metric": "auc",
        "at": "2026-07-02T14:00:00.000Z",
        "status": "duplicate",
        "duplicate": true,
        "pointId": null
      }
    ]
  }
}
StatusMeaning
okA threshold matched and the value is within it.
warnThe value breached a threshold with severity warn.
breachThe value breached a high or critical threshold.
no_thresholdNo threshold matched this metric, segment and window. The point is stored.
duplicateAn identical point already exists. Nothing new is stored or evaluated.

When a threshold matched, the result carries a frozen snapshot of it: op is one of gt, gte, lt, lte or outside; scalar operators carry value_num, while outside carries value_lo and value_hi; severity is warn, high or critical. For no_threshold and duplicate results the threshold key is omitted entirely — check for its presence before reading it. pointId is the stored point’s id, or null for duplicates.

Idempotency and deduplication

A point is identified by its model, metric, segment, window and timestamp truncated to the second, within your organization. Re-sending the same point returns 200 with status duplicate: no second row is stored, no second evaluation is recorded, and a breach is never double-counted. Re-delivery is safe by design.

Distinct sub-second readings of the same metric, segment and window collapse into one point. Send at most one reading per second per series, or separate them with different windows or segments.

Errors

Error bodies vary by status: validation errors (422) return { message, data: { message, errors } } with per-point errors by index; the authentication 401s return { message, data: { message } }; other errors (400, 403, 404) return { message, data } where data is the reason as a plain string. The rate-limit response (429) is flat: { message, statusCode }.

StatusWhenMessage
400Blank model key in the path.A model key is required
401Missing or malformed Authorization header.Missing or invalid ingestion token
401Unknown or revoked token.Ingestion token is invalid or has been revoked
403Model-scoped token used on a different model.This token is not scoped to this model
404No model with this external key in your organization.Model not found for this key
422One or more invalid points, or an empty points array. Nothing is written; errors lists each failing point by index.One or more points are invalid
429Token rate limit exceeded (5000 requests per 15 minutes in production). RateLimit headers indicate when to resume.Too many metric ingestion requests for this token, please slow down and retry

Retry guidance

  • Retry 429 and 5xx with exponential backoff. The 429 response includes RateLimit headers that indicate when to resume.
  • After a timeout or network failure, re-send the whole request. Deduplication makes re-delivery safe; already-stored points come back as duplicate.
  • Do not retry other 4xx responses unchanged. A 422 lists exactly which points failed and why — fix them and resend.

What happens after ingestion

Every newly stored point gets an immutable evaluation with a frozen copy of the threshold it was judged against — the audit record examiners see. A warn or breach notifies the people assigned to the model’s MRM roles plus any additional recipients configured in alert settings, in-app and — when your organization enables it — by email. A hard breach can also open a finding automatically when that setting is on, and a threshold set to notify and flag for revalidation opens or annotates a revalidation task for the model. These steps run after the response is computed and never change it.

PreviousInbound integrations
Model metrics ingestion - Developer guide - VerifyWise User Guide