# Screen a crypto address against OFAC and EU sanctions lists > An agent paying on-chain — an x402 payTo, a payout wallet, a refund address — can clear it against OFAC and EU sanctioned addresses in one paid call. Published 2026-08-10 · Updated 2026-08-16 · HTML version: https://invoket.com/blog/screen-a-crypto-address-before-an-agent-sends-funds --- An autonomous agent that pays over x402 is signing a transfer to an address it did not choose: the `payTo` came out of a `402` challenge, a discovery listing, or a config file someone else maintains. One paid call — [`GET /screen/address`](/docs/api/screen-address) — checks that address against the sanctioned addresses published by **OFAC** (SDN and Consolidated) and by the **EU** financial sanctions list, and returns the designated entity behind any hit, its programmes and its designation date. Bitcoin, EVM, Tron, Solana and Monero. The lookup runs against a local snapshot, so the address is never forwarded to a third party. ## The problem: the payee address arrives from a machine surface In a human payment flow, someone eyeballs the destination. In an agentic flow there is no one to eyeball it. The address reaches the signer through the protocol itself: - **An x402 `payTo`.** The [x402 v2 specification](https://github.com/coinbase/x402/blob/main/specs/x402-specification-v2.md) defines an `accepts` array in the `402` response, each entry carrying a `scheme`, a CAIP-2 `network`, an `asset` contract and a **`payTo` recipient address**. The agent picks an entry and signs against that address. If it discovered the resource from a machine index rather than from a human integration — see [what happens between a 402 challenge and the paid response](/blog/how-an-agent-discovers-and-buys-a-paid-api) — nothing in that path involved a compliance review. - **A payout or refund wallet** supplied by a counterparty, a marketplace seller or a support ticket. - **A treasury instruction** an agent is executing on a schedule against a wallet book that was cleared once, at onboarding. The exposure is asymmetric. OFAC has published **digital currency addresses inside SDN entries since 2018**, and its [Sanctions Compliance Guidance for the Virtual Currency Industry](https://ofac.treasury.gov/media/913571/download?inline) (October 2021) is explicit on two points that matter here: firms should screen virtual currency addresses against the listed ones, and OFAC may impose civil penalties **on a strict liability standard** — liability does not require knowledge that the counterparty was designated. An agent that cannot say which lists it screened, and as of when, has no answer to give afterwards. The lists are not static. Recent designations show the cadence: OFAC updated the **Central Bank of Iran** designation on 24 April 2026, adding two Tron addresses to the SDN list after a [$344 million USDT freeze](https://www.chainalysis.com/blog/central-bank-of-iran-designation-ofac-update-april-2026/); on 20 May 2026 it designated a **Sinaloa Cartel** network, adding [six Ethereum addresses](https://www.trmlabs.com/resources/blog/ofac-sanctions-sinaloa-cartel-network-including-six-ethereum-addresses). [OFAC publishes list changes continuously](https://ofac.treasury.gov/recent-actions/sanctions-list-updates), on no fixed schedule. An address cleared at onboarding is not an address cleared today. ## Why OFAC-only is not enough The free address feeds circulating in the ecosystem are **OFAC-only** — they are built by extracting the digital-currency fields from the SDN XML. The EU publishes sanctioned addresses too, and they are **not a subset**. The clearest example is Garantex. The EU listed the exchange in its [16th sanctions package, adopted 24 February 2025](https://www.trmlabs.com/resources/blog/eu-includes-crypto-exchange-garantex-in-16th-sanctions-package-on-russia), together with associated wallet addresses — the first time the EU directly designated a Russian crypto exchange. Against an OFAC-only checker, those EU addresses return silence. `/screen/address` ingests the [EU consolidated financial sanctions list](https://data.europa.eu/data/datasets/consolidated-list-of-persons-groups-and-entities-subject-to-eu-financial-sanctions) alongside the OFAC lists, and every response names which list produced the hit. Coverage is the first difference. **Attribution** is the second: a hit is not a boolean and not a score. It carries `entity_name`, `list_source`, `programs`, `listed_on` and the address exactly as the list published it — enough for the agent to log a reason, not just a refusal. ## The call ```http GET /screen/address?address=3My1ffQr5qQzmq4aBFgRqwRYqfB5zPAt8t ``` ```json { "query": { "address": "3My1ffQr5qQzmq4aBFgRqwRYqfB5zPAt8t", "address_normalized": "3My1ffQr5qQzmq4aBFgRqwRYqfB5zPAt8t", "chain_family": "bitcoin", "chain_family_source": "inferred" }, "verdict": "sanctioned", "hits": [ { "entity_id": "EU:EU.12747.39", "entity_name": "Garantex", "list_source": "EU-FSF", "programs": ["UKR"], "listed_on": "2025-02-24", "chain": "BTC", "address_published": "3My1ffQr5qQzmq4aBFgRqwRYqfB5zPAt8t" } ] } ``` The verdict is binary — `sanctioned` or `not_listed` — and the evidence sits in `hits`. Note the `list_source`: **EU-FSF**, not OFAC. ### Normalization is per chain family, and the normalized form is returned Address equality is not one rule. The endpoint applies the rule the chain family dictates and hands back the form it compared: - **EVM** — lowercased, `0x` enforced. The EIP-55 checksum is *not* verified and never causes a rejection, so a mixed-case address copied from a block explorer and a lowercased one from a database both match. - **base58** (Bitcoin legacy, Tron, Solana) — **case preserved**, because it is significant. - **bech32** — lowercased. Whitespace inside the value is collapsed, which matters more than it sounds: an address pasted out of a PDF invoice usually carries some. ### `chain` is law when you give it Send `chain` and it is **law**: an address without that family's shape is refused with **400 `UNRECOGNIZED_ADDRESS_FORMAT`**, unbilled, rather than quietly reinterpreted as another family. Omit it and the family is inferred from the shape, with the answer stating which happened in `query.chain_family_source` (`provided` vs `inferred`). For an agent, that is the difference between a guess and a check. If your pipeline already knows it is paying on Base, declaring `chain=evm` turns a malformed value into a hard stop instead of a lookup against the wrong family. ## What a `not_listed` verdict actually asserts `not_listed` is the answer most calls return, so it has to be precise about its own scope. Every response — hit or no hit — carries `lists_screened`, an array of `{ list, addresses }` counts, **including lists that publish zero addresses**: ```json "lists_screened": [ { "list": "EU-FSF", "addresses": 7 }, { "list": "OFAC-CONS", "addresses": 0 }, { "list": "OFAC-SDN", "addresses": 961 }, { "list": "UN", "addresses": 0 } ] ``` A clean verdict therefore cannot be mistaken for broader coverage than it has. Alongside it, `provenance.freshness` is a `snapshot` with an `as_of` date, and a fixed `limits` sentence restates the boundaries in the payload itself. The three together are what an agent should log next to the transaction: *these lists, at these sizes, as of this date*. ## The x402 golden rule, applied to a verdict The agent pays for the **answer to its question**. "This address is not on these lists, as of this date" is a successful answer → **200**. That is the whole point of the endpoint: the clean result is the one you will get almost every time, and it is the one that carries the audit value. The 4xx range is reserved for requests the service cannot answer, and those are never charged: a missing `address`, a `chain` outside the five families, or a value that is not a recognizable address at all. That last case is deliberate — a password, a truncated string or a misplaced IBAN dropped into the `address` parameter must not be billed as a verdict. ## A concrete shape: the pre-signature gate Wiring this in front of an x402 payment is four steps: 1. **Read the challenge.** Parse the `402` response and pick an entry from `accepts`; extract its `payTo`. 2. **Screen it.** One `GET /screen/address`, with `chain` declared from the entry's CAIP-2 `network` when you know it. 3. **Branch.** On `sanctioned`, do not sign — log `entity_name`, `list_source`, `programs` and `listed_on` as the reason. On `not_listed`, proceed. 4. **Log** `provenance.freshness.as_of` and `lists_screened` next to the settlement hash. The screening call is itself an x402 call, so it runs the same discover → `402` → pay → replay loop as the payment it is gating; the [Quickstart](/docs/quickstart) walks that loop with runnable snippets and [For agents](/docs/for-agents) covers the discovery surfaces. Prices and accepted rails are served live by the [catalog](https://api.invoket.com/catalog) — never hardcoded, here or anywhere else. Two practical notes. A `payTo` is usually **stable per seller**, so the sane policy is to cache the verdict per address with its `as_of` date and re-screen on a schedule rather than on every request. And because the endpoint answers from a local snapshot in milliseconds, the gate does not meaningfully change the latency budget of the payment it protects. ## Files, payout runs and re-screening The same engine takes a list. [`POST /screen/address/batch`](/docs/api/screen-address-batch) screens up to **500 addresses in one call and one settlement**, returning a `summary` of `sanctioned` / `not_listed` / `error` counts and one result per item, in submission order, each with its `index`. A malformed item is flagged at its position without failing the batch. `lists_screened`, `limits` and `provenance` are stated **once for the batch** — every address cleared against the same snapshot, in the same call. That consistency is exactly what a row-by-row loop cannot give you, and it is what makes the batch the natural **re-screening** primitive: after a list update, re-clearing a whole wallet book is one scheduled call, not a project. The same argument, on the fiat rail, is developed in [screening a vendor file of IBANs in bulk](/blog/screen-a-vendor-file-of-ibans-in-one-settlement). ## What this gate does *not* do The scope is narrow on purpose, and stated in the [endpoint documentation](/docs/api/screen-address#scope-and-limits): - **Only addresses the lists publish themselves.** No derived addresses, no associated addresses, no addresses inferred from an entity's other holdings. - **No transaction analysis, no clustering, no risk scoring.** An address that received funds from a designated address is **not** a hit here. That is a different product making a different claim, and conflating the two would make both verdicts worthless. - **Absence from an official list is not a safety signal.** `not_listed` means no signal on the covered lists as of the snapshot date. It is not a compliance opinion and does not discharge your own AML obligations. - **It screens an address, not a party.** To screen a company or person *name* against the same lists, use [`GET /screen/entity`](/docs/api/screen-entity). For a bank transfer rather than an on-chain payment, the equivalent gate is [`GET /preflight/payment`](/docs/api/preflight-payment), which combines IBAN validity, bank and BIC resolution, jurisdiction risk and payee-name screening into one verdict. Used for what it is — one settled call that clears an exact address against the OFAC and EU published lists, with the designating entity attached and the snapshot dated — it turns the one control an autonomous payer cannot perform by eye into something it can run before every transfer. Field tables, the full error contract and the `chain`-is-law rule are in the [`GET /screen/address` documentation](/docs/api/screen-address).