---
title: "Unified risk verdict"
method: GET
path: "/v1/addresses/{address}/risk"
tags: ["addresses"]
---

# Unified risk verdict

`GET /v1/addresses/{address}/risk`

## Request

- Base URL: `https://stablescan.achivx.com`
- URL: `https://stablescan.achivx.com/v1/addresses/{address}/risk`
- Auth: none required

`unknown` is not `none` — never read it as safe. A tier `unknown` names its cause in `evidence` and `incomplete_reasons`; an `unknown` freeze status or a pair whose freeze history is not backfilled turns a would-be `none` into `unknown`, so a Solana tier is never `none`. `Cache-Control: max-age=10` instead of 60 means the exposure walk was cut by its time budget (`time_budget`; read the cause from `/exposure`: `truncation_reason`, `hops_completed`). A replayed verdict carries `Age`; `computed_at` says when it was computed.

## Path parameters

- `address` string, required

## Query parameters

- `chain` union: A network — number, number in a string, slug or CAIP-2 id (the slugs and CAIP-2 ids are /v1/networks' `slug` and `caip2`). The string forms are listed exactly; the server reads them leniently, as A2A and MCP do: surrounding whitespace, a leading + and leading zeros are ignored, slugs and CAIP-2 ids match in any letter case, and an empty or blank value is no chain at all (the default; /v1/freezes/acts alone refuses an empty filter). Anything else (hex, a decimal fraction, a zero-padded CAIP-2 reference, an unindexed network) is 400 invalid_chain.
  - 1 | 10 | 56 | 8453 | 42161 | 728126428 | -1
  - '1' | '10' | '56' | '8453' | '42161' | '728126428' | '-1' | 'ethereum' | 'optimism' | 'bnb-chain' | 'base' | 'arbitrum' | 'tron' | 'solana' | 'eip155:1' | 'eip155:10' | 'eip155:56' | 'eip155:8453' | 'eip155:42161' | 'eip155:728126428' | 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp'
- `from` string

## Response `200`

Unified risk verdict.

- Risk
  - `address` string, required
  - `address_respelled` boolean, required: true when the address you sent is spelled for another kind of chain than chain_id — a T… (Tron) address asked about an EVM chain, a 0x… address asked about Tron, a 32-byte base58 (Solana) key asked about an EVM chain or Tron — and the answer is about those same bytes on chain_id. Accepted, not refused; the same 20 bytes are the same owner on EVM chains and Tron only for a plain key account, not for a contract. Always false when you did not pass chain: the spelling then chooses the chain.
  - `attestation` object: Present when this deploy signs its answers: an Ed25519 signature over the JCS-canonical body without this field. Check it with POST /v1/verify-attestation or against /.well-known/jwks.json.
    - `alg` string, required
    - `data_vintage` integer
    - `kid` string, required
    - `method` string, required
    - `observed_hash` string, required
    - `ruleset_version` string, required
    - `sig` string, required
    - `signed_at` string, required
  - `chain_id` integer, required
  - `chain_source` string, required: How chain_id was chosen: explicit (you passed chain), default (a 0x address with no chain is answered about Base), address_form (a T or base58 address is answered about the chain its spelling implies). The answer is always about the one chain in chain_id.
  - `chains_with_data` object[], nullable, required: The indexed networks, the asked one included, where this address has anything at all (kinds: transfers, labels, issuer_acts) — a cheap presence probe, not a verdict. [] = no indexed network holds a transfer, a label or a standing issuer act for it; null = the map could not be read for this answer, ask again.
    - `chain_id` integer, required
    - `kinds` string[], nullable, required
  - `computed_at` string, required
  - `coverage_status` string
  - `data_vintage` string: The start of this chain's observed history (RFC3339): the left edge of what the scanner has seen, not the age of any data. How fresh the data is: freshness_seconds; the OFAC list: sanctions_list_as_of.
  - `degraded_reason` string: Why a stored exposure walk stands in for the live one: live_build_busy (the server was too busy to start the walk) or live_build_timeout (the live walk ran out of its time budget). Such a verdict is never cached; ask again later. Absent otherwise.
  - `evidence` object[], nullable, required
    - `class` string, required
    - `detail` string, required
    - `signal` string, required
    - `source` string
    - `sources` integer
    - `tier` string
  - `flags` object, required
    - `exposed` boolean
    - `frozen` boolean
    - `labeled` boolean
    - `sanctioned` boolean
    - `service` boolean
  - `freshness_seconds` integer, nullable, required: Age in seconds of this chain's newest indexed transfer when the answer was served; null when it could not be read for this answer or the chain has no indexed transfer yet — a failed read is never reported as a number. The verdict never moves on it.
  - `incomplete_reasons` string[], nullable, required: What this verdict was built without, one reason each, complete and in a fixed order; [] when nothing is missing. coverage_pending, coverage_partial: this Solana address's coverage (coverage_status). time_budget, frontier_cap, short_history, ofac_list_unloaded, ofac_list_stale, no_accusation_labels: the exposure walk under the verdict (get-address-exposure has the walk). freeze_history_incomplete: a freeze pair on this chain whose history is not backfilled (freeze[].freeze_log_backfilled). labels_unread, new_counterparty_unread: a read that did not finish before the deadline (the answer is no-store; ask again). A tier a fact or a label sets stands whatever is listed, and tier unknown always lists at least one reason. The evidence says the same in words, in rows of signal coverage and tier unknown: class F for the exposure walk (its detail names the cut and the hops completed) and for the freeze history, class A for the labels.
  - `new_counterparty` string
  - `note` string, required
  - `requested_address` string, required: The address exactly as you sent it. address beside it is the same bytes written in the spelling of chain_id (0x-hex in lower case on EVM chains, T… on Tron, base58 on Solana) — except on /v1/addresses/{address}/balances, whose address echoes what you sent.
  - `sanctions_list_as_of` string: When the OFAC sanctions list behind this verdict was last refreshed (RFC3339), for this chain's address kind; older than 7 days lists ofac_list_stale, and a verdict with no positive signal reads unknown.
  - `snapshot_age_seconds` integer, nullable: The age in seconds of the stored exposure walk under this verdict when it was served; present exactly when snapshot_served is true.
  - `snapshot_served` boolean, required: true when the exposure walk under this verdict was a stored one rather than walked for this call (the compliance facts and the labels were read now); snapshot_age_seconds is then the walk's age. A fresh stored walk is under 15 minutes old; one standing in for a live walk that could not run or finish (degraded_reason) is up to 6 hours old. A refreshed OFAC list retires every stored walk at once.
  - `tier` string, required

## Other responses

- `400`: The one error envelope. Branch on `error.code`, not on the status.
- `404`: The one error envelope. Branch on `error.code`, not on the status.
- `429`: The per-IP token bucket (20 requests/second, burst 40) is empty — wait `Retry-After`.
- `500`: The one error envelope. Branch on `error.code`, not on the status.
- `503`: The one error envelope. Branch on `error.code`, not on the status.

---

[API](https://skmtc.dev/achivx/apis/stablecoin-scanner-rest-api.md) · [All operations](https://skmtc.dev/achivx/apis/stablecoin-scanner-rest-api/llms.txt) · [OpenAPI document](https://skmtc.dev/achivx/apis/stablecoin-scanner-rest-api/revisions/cbd001c8925c?raw)
