# Check the payee before your AI agent pays it (x402, Node and Python)

> Source: <https://dev.to/nikoble1926/check-the-payee-before-your-ai-agent-pays-it-x402-node-and-python-671>
> Published: 2026-10-07 14:37:09+00:00

An AI agent that pays over x402 reads a `payTo` address from a 402 response and sends USDC to it. Some agent wallets already let the owner set a daily budget, a per transaction limit and approved domains. A check on the address itself is a separate question: is the wallet you are about to pay on a sanctions list?

This post shows a small pattern for that, with working Node and Python examples. The full code is here: [https://github.com/Nikoble1926/sanctions-check-before-pay](https://github.com/Nikoble1926/sanctions-check-before-pay)

One paid call screens a wallet address against four lists: OFAC SDN, UN Consolidated, EU and UK. The response is signed and reports each list separately, matched or clear, with that list's version date. It costs 0.01 USDC per call, paid over x402 itself, on Base or Solana. No account, no API key.

```
GET https://sanctions.nsgoods.org/screen-multi?address=<payTo>&chain=<optional hint>
```

There is a free signed sample of the exact response shape:

```
GET https://sanctions.nsgoods.org/screen-multi/preview
```

The response has a headline `verdict` (`clean`, `deny` or `indeterminate_ofac_unavailable`) and an `any_list_match` flag that covers all four lists. The headline verdict on its own only speaks for OFAC. So the decision uses both, and it treats everything that is not an explicit clean answer as a no:

``` js
export const decide = (status, d) =>
  status === 200 && !!d && d.verdict === "clean" && d.any_list_match === false;
```

`deny`, `indeterminate_ofac_unavailable`, a 400, a 5xx, a timeout or a body that is not JSON all mean: do not pay. We had this wrong in our first draft. The script said "pay" when the OFAC feed was unavailable, which is the worst possible failure for a check like this. A check that cannot run must never turn into a yes.

Node 20 or newer:

```
npm install
EVM_PRIVATE_KEY=0x... node check-before-pay.mjs <payTo address> [chain]
```

Python:

```
pip install -r requirements.txt
EVM_PRIVATE_KEY=0x... python check_before_pay.py <payTo address> [chain]
```

Exit code 0 means clean, go ahead. Any other exit code means do not pay, so you can drop it in front of the payment step of an agent or a script. Without `EVM_PRIVATE_KEY` both scripts stop at the 402 and print the price, so you can try them without a wallet. Use a low balance hot wallet for the key, never your main one.

Two things we only found by testing on a clean machine, in case they save you time:

`@x402/axios`, the payment happens when the first 402 comes back as an error. If you set `validateStatus: () => true` on the paying client, the 402 counts as a success and the client never pays. The example uses `validateStatus: s => s !== 402`, so 400 and 5xx still do not throw.` web3` installed (the `x402[evm]` extra). Without it, the import fails before anything runs. The `requirements.txt` in the repo pins it.
The response is signed, so you do not have to trust the transport:

```
node verify-signature.mjs
```

It fetches the free preview and recovers the signer offline. The service signs the body without `signed_by` and `signature`, as compact JSON with sorted keys and non ASCII characters escaped, with an Ethereum personal_sign. Expected signer: `0x57fF0F084Cba33e6761503f90eEF0Da9F159350c`. The one trap here: the escaping. JavaScript's `JSON.stringify` leaves non ASCII characters as they are, so the verifier escapes them by hand to match.

Not legal advice. Passing the rule means the address is not on these four lists at their stated version dates, nothing more. It says nothing about who controls the wallet or where its funds came from.

Code, MIT licensed: [https://github.com/Nikoble1926/sanctions-check-before-pay](https://github.com/Nikoble1926/sanctions-check-before-pay)

More about the service: [https://x402.nsgoods.org](https://x402.nsgoods.org)
