I wanted to see if an agent could pay to send me a message.

Not a human filling out a contact form. Not a CAPTCHA. An agent with a wallet, a task, and a desire to reach me.

I am not unique. I am simply a meat stick a computer might want to say something to.

A human can reach me on LinkedIn or X whenever they want, as outlined on the contact page.

If I put a contact endpoint on this site, I get a new inbox to monitor. Spam, bots, cold outreach. A CAPTCHA gates humans. A payment gates everyone, but credit cards gate humans too. I needed something native to how agents work.

x402 is that gate. It's a protocol built on HTTP 402 Payment Required, the status code the internet reserved for payments but never defined. x402 defines it. A server returns 402 with a price and a stablecoin address. The agent reads the response, signs a permit with its wallet, retries with a payment header, and the server settles on chain. No checkout page. No form. No account. Just a status code and a signature.

That's a terrible experience for a person. It's a great experience for an agent.

How it works

The endpoint lives at /contact/send on this site. An agent discovers it through the OpenAPI spec or agents.md. The first POST has no payment header. The server returns a 402 with the price, accepted assets, and a schema describing what fields to include. The agent doesn't need documentation beyond the response itself.

sequenceDiagram
    participant Human as Human
    participant Agent as Agent
    participant Site as benmilne.com
    participant Facilitator as x402 Facilitator
    participant Chain as Blockchain
    participant Admin as Admin Panel

    Human-->>Agent: "Contact Ben Milne about X"
    Agent-->>Site: POST /contact/send (no payment)
    Site-->>Agent: 402 + price + assets + field schema
    Agent-->>Agent: Sign stablecoin permit
    Agent-->>Site: POST + PAYMENT-SIGNATURE + message body
    Site-->>Facilitator: Verify signature
    Facilitator-->>Site: Valid
    Site-->>Facilitator: Settle
    Facilitator->>Chain: On-chain transfer (SBC)
    Chain-->>Facilitator: Tx hash
    Facilitator-->>Site: Settlement confirmed
    Site-->>Site: Store message in D1
    Site-->>Agent: 200 + receipt with tx hash
    Admin-->>Admin: Message appears with block explorer link

    rect rgba(200, 120, 50, 0.08)
    Note over Human,Admin: Future: callback URL enables two-way communication
    end

The Stable Coin Company operates a facilitator that verifies the agent's signed permit and executes the on-chain transfer, paying gas on behalf of both parties. The agent doesn't need to hold ETH or SOL. It holds one asset, the one it's paying with.

Getting going

I wrote a small library called x402-payment-path for this. It handles the 402 challenge, signature verification, settlement, and receipt generation.

The protocol underneath is x402, the stablecoin-native payment standard that Coinbase open sourced and the x402 Foundation now maintains as a Linux Foundation project. The Stable Coin Company runs the facilitator, which supports both USDC and SBC on Base, Solana, and Radius. I used SBC because it's the asset I know best.

If you wanted to implement this for a product instead of a message, you'd change what happens after payment. Instead of storing a message, you'd generate a download URL, trigger a webhook, or initiate a shipment. The library doesn't care what the action is. It gates the action behind payment.

Decisions

The endpoint accepts both USDC and SBC. I used SBC because it's the asset I know best and the one I wanted to verify end to end on both chains.

I have two wallets. My EVM wallet receives SBC on Base. A separate Solana wallet receives SBC on Solana. Same endpoint, same price, different settlement rails.

Messages land in a D1 table. I read them in the admin panel. No email forwarding, no inbox to monitor. A message shouldn't be able to do anything except be a message.

The price is $1. High enough to make spam uneconomical. Low enough to be trivial for a legitimate agent.

It works

Here are real transactions from June 13, 2026. Each message cost $0.10 in SBC. Settlement was on chain.

SenderNetworkAmountTransaction
cursor-agent/0.46.2Base$0.10 SBCBaseScan
claude-research-agent/1.2Base$0.10 SBCBaseScan
e2e-test-agentBase$0.10 SBCBaseScan
e2e-test-agentSolana$0.10 SBCSolscan
Admin panel showing x402 agent messages with sender, message body, network, and transaction hash links to BaseScan and Solscan

That's my admin panel. Seven messages. Base and Solana. Each one settled on chain before it showed up.

Sending me a message

If you're building an agent, the endpoint is POST benmilne.com/contact/send. It's documented in the OpenAPI spec and agents.md.

POST with no payment header and the server returns a 402 with everything you need. The x402 client library handles signing and retry from there. The price is $1. SBC on Base and Solana. If you include a callback URL, I'll have the ability to respond at that URL in the future.

What I learned

The 402 response is a machine-readable invoice. The agent doesn't need UI. It needs a price, an asset address, and a network. It signs, it pays, it retries.

I tried adding USDC support through community facilitators that advertise themselves as keyless. None of them worked permissionlessly. One required address registration. Another had version incompatibility. SBC through The Stable Coin Company was the only facilitator that worked without any setup at all. No keys, no accounts, no registration. That matters if you want agents to be able to pay without a human configuring something first.

One thing that also surprised me: both MetaMask and Phantom kept hiding SBC in my wallet, even after I approved it multiple times. I'd add the token, confirm the transaction, check my balance, and the asset would be gone again. This happened repeatedly during testing. I have nothing but respect for both teams, but having to tell my wallet on my own device that I wanted to use my own asset from a regulated issuer, over and over, felt ridiculous. The internet is big. Wallets should trust the choices their users have already made.

The code I used for this is open source and you're free to do whatever you want with it, presuming MetaMask or Phantom don't want to influence what you do instead.