x402

A machine can buy something without an account.

HTTP has had a status code for "Payment Required" since 1997. Nobody could use it, because there was no way to settle money inside a single request. There is now. Ask our gateway for something priced and it answers 402 with a list of ways to pay. Pay on any chain you like, retry with two headers, get your goods.

Every figure below was measured against the live gateway on 2026-08-24, or read out of the shipped source. Timings are medians over repeated runs from one client, not best-case samples. Nothing on this page is illustrative.

155ms

median to answer a payment challenge, measured live

5

chains verified directly — no third party in the loop

66

protocols the gateway serves and documents itself

$0

to create a sellable link — no account, no database row

Run it against production, right now

Nothing below is recorded. Press a button and it fetches from pay.one.ie — open your network tab first if you want to watch it happen.

Live

Nothing has been fetched yet. Press the button and what appears is the response your browser just received.

Three real calls: the paywall challenge, the rate board every quote derives from, and the gateway's own agent — which holds the same payment tools an outside agent would call.

What you just pressed

  1. HTTP 402 Payment Required

    The status code itself is the product. It was reserved in 1997 and left unimplemented because there was no way to settle money inside one request.

  2. The four x-payment-* headers

    A client that reads only headers has enough to pay. The JSON body repeats the same facts for one that prefers to parse.

  3. Ways to pay, priced this second

    One row per chain, each with its own treasury address. The buyer picks; the server did not decide for them.

  4. The disabled Solana row

    Quoted but not payable, with the reason on the row. A rail we cannot verify is never presented as one we can.

155ms

Median to answer a payment challenge, 15 runs

5

Chains verified by reading their own node

66

Protocols behind one POST — 24 of them live

0

Cost and accounts needed to mint a sellable link

eyebrow="The handshake" title="Five moves, and nothing to install" subtitle="The whole protocol is a status code, a JSON body, and two request headers." steps=[object Object][object Object][object Object][object Object][object Object] />
# the entire client side of it
curl https://pay.one.ie/x402/demo \
  -H "X-Payment-TX:    <your transaction hash>" \
  -H "X-Payment-Chain: SUI"

What happens between the retry and the 200

Four checks, in order, all of them fail-closed. The code below is the shipped source verbatim; only its longer explanatory comments are trimmed.

// pay/backend/src/x402.ts

const verification = await verifyPayment(tx, chain, env)
if (!verification.ok) {
  return c.json({ error: 'Payment not verified', chain }, 402)
}

const rate     = (await getCryptoPrices(env))[chain] || 0
const valueUsd = verification.amount * rate

// Require at least 99% of target USD to account for price drift.
if (valueUsd < usdTarget * PAYMENT_TOLERANCE) {
  return c.json({ error: 'Payment amount too low', requiredUsd: usdTarget,
                  receivedUsd: valueUsd }, 402)
}

const replayKey = `x402:${new URL(c.req.url).pathname}:${chain}:${tx}`
if (await env.CUSTOMERS.get(replayKey)) {
  return c.json({ error: 'Payment already used', chain, tx }, 402)
}
await env.CUSTOMERS.put(replayKey,
  JSON.stringify({ at: Date.now(), sender: verification.sender }))

Note what is absent: no callback to a payment processor, no webhook to wait for, no pending state. The chain is the source of truth and it is read directly.

Verification

We read the chain ourselves

Most servers that accept machine payments cannot check them. They ask a third party — a facilitator — whether the money arrived, and take its word. We do not have one. Every payment is confirmed by fetching the transaction from that chain's own node.

Sui

SUI

The default destination for claims. A rejection verdict came back in about 80ms.

Verified live

Base

BASE

Native ETH or USDC at 0x8335…2913. Chain ID 8453.

Verified live

Ethereum

ETH

Native ETH or USDC at 0xA0b8…eB48.

Verified live

Bitcoin

BTC

Read straight from mempool.space — verdict in 317ms on the claim route. Not offered on the demo paywall, which takes four chains.

Live on claim

Solana

SOL

Quoted and offered, but verification is failing in production right now — cause and detail below.

Quote only
// pay/backend/src/x402.ts — one dispatcher, five chains, zero intermediaries

if (chain === 'SOL')                                  → verifySolanaTransaction(...)
if (['ETH','BASE','ARB','OPT'].includes(chain))       → verifyEvmTransaction(...)
if (chain === 'BTC')                                  → verifyBitcoinTransaction(...)
otherwise                                             → verifySuiTransaction(...)

Bitcoin is the unusual one — we know of no x402 facilitator that verifies it. Ours answers on the claim route in production; it is not one of the four chains the demo paywall offers.

The whole wire format

Six headers. Four the server writes, two the client sends back. There is nothing else to implement.

x402 headers

pay/backend/src/x402.ts

The headers ONE's 402 dialect writes and reads.
Prop Type Default Notes
X-Payment-Amount string optional How much, in the primary chain's own units. Omitted entirely on an open-priced link. pay/backend/src/x402.ts
X-Payment-Currency string optional The chain symbol of the primary option — not an ERC-20 contract address. pay/backend/src/x402.ts
X-Payment-Address string optional Where to send it. The seller's treasury, never ours. pay/backend/src/x402.ts
X-Payment-Chains string optional Comma-joined list of every chain this resource accepts. pay/backend/src/x402.ts
X-Payment-TX string required Sent by the CLIENT on the retry — the hash of the transaction it broadcast. pay/backend/src/x402.ts
X-Payment-Chain string required Sent by the CLIENT on the retry — which chain that hash lives on. pay/backend/src/x402.ts

Six pieces, one door

Almost everything reachable through a single POST to one URL. There is no SDK you are obliged to install.

The gateway

pay/backend/

A Cloudflare Worker at pay.one.ie. Emits the 402, verifies on five chains, signs coupons, runs escrow. Stateless — it keeps no ledger of its own.

The protocol registry

66 protocols

Every capability is a named protocol with a schema, reachable by one POST and documented at /protocol/{name} in prose a model can read.

The 402 middleware

x402.ts

Wraps any route in a paywall: challenge, verify, tolerance check, permanent replay guard. One demo route uses it today.

The SDK

@one-protocol/sdk

one.payment.quote() and one.payment.claim() over the same POST door, plus generic execute/discover/health.

The agent tools

34 tools

The same surface emitted as OpenAI and Anthropic tool definitions, so a model can pay without an integration written for it.

The contracts

Move + Solidity

A Sui package and an EIP-712 UniversalVerifier on EVM that redeem a signed coupon and burn its nonce. Testnet today.

Where to check any claim on this page

  • pay/backend/src
    • x402.ts
    • settlement.ts
    • escrow.ts
    • signer.ts
    • chains
      • evm.ts
      • sui.ts
      • solana.ts
      • bitcoin.ts
    • protocol
      • index.ts
      • handlers
        • x402.ts
        • payment-link.ts
    • routes
      • discovery.ts
      • links.ts
      • escrow.ts
      • status.ts
  • pay/contracts
    • sui (Move)
    • evm (Solidity)
  • one.ie/web/src/pages/api/x402
    • demo.ts

Not just this page

Four surfaces you can open right now

Each of these charges, quotes or derives against production. No sandbox, no fixtures. Every one was loaded anonymously while this page was written.

The registry

66 protocols, and an honest count of them

The gateway is not only a paywall. It registers 66 named protocols behind one POST, each with a schema and a description written for a model to read. But a count is a marketing number unless somebody says which ones actually work, so here is the breakdown by what we found reading every handler.

24
live — verified working against production
9
written and dark — they read testnet objects over a mainnet RPC
28
demo — in-memory, reset when the worker restarts
5
stubs — they return an id and persist nothing
2 Payments & x402 live Quote a price on seven chains; verify a real payment and get a signed coupon.
3 Payment links live Create, quote and claim a signed self-contained link. No database row anywhere.
8 Agent onboarding live An agent signs itself up, proves it owns its wallets, and gets a session token. All eight wired.
2 Prices live Live spot rates and conversion for ten assets, straight from Coinbase.
3 Identity live Derive deposit addresses across four chains from a public key. Pure crypto, no state.
2 Faucet live Real testnet dispensing on Sui and Solana, rate-limited to one per address per day.
2 Signed access live Mint and verify a time-limited HMAC URL for gated content.
2 Token gating dark Reads holdings across three chains against a threshold — but the contract it reads is not on mainnet, so it always answers zero.
5 Credits & subscriptions dark Complete implementations pointed at Sui objects that exist on testnet only.
2 Merchant stats dark Same cause. One of the two also hardcodes its volume figures.
28 Groups & tokenisation demo A six-dimension ontology and a learn-to-earn token, both running on in-memory maps that reset with the isolate.
5 Commerce stub Mint an id and a payment link, persist nothing. Gas funding funds nothing.

A note on the count the gateway reports about itself: its discovery response groups only 59 of the 66 into categories, so seven registered and callable protocols — the faucet, staking and wallet-registration family — are invisible in its own index.

The guards

Where the money is, the boring parts matter

A transaction is spent exactly once

Every accepted payment writes a replay key of (path, chain, transaction) with no expiry. A spent transaction is spent permanently — you cannot buy once and replay the receipt tomorrow.

A gateway that cannot count refuses to sell

If the replay store is unreachable the server returns 503 before it ever quotes an address. It will not take money it cannot account for. That is the harder direction to fail in, and the correct one.

Fake tokens do not clear

An ERC-20 transfer is only accepted if the emitting contract is on the whitelist for that exact chain — built from the same object that supplied the RPC endpoint, so the endpoint and the token address can never disagree.

The network cannot be half-set

One switch picks mainnet or testnet as a whole profile. An RPC override pointing at the wrong network is refused, not served — so a misconfigured deploy cannot report "not found" for money that actually moved.

Price drift is absorbed, not exploited

Quotes carry a 1% buffer and settlement accepts 99% of the target. A payment does not fail because a rate moved between the quote and the block.

The payer is the payer

A claim is bound to the address that actually sent the funds on-chain, never to whatever address the caller typed into the request body.

title="Where this wins, and where it plainly does not" subtitle="Stripe is better than this at almost everything a human buyer needs. The column that matters is the one where the buyer is a program." usLabel="x402" alt1Label="Card checkout" alt2Label="Exchange transfer" rows=[object Object][object Object][object Object][object Object][object Object][object Object][object Object][object Object][object Object][object Object] />

Selling

One link. Two audiences. No database.

This is the part worth understanding, because it is not how payment links usually work. A ONE pay link is not a row in our database that you look up. The link is the offer — the price, the chains, and your payout addresses, encoded into the URL and signed. We store nothing. And the same URL behaves differently depending on who asks for it.

A person opens it

A checkout page

The link renders a hosted payment card — your product name, the price, the chains you accept. Measured live: 25KB of HTML, 223ms median over 8 runs.

An agent opens it

An HTTP 402

Send Accept: application/json and the identical URL returns a 402 with the price list and the instructions to redeem. Measured live: 216ms median over 8 runs.

# Create something sellable. No account, no key, no dashboard.
curl -X POST https://pay.one.ie/ -H 'Content-Type: application/json' -d '{
  "protocol": "payment_link_create",
  "data": {
    "merchantSlug": "acme",
    "amount": 2500, "unit": "usd",
    "product": "Consulting hour",
    "chains": ["SUI","BASE"],
    "treasuries": { "SUI": "0x90…", "BASE": "0x9e35…" }
  }
}'

# → { "url":      "https://pay.one.ie/l/eyJhIjoxMDAsImMiOls…<signature>",
#     "short":    "https://pay.one.ie/s/<8-char-code>",
#     "linkHash": "0x3ed4fc957dc20d8c",
#     "decoded":  { … the whole offer, readable, signed … } }
#
# Server time: 0 ms. Nothing was written to a database, because there is no row.

The money never touches us

Buyers pay the treasury addresses you put in the link. Funds go from their wallet to yours. We read the chain to confirm it happened — we do not stand between you and the payment, and we never hold a key of yours.

It refuses to mint without your address

Name a chain but give no payout address for it and the link is simply not created. It used to fall back to a default treasury. That was quietly wrong, so it was replaced with a refusal.

Escrow, when trust is missing

For deals where neither side wants to go first, the gateway can hold funds at a generated address with a 30-minute window, then forward or refund. Create, poll, forward, refund — four endpoints.

The transaction

A coupon, redeemed on-chain

When a payment on one chain has to grant something on another, the gateway signs an EIP-712 coupon after verifying the payment, and the buyer spends that coupon against a contract. The payment transaction hash is the nonce, so one payment can never mint twice. Here is one that was actually redeemed.

Verified claim

Base Sepolia — testnet
tx      0x90d503966a90a7db05f4d80638b52e02f07de0e0057b32c46749225d5e930e4b
status  0x1  (success)
block   43772965
to      0x40cBF1C4963Db6cDB82D3e09A9cF46ac12F15C29   (UniversalVerifier)
result  balanceOf = 1000 · coupon nonce burned

Re-read from a Base Sepolia node on 2026-08-24 while writing this page, not copied out of a changelog. Check it yourself against any Base Sepolia RPC.

This is a testnet transaction and we label it as one. The next section says exactly which halves of this run on mainnet today and which do not.

What we're honest about

The envelope is missing. The hard part is done.

Everything above is live and reproducible. Five things are not, and a page that listed only the working parts would be a worse page.

We speak a dialect, not the standard

The published x402 specification has its own wire format — a PaymentRequirements body, and an X-PAYMENT header carrying a signed authorization. We use our own headers and take a transaction hash instead. The idea is identical; the envelope is not, so a client written against the standard cannot pay us today. Closing that gap needs nobody's permission, and it is first on the list.

Verification is on mainnet. Minting is not.

Confirming a payment works against live mainnet chains. The contracts that redeem a coupon into tokens are testnet deployments. We checked while writing this page: the EVM verifier address the gateway advertises holds no code on Base mainnet, and the Sui package exists on testnet and not on mainnet. Reading money is live; minting against it is not.

Solana is quoted but cannot be verified

The challenge offers Solana and the quote endpoint prices it, but verification fails. Probing the live gateway for this page turned up the cause: api.mainnet-beta.solana.com answers our worker with 403 — your IP or provider is blocked. Solana's public RPC does not serve Cloudflare's egress. On the claim route that surfaces as a clean error; on the demo paywall it surfaces as a 500. Ethereum, Base, Sui and Bitcoin all answer correctly. Do not pay us in SOL until this paragraph is gone.

The seller's path has gaps, and they are in odd places

Setting a payout address works from Settings, but only for Sui and Ethereum-family chains — Solana and Bitcoin can be read as payout targets and cannot be entered anywhere in the product. The link builder at /w/shop assembles a URL client-side, so what it produces is unsigned and carries none of the metadata the settlement webhook uses to attribute a payment. Both are real gaps between "the rail works" and "an operator can use the rail without help."

An agent cannot discover what is for sale

The gateway advertises its 66 protocols well. It does not advertise priced resources. A buying agent has to be handed a link out of band — there is no catalogue to browse. And our own SDK can serve a 402 but cannot yet answer one automatically.

Try it

Four commands, no signup

Paste these anywhere. The gateway is open, unauthenticated, and describes itself — because the client reading it is often a model that has never seen our documentation.

the whole handshake
$ curl -i https://pay.one.ie/x402/demoHTTP/2 402x-payment-chains: ETH,BASE,SOL,SUI{ "status": "payment_required", "amountUsd": 0.1, … }$ curl https://pay.one.ie/x402/demo \$   -H "X-Payment-TX: <hash>" -H "X-Payment-Chain: SUI"HTTP/2 200 — payment verified, content unlocked
# 1. What can this thing do?
curl https://pay.one.ie/
#    → 66 protocols, and a prompt written for a language model to read

# 2. Explain one of them
curl https://pay.one.ie/protocol/x402_quote
#    → input schema, error codes, and a worked example response

# 3. Price something
curl -X POST https://pay.one.ie/ -H 'Content-Type: application/json' \
  -d '{"protocol":"x402_quote","data":{"amount":10,"chain":"BASE","currency":"USDC"}}'
#    → { "totalUsd":0.1, "payAmount":0.1, "paymentCurrency":"USDC",
#        "usdcContract":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
#        "treasury":"0x9e35…", "expiresAt":1787561511018 }

# 4. Get told to pay
curl -i https://pay.one.ie/x402/demo
#    → HTTP 402, four ways to pay ten cents

Every response carries a requestId and its own server-side latency, so you can hold us to the numbers on this page.

Questions

The gateway is open. Go and ask it something.

No key, no account, no sales call. One GET returns a price list; one POST returns a sellable link. Everything on this page was measured against the same endpoint you would call.

66 protocols · 5 chains verified directly · no signup to try it