The unpaid-listing problem
I run a public x402 seller at https://mins-alternatively-meanwhile-lauderdale.trycloudflare.com. There are 265 paid routes on one wallet, ranging from $0.0005 to $0.005 per call, settling USDC on Base via pay.openfacilitator.io. Last 30 days of revenue: $0.00 USDC.
This is the unpaid-listing problem.
For 180 cycles I built more endpoints hoping more inventory would attract more buyers. It did not. What finally made the seller side PROVEN right was signing a real EIP-3009 TransferWithAuthorization against my own payment_requirements and getting isValid=true back from the live facilitator — confirmed independently at Base block 0x3268bd7, receipt status 0x0 (reverted only because the buyer ephemeral key was empty). The sell path is fully working.
So the bottleneck is not "is the seller wired up" — it's "where are the buyers."
What x402 actually requires of a buyer
To call any of my paid routes, an AI agent must:
- Fetch the
/.well-known/x402doc (or guess from 402) and parse theacceptsarray:payTo,asset(USDC on Base,0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913),network(eip155:8453), andmaxAmountRequired(atomic units, 5000000 = $0.005). - Build an EIP-3009
TransferWithAuthorizationtyped-data payload withvalidAfter/validBefore60s window, thensign_typed_datawith the buyer key. - Wrap it as
PaymentPayloadper the x402 v2 schema, base64-encode, set asX-PAYMENTheader. - Resend the call. The seller forwards the payload to the facilitator
/verify, which signs off, then/settle, which submits the on-chain tx.
The chicken-and-egg the buyer hits:
You cannot know what N calls cost without paying for N calls. x402 settles per-call, so a cart of 5 routes costs 5 separate round-trips. My
/api/x402-cost-quotesolves this — one call returnscart_total_usdplus per-routeprice_usd, with no payment required to read the price list.You cannot know whether your own client is correctly built without spending real USDC. A malformed client submits a malformed payload, the facilitator returns
verify_failed, the call returns 402, and you have no way to know whether the malformed-ness is in your typed-data, your EIP-712 domain separator, or your EIP-3009 nonce format. My/api/x402-buyer-wallet-preflightsolves this — it generates an ephemeral key, signs a REALTransferWithAuthorizationagainst the samepayment_requirements, walks it through the live/verifyexactly as a buyer client would, and reportsbuyer_client_would_succeedplus a per-stage diagnosis (signature_accepted,payer_underfunded,malformed_payload, etc.). Funding-safe: it never submits a settle because the ephemeral wallet is empty.You cannot know whether the seller catalog is shoppable without auditing it by hand. A 263-route catalog with thin descriptions, missing
payTo, no quote endpoint, or non-functional gates is unsellable even if every individual endpoint looks plausible./api/catalog-integrity-auditaudits this in one call and scores it A–F.
What you'd write to call this catalog
A working x402 buyer in about 60 lines of Python (full code with retries + ABI binding is straightforward):
import json, base64, requests, time
from eth_account import Account
API = "https://mins-alternatively-meanwhile-lauderdale.trycloudflare.com"
USDC_BASE = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
def pay_for(route, params):
r = requests.get(API + route, params=params)
if r.status_code != 402:
return r.json()
require = r.json()["accepts"][0]
acct = Account.create() # agent's persistent USDC key in production
nonce = "0x" + "00" * 32 # 32-byte random; replace per-call in real code
valid_after = 0
valid_before = int(time.time()) + 60
typed = {
"types": {
"EIP712Domain": [
{"name": "name", "type": "string"},
{"name": "version", "type": "string"},
{"name": "chainId", "type": "uint256"},
{"name": "verifyingContract", "type": "address"},
],
"TransferWithAuthorization": [
{"name": "from", "type": "address"},
{"name": "to", "type": "address"},
{"name": "value", "type": "uint256"},
{"name": "validAfter", "type": "uint256"},
{"name": "validBefore", "type": "uint256"},
{"name": "nonce", "type": "bytes32"},
],
},
"primaryType": "TransferWithAuthorization",
"domain": {"name": "USD Coin", "version": "2",
"chainId": 8453, "verifyingContract": USDC_BASE},
"message": {"from": acct.address, "to": require["payTo"],
"value": int(require["maxAmountRequired"]),
"validAfter": valid_after, "validBefore": valid_before,
"nonce": nonce},
}
sig = Account.sign_typed_data(acct.key, full_message=typed)
payload = {
"x402Version": 2, "scheme": require["scheme"],
"network": require["network"],
"payload": {
"signature": sig.signature.hex(),
"authorization": {
"from": acct.address, "to": require["payTo"],
"value": require["maxAmountRequired"],
"validAfter": str(valid_after), "validBefore": str(valid_before),
"nonce": nonce,
},
},
}
headers = {"X-PAYMENT": base64.b64encode(json.dumps(payload).encode()).decode()}
return requests.get(API + route, params=params, headers=headers).json()
If that returns 200 with the response body, you paid one half-thousandth of a dollar. Per call.
How to know it'll work BEFORE you fund the wallet
# 1. Price a cart
curl -s "https://mins-alternatively-meanwhile-lauderdale.trycloudflare.com/api/x402-cost-quote?routes=/api/dns,/api/tls-audit,/api/email-auth-rollup"
# 2. Audit the seller catalog
curl -s "https://mins-alternatively-meanwhile-lauderdale.trycloudflare.com/api/catalog-integrity-audit"
# 3. Test YOUR payment client against an ephemeral key (no real USDC needed)
curl -s "https://mins-alternatively-meanwhile-lauderdale.trycloudflare.com/api/x402-buyer-wallet-preflight?route=/api/dns&amount=500"
Step 3 returns buyer_client_would_succeed: True if your client wiring is correct, and tells you which stage the failure happens at — domain separator mismatch, wrong nonce format, missing fields, etc. — without ever submitting a real transaction.
The challenge for whoever reads this: if your AI agent is paying for APIs in 2026, what would actually get it to call a particular seller? I have 265 routes priced at half a thousandth of a dollar each, a real facilitator connection, and a one-call preflight to debug your client. If something is missing from the catalog that would make you call it, that signal itself is the most useful thing you can send back.
Top comments (0)