<!-- Generated from https://zscreen.zeruai.org/docs/responses by scripts/agent-docs.mjs. Do not edit. -->

# The response

Field by field, what a scored body contains. Every 200 has this shape, whether the answer came from a list lookup or from the model.

## The whole body

One live body, so the sections below have something to point at. It is a real response, copied from a real screen, and every 200 carries the same keys in the same places.

```
{
  "address": "0x97b1043abd9e6fc31681635166d430a458d14f9c",
  "verdict": "FLAGGED",
  "risk_score": 100.0,
  "decided_by": {
    "rule": "ofac_list",
    "source": "rule",
    "reason": "This wallet is on the OFAC sanctions list. Dealing with it is a legal risk."
  },
  "summary": "This wallet is on the OFAC sanctions list. Dealing with it is a legal risk.",
  "checks": [
    {
      "name": "Sanctions list",
      "status": "hit",
      "detail": "Match"
    },
    {
      "name": "Known hack and scam wallets",
      "status": "clear",
      "detail": "No match"
    },
    {
      "name": "Mixers",
      "status": "clear",
      "detail": "No contact"
    },
    {
      "name": "Behaviour",
      "status": "hit",
      "detail": "4 of 6, above the 2 a typical wallet shows"
    }
  ],
  "behaviours": [
    {
      "key": "exchange_exit",
      "label": "Sends most of its money straight into an exchange",
      "clear_label": "Does not funnel its money into an exchange",
      "present": true
    },
    {
      "key": "fast_onward",
      "label": "The wallets it pays move the money on again quickly",
      "clear_label": "The wallets it pays hold on to the money",
      "present": true
    },
    {
      "key": "round_amounts",
      "label": "Sends round, hand-picked amounts",
      "clear_label": "Amounts look ordinary, not hand-picked",
      "present": false
    },
    {
      "key": "forwards_fast",
      "label": "Forwards nearly everything within a day of receiving it",
      "clear_label": "Holds what it receives rather than passing it straight on",
      "present": true
    },
    {
      "key": "keeps_nothing",
      "label": "Keeps nothing back — sends out as much as it takes in",
      "clear_label": "Keeps some of what it receives",
      "present": true
    },
    {
      "key": "burst_then_quiet",
      "label": "Worked in a burst, then went quiet for months",
      "clear_label": "Used steadily rather than in one burst",
      "present": false
    }
  ],
  "show_behaviours": true,
  "behaviour_note": "4 of 6. A typical wallet shows 2, and even wallets we know are sanctioned typically show 4.",
  "why": [
    {
      "kind": "decided",
      "reason": "This wallet is on the OFAC sanctions list. Dealing with it is a legal risk.",
      "feature": "ofac_list",
      "value": null,
      "pushed": null,
      "percentile": null
    },
    {
      "kind": "model",
      "reason": "The wallet has been in use for 6 years.",
      "feature": "age_days",
      "value": 1981.5,
      "pushed": null,
      "percentile": null
    },
    {
      "kind": "model",
      "reason": "It has moved $18.3M in and $18.4M out. Almost everything that came in went straight back out.",
      "feature": "usd_in",
      "value": 18322838.163848,
      "pushed": null,
      "percentile": null
    },
    {
      "kind": "model",
      "reason": "It has dealt with 15 different wallets and handled 4 different tokens.",
      "feature": "n_counterparties",
      "value": null,
      "pushed": null,
      "percentile": null
    }
  ],
  "caveats": [],
  "confidence": 0.927,
  "confidence_label": "HIGH",
  "ofac_hard_match": true,
  "data_quality": {
    "capped": false,
    "truncated": false,
    "n_tx_total": 246,
    "coverage_ratio": 1.0,
    "payload_missing": false
  },
  "model": {
    "bags": 200,
    "name": "pu_bag_variant_a_plus500",
    "note": "percentile is the model's own number, 0-100, against the wallets it was trained on; when a list or contact rule fires the risk_score is 100 regardless of it",
    "variant": "a",
    "trained_at": "2026-08-26T13:55:21Z",
    "recall_guarantee": "100% of the 88 OFAC-listed positives sit at or above the REVIEW line in out-of-fold validation",
    "percentile": 98.2
  },
  "scoring_version": "v2",
  "screening_id": 45779,
  "scored_at": "2026-09-17T11:24:01.860246Z",
  "stale": false,
  "served_from": "cache"
}
```

## The answer

| Field | Type | What it is |
| --- | --- | --- |
| `address` | `string` | The address you asked about, lowercased. |
| `verdict` | `string` | CLEAR, REVIEW or FLAGGED. Derived from risk_score, never set beside it. |
| `risk_score` | `number | null` | 0 to 100. Null means the address was not scored at all, which is not the same as scoring low. |
| `decided_by.rule` | `string` | The one thing that set the score: ofac_list, hacker_list, ofac_contact, hacker_contact, mixer_contact, contract, exchange_custody, unchecked or model. |
| `decided_by.source` | `string` | "rule" for a deterministic lookup, "model" when nothing above it fired. |
| `decided_by.reason` | `string` | That decision written out as a sentence. It is repeated as the first entry of why. |
| `summary` | `string` | One or two sentences for a human reader. |
| `ofac_hard_match` | `boolean` | True only for a direct hit on the sanctions list. A lookup, not a prediction. |
| `confidence` | `number` | 0 to 1, how much evidence stood behind the answer. Not the probability that the wallet is bad. |
| `confidence_label` | `string` | HIGH, MED or LOW, the same number banded. |

risk_score can be null

A contract, or a trail that ends inside an exchange's own wallet, is an address this API refuses to score rather than one it scored low. The verdict comes back `REVIEW` and the number comes back `null`. Rendering that as a zero turns a refusal into a clean bill of health.

## why

The reasoning, as an array. Every entry has a `kind`, and there are exactly two of them.

| Field | Type | What it is |
| --- | --- | --- |
| `kind` | `string` | "decided" for the line that set the verdict, "model" for something the model noticed. The first entry is always the decided one. |
| `reason` | `string` | The sentence itself, written for a person. |
| `pushed` | `string | null` | Reserved. Intended to carry "up" or "down", which way an observation moved the score, but the scorer does not yet set it: today it is null on every entry. Do not branch on it. |
| `feature` | `string | null` | The machine name of the signal behind the sentence. |
| `value` | `number | null` | What that signal measured for this wallet. |
| `percentile` | `number | null` | Reserved. Intended to place that value against ordinary wallets, 0 to 100, but the scorer does not yet set it per reason: today it is null on every entry. |

[Reading a verdict](https://zscreen.zeruai.org/docs/reading-a-verdict) works through what to do with the two kinds, and why showing them as one flat list misleads a reader.

## checks

Every check that was run and how it came back. The clean ones are the point: three passes beside one hit is what makes the hit credible.

| Field | Type | What it is |
| --- | --- | --- |
| `name` | `string` | The check, named for a reader rather than for a log. |
| `status` | `string` | "clear" if it ran and found nothing, "hit" if it found something, "unchecked" if it could not run. |
| `detail` | `string` | One sentence on what that status means here. |

unchecked is not clear

A check that could not run has told you nothing. Count it separately in any tally you show a user, and never fold it in with the passes.

## behaviours

Named patterns, each measured against the wallets confirmed to be sanctioned. They describe how a wallet moves money; on their own they accuse it of nothing.

| Field | Type | What it is |
| --- | --- | --- |
| `key` | `string` | Stable machine name for the behaviour. |
| `label` | `string` | How to say it about a wallet that shows it. |
| `clear_label` | `string | null` | The same behaviour said from the other side, so a clean result can name what was looked for instead of showing an empty list. |
| `present` | `boolean | null` | Null, never false, when the input that decides it was missing. A behaviour nobody could look at must not read as one the wallet lacks. |
| `show_behaviours` | `boolean` | Whether this list belongs in front of a customer at all. Two or three of these is what an ordinary wallet looks like. |
| `behaviour_note` | `string | null` | One sentence about how this wallet moves money, safe to show whether or not the list is. |

## caveats

Limits on the answer: how old the sanctions snapshot was, where the data ran out, where a custodian's wallet made one customer indistinguishable from thousands of others.

Caveats are not evidence

A caveat says what this answer could not see. It is not a finding against the wallet, and stacking caveats into a risk display turns a data limit into an accusation.

## The record

| Field | Type | What it is |
| --- | --- | --- |
| `screening_id` | `number | null` | Id of the permanent record. Fetch it later through /v1/screening/{screening_id}. |
| `scored_at` | `string` | ISO 8601 timestamp of when this answer was produced. |
| `scoring_version` | `string` | Which scoring contract produced it. |
| `model` | `object` | Name and version of the model behind a model decision. |
| `data_quality` | `object` | How much history stood behind the answer, so a thin record can be recognised as thin. |

## Freshness

| Field | Type | What it is |
| --- | --- | --- |
| `stale` | `boolean` | True when the record is past its 24 hour life and a refresh is already running. The answer is the last known one. |
| `served_from` | `string` | "cache" for a live record, "stale" for one being refreshed behind your request. |

A stale answer is a real answer that has aged, and it is returned rather than withheld because the last known verdict beats no verdict. If your workflow cannot accept one, read `stale` and wait for the refresh.
