ONE x402

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 it offers, retry with the transaction digest, get your goods. On mainnet, with real money.

Mainnet since 2026-11-12: a payment here is real money. Most figures were measured on testnet on 2026-11-08 and say so, or were read out of the shipped source; timings are medians over repeated runs from one client. Nothing on this page is illustrative.

~190ms

median for a payment challenge, measured on testnet from Bangkok on 2026-11-08

3

chains offered today on mainnet, each checked on its own RPC

36

protocols behind one POST, 18 of them documented in its index

$0

to create a sellable link — no account needed

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 chains that are missing

    Solana and Bitcoin are not in the list. A rail we cannot check is never presented as one we can, and the network is said out loud.

~190ms

Median to answer a payment challenge, 9 runs on testnet, 2026-11-08

3

Chains offered today on mainnet, each read from its own RPC

36

Protocols behind one POST — 18 documented

0

Cost and accounts needed to mint a sellable link

The handshake

Five moves, and nothing to install

The whole protocol is a status code, a JSON body, and two request headers.

1

GET

A buyer asks for something priced

No key, no signup, no session. The buyer can be a person, a script, or an agent that has never heard of us.

2

402

The server answers with a price list

Chain, currency, exact amount, address — every acceptable way to pay, in the response body. Nothing is negotiated.

3

PAY

The buyer pays on whichever chain suits them

An ordinary transaction they sign and broadcast themselves. No wallet popup, no redirect, no custody of anyone's keys.

4

RETRY

Same URL, two headers

X-Payment-TX and X-Payment-Chain. That is the entire client-side protocol — two headers a curl command can set.

5

200

The server reads the chain and serves

It fetches the transaction from that chain's own RPC, checks the recipient, the value and the replay guard, and returns the goods.

# 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, with its longer comments and the unreachable-RPC branch 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 rates    = await getCryptoPrices(env)
const valueUsd = valueUsdOf(verification.amount, verification.currency, chain, rates)
if (valueUsd === null) {
  return c.json({ error: 'Payment currency cannot be valued', chain }, 402)
}

// 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 RPC.

Sui

SUI

Sui mainnet. On 2026-11-12 agent one bought $1 of credits here with its own key: 1.23 SUI, final in 571 ms, 850 credits. The first agent buy was on testnet (walk 8, 2026-11-07), final in 1.27 s.

Offered · mainnet

Base

BASE

Base mainnet (chain 8453): native ETH, or USDC at 0x8335…2913.

Offered · mainnet

Ethereum

ETH

Ethereum mainnet: native ETH or USDC.

Offered · mainnet

Bitcoin

BTC

The verifier is built and reads mempool.space. Not offered for paying ONE: the platform Bitcoin treasury is not proven, so x402_quote refuses BTC by name. A seller's own Bitcoin address still works on a pay link.

Not offered

Solana

SOL

Not offered. Solana's public RPCs refuse our worker, and a USDC transfer on Solana cannot be read the way the verifier reads balances. x402_quote refuses SOL by name.

Not offered
// pay/backend/src/x402.ts — one dispatcher, no intermediaries

if (chain === 'SOL')                             → verifySolanaTransaction(...)
if (['ETH','BASE','ARB','OPT'].includes(chain))  → verifyEvmTransaction(...)
if (chain === 'BTC')                             → verifyBitcoinTransaction(...)
if (chain === 'FETCH')                           → verifyFetchTransaction(...)
if (chain === 'SUI')                             → verifySuiTransaction(...)
anything else                                    → refused, never guessed

A verifier existing is not the same as a chain being offered. Today the paywall offers ETH, BASE and SUI on mainnet. Solana and Bitcoin have verifiers and are not offered to buyers paying ONE.

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: ONE's platform treasury for that chain. A pay link names the seller's own address instead. 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. (one.ie's credits door takes it in the body as tx_digest instead.) 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, on mainnet since 2026-11-12. Emits the 402, reads Sui, EVM chains and Bitcoin from their own RPCs, signs coupons, runs escrow. It keeps no ledger of payments of its own.

The protocol registry

36 protocols

Every capability is a named protocol with a schema, reachable by one POST; the 18 with written docs are listed at / and explained 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. On testnet only: pay.one.ie/status says neither is on mainnet yet.

Where to check any claim on this page

  • pay/backend/src
    • x402.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
      • verify.ts
  • pay/contracts
    • sui (Move)
    • evm (Solidity)
  • one.ie/web/src/pages/api
    • x402.ts
    • x402
      • demo.ts
      • gate.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

36 protocols, and an honest count of them

The gateway is not only a paywall. It registers 36 named protocols behind one POST, each with a schema. A count is a marketing number unless somebody says which ones work, so here is what answered on production on 2026-11-07 and 2026-11-08, and what nobody called.

7
live — answered correctly on production on 2026-11-07 or 2026-11-08
18
documented in the gateway's own index at pay.one.ie/
24
not re-checked for this page — built, but nobody called them
5
stubs — they return an id and persist nothing
1 x402 quote live Price an amount on ETH, BASE or SUI. SOL and BTC are refused by name.
2 Payment link create + quote live Mint a signed self-contained link that pays the seller's own address, and price it live.
2 Prices live Live spot rates and conversion for ten assets, from Coinbase.
2 Faucet live Testnet SUI for an address, one ask per address per day; ONE's treasury pays when the public faucet is down (paid in the agent walks of 2026-11-07).
2 Claims not re-checked x402_claim and payment_link_claim: verify a real payment, then sign a coupon or mark the link used. They need a fresh payment to call.
7 Agent sign-up not re-checked An agent proves it owns its wallets and gets a session token; the old one-call onboard is retired.
1 Spend cap not re-checked Register an on-chain spending ceiling for an agent.
3 Identity not re-checked Derive deposit addresses from a public key. Pure crypto, no state.
4 Signed access & gating not re-checked Mint and verify a time-limited signed URL; read holdings against a threshold.
5 Credits & subscriptions not re-checked Read the on-chain credits ledger and subscriptions on the testnet Sui package.
2 Merchant not re-checked A merchant's config and stats.
5 Commerce stub Mint an id or a link and persist nothing. Gas funding funds nothing.

A note on the count the gateway reports about itself: its index lists only the 18 protocols that carry written docs. The other 18 — the faucet, agent sign-up, payment links and commerce among them — are registered and callable but absent from that list.

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, and today it says mainnet. 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.

Where this wins, and where it plainly does not

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.

Featurex402Card checkoutExchange transfer
Buyer needs an account
Seller needs an account
Works for a program with no human
Settles inside one request
Viable at ten centsCard interchange has a fixed floor; a 30c fee on a 10c sale is not a business.
ChargebacksA finality nobody can reverse cuts both ways — it protects the seller and removes the buyer's recourse.
RefundsEscrow can refund inside its window. A direct settled payment cannot be pulled back.
Buyer protection
Custody of your moneyOn a pay link the buyer pays the seller's own address. The x402 demo and ONE's credits pay ONE's treasury, because ONE is the seller there.Your walletProcessorExchange
Time to first sellable linkOne POSTOnboarding + reviewAccount + KYC

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. The short code is only a pointer to it. 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. Re-checked 2026-11-08: about 30KB of HTML.

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. Re-checked 2026-11-08.

# 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 … } }
#
# The long URL carries the whole signed offer.

By default 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. ONE holds a sale only if you switch escrow on.

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 an address it generates, then forward or refund. Create, poll, forward, refund — four endpoints. While funds sit there, the gateway holds the key to that address; that is a stated decision.

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 RPC 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 coupon contracts are not on mainnet yet; the next section says what that means.

Payments themselves run on mainnet. Two receipts from 2026-11-12: agent one bought $1 of credits with its own key, 1.23 SUI, final in 571 ms, 850 credits (Suiscan). A 0.10 USDC agent send was final in 525 ms (Suiscan).

What we're honest about

The envelope is missing. The hard part is done.

Everything above is live and reproducible, on the network each part names. 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 x402 v2 standard has its own wire format — a PAYMENT-REQUIRED challenge, and a PAYMENT-SIGNATURE header carrying a signed authorization the server settles. We use our own X-Payment headers and take a transaction digest after the buyer has paid. The idea is identical; the envelope is not, so a client written against the standard cannot pay us today. The design to close it is written; it is not built.

The coupon contracts are still on testnet

The gateway and one.ie run on mainnet since 2026-11-12, and pay.one.ie/status says so. Checking payments and granting credits there is real money. The coupon contracts are not on mainnet yet; the status page says so too. On Sui mainnet only the spend-cap package is published, 0xd2bd9146732b0fbe436aa111981f94b22b6c51485d04a566788e685535548930. Agents still practise on testnet on purpose, with the faucet.

Solana and Bitcoin are not offered

Solana's public RPCs answer our worker with 403 — your IP or provider is blocked, and a USDC transfer on Solana cannot be read the way our verifier reads balances. So the challenge does not offer it, and x402_quote refuses SOL by name. Bitcoin's verifier works, but the platform Bitcoin treasury is not proven, so it is not quoted for paying ONE either. A seller's own Bitcoin address still works on a pay link.

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 buys from ONE, not yet from another agent

On 2026-11-07 an agent bought its own credits with test SUI and spent them on a model call, with no person in the loop. On 2026-11-12 agent one bought $1 of credits on mainnet with its own key. Buying from another agent stops at grant_required until the workspace's owner, signed in, approves it. The gateway also does not advertise priced resources, so a buying agent has to be handed a link. Our SDK answers a 402 with the buyer's own signer on pay.one.ie; one.ie's credits door takes the digest in the body, which the SDK does not send yet.

Try it

Four commands, no signup

Paste these anywhere. The gateway is open, unauthenticated, on mainnet, 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,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/
#    → 18 documented protocols, and a prompt written for a language model

# 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":"0x18cA…080E", … }   (Base mainnet)

# 4. Get told to pay
curl -i https://pay.one.ie/x402/demo
#    → HTTP 402, three ways to pay ten cents (ETH, BASE, SUI)

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. Mainnet, real money. Everything on this page was measured against the same endpoint you would call.

mainnet · 3 chains offered · no signup to try it