x402-payguard

PayGuard: paid x402 OFAC SDN sanctions screening for crypto addresses (entity, programs, list date, chain + checksum) + MCP server

Browse source · x402-payguard.tar.gz · x402-payguard.bundle
working-tree snapshot on branch main · 03a3cf34468ef69136ec45bf5ff91d5aabfeb001
Clone: git clone https://agentgate-source.pages.dev/x402-payguard/x402-payguard.git (branch main, commit 03a3cf34468ef69136ec45bf5ff91d5aabfeb001) or curl -O https://agentgate-source.pages.dev/x402-payguard/x402-payguard.bundle && git clone x402-payguard.bundle x402-payguard
Home: this repo is hosted here (it was never on GitHub).

PayGuard: OFAC sanctions screening for crypto addresses (MCP server + x402 API)

Start here: copy-paste MCP configs for Claude, Cursor and Windsurf, x402 client code, AgentKit and LangChain wrappers: https://agentgate-source.pages.dev/start/
API: https://api.youraitools.co/payguard · fallback (same Worker): https://payguard.agentgate.workers.dev · npm: @agentgate-builder/payguard

OFAC SDN sanctions screening for crypto addresses, for AI agents and payment flows, paid per call over x402. One call reports whether an address appears in the US Treasury OFAC Specially Designated Nationals (SDN) list, and if so which entity, which sanctions programs and since when.

Informational screening against public OFAC SDN data. Not legal advice and not a compliance program.

Why

Agents are starting to send money: x402 payments, payouts, refunds, bounties. Paying an address that OFAC has designated is the one mistake that is both easy to check and expensive to get wrong. OFAC publishes the addresses it designates ("Digital Currency Address - ETH / XBT / TRX / USDT ..." identifiers in the SDN list); PayGuard turns that file into a fast exact-match lookup with the context a human reviewer needs.

Quick start

# Free preview (the sample is a Lazarus Group address from the SDN list; the second is the USDC contract on Base)
curl -s "https://api.youraitools.co/payguard/v1/preview?address=0x098B716B8Aaf21512996dC57EB0615e2383E2f96"
curl -s "https://api.youraitools.co/payguard/v1/preview?address=0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"

# Paid: answers 402 with x402 terms; any x402 client pays it
curl -i "https://api.youraitools.co/payguard/v1/address?address=0x098B716B8Aaf21512996dC57EB0615e2383E2f96"
import { wrapFetchWithPaymentFromConfig } from '@x402/fetch';
import { ExactEvmScheme } from '@x402/evm';
import { privateKeyToAccount } from 'viem/accounts';

const pay = wrapFetchWithPaymentFromConfig(fetch, {
  schemes: [{ network: 'eip155:8453', client: new ExactEvmScheme(privateKeyToAccount(process.env.BUYER_KEY)) }],
});
const r = await (await pay('https://api.youraitools.co/payguard/v1/address?address=' + encodeURIComponent(recipient))).json();
if (r.sanctioned) holdForReview(recipient, r.matches);

Response (GET /v1/address)

{
  "address": "0x098b716b8aaf21512996dc57eb0615e2383e2f96",
  "sanctioned": true,
  "match_count": 1,
  "matches": [{ "entity": "LAZARUS GROUP", "entity_type": "Entity", "sdn_uid": 27307, "programs": ["DPRK3"],
                "list_date": "2019-09-13", "listed_currency": "ETH", "listed_as": "Digital Currency Address - ETH" }],
  "chain": { "format": "evm-hex20", "guess": "evm", "checksum": "eip55", "checksum_valid": true, "checksum_address": "0x098B716B8Aaf21512996dC57EB0615e2383E2f96" },
  "source": "US Treasury OFAC Specially Designated Nationals (SDN) list",
  "source_url": "https://sanctionslistservice.ofac.treas.gov/api/PublicationPreview/exports/SDN.XML",
  "list_updated_at": "2026-10-21",
  "list_age_days": 0,
  "sdn_address_count": 1050,
  "disclaimer": "Informational screening against the public OFAC SDN list ... Not legal advice. ..."
}

POST /v1/batch {"addresses": [...]} (1-50) returns count, sanctioned_count, any_sanctioned and one result per address.

MCP

{ "mcpServers": { "payguard": { "type": "http", "url": "https://api.youraitools.co/payguard/mcp" } } }
{ "mcpServers": { "payguard": { "command": "npx", "args": ["-y", "@agentgate-builder/payguard"] } } }

Tools screen_address (remote and stdio) and screen_addresses (stdio, up to 50).

Remote /mcp stdio (npm), default stdio with PAYGUARD_X402_PRIVATE_KEY
Cost free, 10 calls/min per IP free, offline, unlimited $0.001 per address, $0.01 per batch (USDC on Base, from your wallet)
Returns sanctioned true/false, match count, chain guess same, from the list snapshot bundled in the installed version (list_updated_at) full result: SDN entity, type, uid, programs, list date, checksum; list refreshed daily

The free tiers don't include entity, program or date details. Those come from the paid endpoint https://api.youraitools.co/payguard/v1/address, which every offline result points to (full_details).

Paid mode in the stdio server. Set PAYGUARD_X402_PRIVATE_KEY (or X402_PRIVATE_KEY) to the private key of a Base wallet holding a little USDC, and add the optional x402 client packages:

{ "mcpServers": { "payguard": {
  "command": "npx",
  "args": ["-y", "-p", "@agentgate-builder/payguard", "-p", "@x402/fetch", "-p", "@x402/evm", "-p", "viem", "payguard"],
  "env": { "PAYGUARD_X402_PRIVATE_KEY": "0x<key of a wallet with a few cents of USDC on Base>" } } } }

The server then pays the hosted API per call over x402 with @x402/fetch. Guards: Base USDC only, at most $0.01 per payment, and only to PayGuard's pay-to address. Invalid input is rejected locally and never paid for. If a paid call fails, you get the offline preview plus the error. Use a dedicated low-balance wallet: the key stays in your MCP client's config and is never logged or sent anywhere except as an x402 payment signature. PAYGUARD_API_URL overrides the endpoint (e.g. the workers.dev fallback).

Library

import { previewAddress } from '@agentgate-builder/payguard';
previewAddress('0x098B716B8Aaf21512996dC57EB0615e2383E2f96');
// { sanctioned: true, match_count: 1, chain: { guess: 'evm', ... }, tier: 'preview', full_details: 'https://api.youraitools.co/payguard/v1/address', ... }

Since 0.2.0 the npm package ships a lite snapshot (lookup keys and match counts only) and exports previewAddress, previewBatch, normalizeAddress and invalidInput. The full engine with entity details (src/screen.js) runs only in the hosted Worker. Before 0.2.0, the package bundled the full list and exported screenAddress.

Data and matching

Honest limits

Routes

Route Price
GET\|POST /v1/address $0.001 full screening result
POST /v1/batch $0.01 up to 50 addresses
GET\|POST /v1/preview free, 10/min/IP sanctioned true/false + chain guess
POST /mcp free, 10/min/IP remote MCP, screen_address
GET /v1/list, /v1/stats, /health free list date and counts, usage counters
GET /openapi.json, /.well-known/x402 free discovery

x402 v2 (PAYMENT-SIGNATURE) and v1 (X-PAYMENT) clients both work; only HTTP 200 responses are settled.

Develop

npm install
npm test                    # engine (real bundled list), x402 v2 + v1, proxy, CDP facilitator switch, MCP, stdio free + paid mode
npm run build:lite          # regenerate src/data/sdn-lite.js (also runs on prepack)
npm run build:sdn           # rebuild the list from Treasury
node scripts/injectscan-listing.mjs   # listing text through InjectScan's rules
npx wrangler deploy

MIT licensed.


agentgate-source: code mirror on Cloudflare Pages. All repos