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
POST https://your-server/api/mrm/models/{externalModelKey}/metricsexternalModelKey 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.
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
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"
}'| Field | Required | Description |
|---|---|---|
| metric | Yes | Metric name, up to 100 characters. Free-form: a name with no matching threshold is accepted and returns no_threshold. |
| value | Yes | A finite number. Booleans, NaN and Infinity are rejected. |
| at | Yes | ISO-8601 timestamp of the reading. At most 1 hour in the future; backfilling past readings is allowed. Truncated to the second for deduplication. |
| window | No | Aggregation window label, e.g. "daily". Defaults to none. |
| segment | No | Population segment, e.g. "subprime". Defaults to "overall". |
| context | No | Object 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.
{
"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.
{
"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
}
]
}
}| Status | Meaning |
|---|---|
| ok | A threshold matched and the value is within it. |
| warn | The value breached a threshold with severity warn. |
| breach | The value breached a high or critical threshold. |
| no_threshold | No threshold matched this metric, segment and window. The point is stored. |
| duplicate | An 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.
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 }.
| Status | When | Message |
|---|---|---|
| 400 | Blank model key in the path. | A model key is required |
| 401 | Missing or malformed Authorization header. | Missing or invalid ingestion token |
| 401 | Unknown or revoked token. | Ingestion token is invalid or has been revoked |
| 403 | Model-scoped token used on a different model. | This token is not scoped to this model |
| 404 | No model with this external key in your organization. | Model not found for this key |
| 422 | One 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 |
| 429 | Token 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.