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
What the check does
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 one rule that matters: fail closed
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:
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.
Run it
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:
- With
@x402/axios, the payment happens when the first 402 comes back as an error. If you setvalidateStatus: () => trueon the paying client, the 402 counts as a success and the client never pays. The example usesvalidateStatus: s => s !== 402, so 400 and 5xx still do not throw. - The Python x402 EVM signer needs
web3installed (thex402[evm]extra). Without it, the import fails before anything runs. Therequirements.txtin the repo pins it.
Verify the answer yourself
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.
Limits
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
More about the service: https://x402.nsgoods.org
Top comments (0)