# Wallet Risk Screening Data

Every payment's external counterparty wallet is screened via a blockchain analytics provider. The sender wallet is screened on an inbound payment; the destination or destination VASP wallet on an outbound payment. This page explains how the outcome is reported on the payment record and how to read it. For what screening evaluates and how the decision is made, see [Compliance and Risk Management](/overviews/compliance-and-risk).

Screening results appear on the payment resource in two places:

- **`risk_status_reasons[]`** — the structured signals that determined the outcome, one object per signal.
- **`provider_metadata[]`** — the raw screening response, passed through for your own analytics and audit.

## Risk Statuses and Webhooks

Screening produces one `risk_status` per payment after the initial check:

| `risk_status` | Meaning |
| --- | --- |
| `unchecked` | Initial state; screening has not yet resolved. |
| `automatically_approved` | No material risk (no risk factors, or no risk factors above threshold). |
| `awaiting_decision` | Material exposure warranting review by your organization. |
| `automatically_rejected` | A severe finding, a blocklisted counterparty, or a screening failure (per your configured failure action). |

If the initial status was `awaiting_decision` and your organization manually reviews the payment and reports a decision, then the `risk_status` will be updated to either:

| `risk_status` | Meaning |
| --- | --- |
| `manually_approved` | A reviewer in your organization approved the payment. |
| `manually_rejected` | A reviewer in your organization rejected the payment. |

Every `risk_status` change fires a `payment.risk_updated` webhook.

:::note
On inbound payments, screening runs in parallel with on-chain finalization: `payment.risk_updated` and `step.completed` can fire in either order, and the screening outcome does not gate the balance credit. On outbound payments, an approving outcome is required before the payment executes. See [Receive an Inbound Payment](/how-tos/receive-an-inbound-payment-to-a-wallet) for the full inbound event sequence.
:::

### How a Screen Resolves to a Status

The checks below are applied in order; the first that resolves determines the `risk_status`. See [Compliance and Risk Management](/overviews/compliance-and-risk#how-the-decision-is-made) for the prose version.

<Mermaid
  chart={`flowchart TD
  S[Screen the external<br/>counterparty wallet] --> NBL{Travel Rule provider<br/>identifies the wallet owner,<br/>and it is on your<br/>allowlist or blocklist?}
  NBL -->|Blocklisted| REJ[automatically_rejected]
  NBL -->|Allowlisted| APP[automatically_approved]
  NBL -->|Not listed, or no<br/>Travel Rule data| OK{Screening completed<br/>after retries?}
  OK -->|No| FAIL[Configured screening-failure<br/>action — default:<br/>automatically_rejected]
  OK -->|Yes| TRL{Wallet attributed to<br/>a listed entity, with<br/>confidence exceeding<br/>your threshold?}
  TRL -->|Blocklisted| REJ
  TRL -->|Allowlisted| APP
  TRL -->|Not listed| HB{Severe finding about<br/>the wallet itself — its<br/>ownership, entity, or behavior?}
  HB -->|Yes| REJ
  HB -->|No| CL[Classify custody as<br/>VASP-hosted or self-hosted]
  CL --> TH[Apply materiality thresholds<br/>by direction and custody]
  TH --> MAP{Highest severity among<br/>material findings?}
  MAP -->|None, Low,<br/>or Medium| APP
  MAP -->|High| REV[awaiting_decision]
  MAP -->|Severe| REJ`}
/>

## Reading risk_status_reasons[]

The array is designed so you can reconstruct the decision from the payment record alone:

- Every reason that **decided** the outcome appears, tagged `DECISIVE`.
- Every risk factor **above its materiality threshold** that did *not* decide the outcome also appears, tagged `NON_DECISIVE` — you see what was evaluated, not only what tipped the decision.
- When the external entity was on an allowlist or blocklist, any reason that would otherwise have decided it appears tagged `SUPERSEDED_BY_ALLOWLIST` or `SUPERSEDED_BY_BLOCKLIST`.
- Risk factors **at or below threshold** never appear here, but the complete raw screening data is always available in `provider_metadata[]`.
- Each factor with thresholds carries both the **configured threshold** and the **observed value**, so you can determine which gate tripped — percentage, dollar, or both.
- When the only above-threshold factors are Low or Medium severity, they are surfaced but the payment still approves automatically — severity, not the threshold breach, is what decides.

## Field Reference

| Field | Description |
| --- | --- |
| `risk_status_reason_type` | One of `OWNERSHIP`, `COUNTERPARTY`, `INDIRECT` (risk-indicator types), `BEHAVIORAL` (wallet behavior patterns), `ENTITY` (entity attribution, including allowlist and blocklist matches), or `ERROR` (screening failure, or a token-contract attribution). |
| `risk_status_reason_category` | For `OWNERSHIP` / `COUNTERPARTY` / `INDIRECT`: a [wallet screening risk category](/overviews/wallet-screening-risk-categories). For `BEHAVIORAL`: a behavioral pattern from the same taxonomy. For `ENTITY`: the entity's category — or, for an allowlist or blocklist match, the participant role matched (`ORIGINATOR`, `ORIGINATOR_VASP`, `BENEFICIARY`, `BENEFICIARY_VASP`, `CUSTODY_SERVICE`, `INTERMEDIARY_VASP`, `CONTRACT`). For `ERROR`: `SCREENING_FAILED` when wallet screening could not be completed, or `Token Contract` when the screened wallet is attributed to a token contract (the payment is routed to manual review). |
| `risk_status_reason_participant_id` | The `id` of the [participant](/overviews/payment-participants) referenced. Populated for `ENTITY` reasons and for the token-contract `ERROR` reason; otherwise `null`. |
| `risk_status_reason_message` | A plain-English description of the risk or the failure to screen. |
| `risk_status_reason_severity` | `Low`, `Medium`, `High`, or `Severe`. For an `ENTITY` reason recording a list match: `ALLOWLISTED` or `BLOCKLISTED`. The screening-failure reason carries `Severe`. |
| `risk_status_reason_significance` | How the reason figured in the decision: `DECISIVE`, `NON_DECISIVE`, `SUPERSEDED_BY_ALLOWLIST`, `SUPERSEDED_BY_BLOCKLIST`, or `null`. |
| `volume_usd_percent` | The observed share of the wallet's volume for this risk indicator (e.g., `"25"`). `OWNERSHIP` / `COUNTERPARTY` / `INDIRECT` only; else `null`. |
| `volume_usd_percent_threshold` | The configured percentage threshold, if set; else `null`. `OWNERSHIP` / `COUNTERPARTY` / `INDIRECT` only. |
| `volume_usd_amount` | The observed dollar volume for this risk indicator (e.g., `"999"`). `OWNERSHIP` / `COUNTERPARTY` / `INDIRECT` / `BEHAVIORAL` only; else `null`. |
| `volume_usd_amount_threshold` | The configured dollar threshold, if set; else `null`. `OWNERSHIP` / `COUNTERPARTY` / `INDIRECT` / `BEHAVIORAL` only. |
| `confidence_score` | The observed confidence level of the entity attribution (e.g., `HIGH_CONFIDENCE`, `NEAR_CERTAINTY`). `CERTAIN` — identifies an entity identity established through a Travel Rule authorization flow rather than inferred from on-chain analytics. For `ENTITY` reasons only when `risk_status_reason_severity` = `ALLOWLISTED` or `BLOCKLISTED`. `null` otherwise. |
| `confidence_score_threshold` | The confidence level the entity attribution must exceed in order for an entity to be eligible for allowlisting or blocklisting. For `ENTITY` reasons only when `risk_status_reason_severity` = `ALLOWLISTED` or `BLOCKLISTED`. `null` otherwise. |

:::note{title="Severity and thresholds"}
A `Severe` finding about the wallet itself — its ownership, its entity attribution, or its behavior — rejects automatically with no thresholds applied. A `Severe` finding for the same risk category but with `risk_status_reason_type` = `COUNTERPARTY` or `INDIRECT` is evaluated against its materiality thresholds. This is to ensure that when the screened wallet itself demonstrates severe risk factors (e.g. owned by a sanctioned entity, linked to terrorist financing) it is blocked, whereas a legitimate, high-volume custodial wallet can pass despite incidental contact with risky addresses.
:::

## Outcome Examples

**Approved, nothing surfaced.** A screen with no risk factors — or none above threshold — produces an empty array. The two cases differ only in the raw response in `provider_metadata[]`:

```json
{
  "risk_status": "automatically_approved",
  "risk_status_reasons": []
}
```

**Approved with factors above threshold.** The "Medium" severity factor decided the outcome. When the most severe risk factor identified has a low or medium  severity, the payment still approves:

```json
{
  "risk_status": "automatically_approved",
  "risk_status_reasons": [
    {
      "risk_status_reason_type": "COUNTERPARTY",
      "risk_status_reason_category": "Gambling Service",
      "risk_status_reason_message": "Address has COUNTERPARTY exposure to Gambling Service",
      "risk_status_reason_severity": "Medium",
      "risk_status_reason_significance": "DECISIVE",
      "risk_status_reason_participant_id": null,
      "volume_usd_percent": "3.40",
      "volume_usd_percent_threshold": "2",
      "volume_usd_amount": "2400",
      "volume_usd_amount_threshold": null,
      "confidence_score": null,
      "confidence_score_threshold": null
    },
    {
      "risk_status_reason_type": "INDIRECT",
      "risk_status_reason_category": "Decentralized Exchange",
      "risk_status_reason_message": "Address has INDIRECT exposure to Decentralized Exchange",
      "risk_status_reason_severity": "Low",
      "risk_status_reason_significance": "NON_DECISIVE",
      "risk_status_reason_participant_id": null,
      "volume_usd_percent": "6.20",
      "volume_usd_percent_threshold": "5",
      "volume_usd_amount": "310",
      "volume_usd_amount_threshold": null,
      "confidence_score": null,
      "confidence_score_threshold": null
    }
  ]
}
```

**Routed to manual review.** A "High" severity counterparty exposure above its materiality threshold decided the outcome; the "Low" severity factor above its threshold is surfaced for context:

```json
{
  "risk_status": "awaiting_decision",
  "risk_status_reasons": [
    {
      "risk_status_reason_type": "COUNTERPARTY",
      "risk_status_reason_category": "Darknet Market",
      "risk_status_reason_message": "Address has COUNTERPARTY exposure to Darknet Market",
      "risk_status_reason_severity": "High",
      "risk_status_reason_significance": "DECISIVE",
      "risk_status_reason_participant_id": null,
      "volume_usd_percent": "4.80",
      "volume_usd_percent_threshold": "2",
      "volume_usd_amount": "8200",
      "volume_usd_amount_threshold": null,
      "confidence_score": null,
      "confidence_score_threshold": null
    },
    {
      "risk_status_reason_type": "INDIRECT",
      "risk_status_reason_category": "Gambling Service",
      "risk_status_reason_message": "Address has INDIRECT exposure to Gambling Service",
      "risk_status_reason_severity": "Low",
      "risk_status_reason_significance": "NON_DECISIVE",
      "risk_status_reason_participant_id": null,
      "volume_usd_percent": "5.50",
      "volume_usd_percent_threshold": "5",
      "volume_usd_amount": "450",
      "volume_usd_amount_threshold": null,
      "confidence_score": null,
      "confidence_score_threshold": null
    }
  ]
}
```

**Rejected due to blocklist match.** The blocklist match decides ahead of every other check. The High counterparty exposure would have routed the payment to review on its own, so it is tagged `SUPERSEDED_BY_BLOCKLIST`:

```json
{
  "risk_status": "automatically_rejected",
  "risk_status_reasons": [
    {
      "risk_status_reason_type": "ENTITY",
      "risk_status_reason_category": "ORIGINATOR_VASP",
      "risk_status_reason_message": "Entity blocklisted",
      "risk_status_reason_severity": "BLOCKLISTED",
      "risk_status_reason_significance": "DECISIVE",
      "risk_status_reason_participant_id": "7d2e9f44-1a6b-4c58-8e03-b5f19c72a3d8",
      "volume_usd_percent": null,
      "volume_usd_percent_threshold": null,
      "volume_usd_amount": null,
      "volume_usd_amount_threshold": null,
      "confidence_score": "HIGH_CONFIDENCE",
      "confidence_score_threshold": "MODERATE_CONFIDENCE"
    },
    {
      "risk_status_reason_type": "COUNTERPARTY",
      "risk_status_reason_category": "High-Risk Exchange",
      "risk_status_reason_message": "Address has COUNTERPARTY exposure to High-Risk Exchange",
      "risk_status_reason_severity": "High",
      "risk_status_reason_significance": "SUPERSEDED_BY_BLOCKLIST",
      "risk_status_reason_participant_id": null,
      "volume_usd_percent": "3.10",
      "volume_usd_percent_threshold": "2",
      "volume_usd_amount": "5600",
      "volume_usd_amount_threshold": null,
      "confidence_score": null,
      "confidence_score_threshold": null
    }
  ]
}
```

**Rejected because screening could not complete.** No screening response was obtained, so `provider_metadata[]` carries no wallet-screening entry. The rejection carries a dedicated screening-failure reason, so it is programmatically distinguishable from a risk-based rejection:

```json
{
  "risk_status": "automatically_rejected",
  "risk_status_reasons": [
    {
      "risk_status_reason_type": "ERROR",
      "risk_status_reason_category": "SCREENING_FAILED",
      "risk_status_reason_message": "Wallet screening could not be completed after retries",
      "risk_status_reason_severity": "Severe",
      "risk_status_reason_significance": "DECISIVE",
      "risk_status_reason_participant_id": null,
      "volume_usd_percent": null,
      "volume_usd_percent_threshold": null,
      "volume_usd_amount": null,
      "volume_usd_amount_threshold": null,
      "confidence_score": null,
      "confidence_score_threshold": null
    }
  ]
}
```

## Manual Review

When `risk_status` is `awaiting_decision`, submit a decision via the [risk review endpoint](/api/payments#submit-risk-review) or in the Tesser dashboard. After the decision is recorded, `risk_status` transitions to `manually_approved` or `manually_rejected`, `risk_reviewed_by` and `risk_reviewed_at` populate, and a second `payment.risk_updated` fires. `risk_status_reasons[]` is unchanged from the automatic outcome — the record keeps showing what the screen found.

## Raw Screening Data

The `provider_metadata[]` entry with `provider_metadata_type` = `WALLET_RISK_SCREENING` carries the raw screening response: the wallet's volumes and transaction counts, the full set of risk indicators (including those below threshold), behavioral patterns, and entity attributions. Use it for your own analytics and audit; Tesser's outcome is derived only from the material signals summarized in `risk_status_reasons[]`.

Abridged example:

```json
{
  "provider_metadata_type": "WALLET_RISK_SCREENING",
  "provider_metadata_key": "TRM_LABS",
  "provider_metadata_data": {
    "address": "1Kuf2Rd8mDyAViwBozGTNYnvWL8uYFrkVo",
    "addressIncomingVolumeUsd": "29720.53",
    "addressOutgoingVolumeUsd": "46113.80",
    "addressTotalVolumeUsd": "75834.33",
    "addressRiskIndicators": [
      {
        "category": "P2P Crypto Marketplace",
        "categoryRiskScoreLevelLabel": "Low",
        "riskType": "INDIRECT",
        "totalVolumeUsd": "2979.64",
        "totalVolumePercent": "3.93"
      }
    ],
    "addressBehavioralSignatures": [],
    "entities": [
      {
        "category": "Exchange",
        "confidenceScoreLabel": "High Confidence",
        "entity": "Example Exchange",
        "riskScoreLevelLabel": "Low"
      }
    ],
    "chain": "bitcoin",
    "screenedAt": "2026-01-26T12:30:45.123Z"
  }
}
```

:::note
The raw payload reflects the screening provider's response shape (camelCase field names) and is passed through as received.
:::

## What Your Organization Can Configure

The thresholds, lists, and failure behavior that drive these outcomes ship with Tesser defaults and can be tuned for your organization — see [What Your Organization Can Configure](/overviews/compliance-and-risk#what-your-organization-can-configure).
