Open a tab for your agent.

Deposit USDG on Robinhood Chain and mint a key with a spending limit. Your agent pays x402 endpoints from the tab. It never holds the money.

The limits live in the contract, so a facilitator can refuse a payment but never exceed what you set. The vault is live on Robinhood Chain.

The problem with giving an agent a wallet

An agent that pays for things needs a key that can move money. The usual answer is a wallet with a small balance, and the balance is the only limit.

Agents now buy things by the request: a quote, a page of research, a model call, another agent's time. x402 made that simple on the wire. An endpoint answers 402, the client retries with a signed payment, and a facilitator settles it on chain. What it did not solve is where the money sits while the agent runs.

If the agent's own wallet holds it, a prompt injection, a leaked environment variable or a bad loop can spend the whole balance in one burst. If you top it up in small amounts you spend your day topping up. If a provider holds prepaid credits, the agent can only buy from that provider.

A tab is the third option. Your wallet keeps the money in a contract. The agent gets a key, and the key gets a policy: the most it may pay per request, the most it may pay per day, optionally who it may pay, and when it expires. Every payment is checked against that policy by the contract itself, before any token moves. The facilitator can say no. It cannot say more.

The agent never sees your wallet key. A leaked agent key costs at most one day's cap until you revoke it, which is one transaction. Withdrawing is always yours and never the agent's.

How a payment moves

Ten messages between five parties. Only the first one is yours. The rest happen inside a single HTTP request.

Owner Vault Facilitator Seller Agent 01 deposit USDG, mint a key with a policy 02 GET /research 03 402, accepts tab-vault 04 retry with X-PAYMENT, signed by the key 05 verify(payment) 06 read policy, balance, nonce 07 isValid 08 pay(...) with the signature 09 USDG to the seller 10 200, X-PAYMENT-RESPONSE with the tx
  1. 01 Deposit Send USDG to the vault from your wallet. The balance stays yours and you can withdraw it at any time.
  2. 02 Mint a key Generate a key in the browser and set its policy: the most it may pay per request, its daily cap, an expiry, and optionally which addresses it may pay.
  3. 03 The agent pays When an endpoint answers 402, the agent signs the payment with its key and retries. The seller checks the signature and the policy with the facilitator, then serves the request.
  4. 04 Settle The facilitator submits the signed payment to the vault. The vault checks the policy again, moves the USDG to the seller, and marks the nonce so the same payment cannot run twice.

Watch one payment, byte by byte

A throwaway key is generated in your browser and signs a real payment against the live vault. Verify and settle are simulated on this page, because the throwaway key has no tab, and are marked as such.

The agent asks for a page of research.
POST /research HTTP/1.1
Host: research.example
Content-Type: application/json

{"q": "NVDA guidance, last two quarters"}

Press run. Each step prints what actually crosses the wire.

What a key can and cannot do

A key is an address with a policy. The policy is checked by the contract on every payment, not by a server you have to trust.

Size a policy
Calls the cap allows per day
2,000
Normal daily spend
3.00
Balance lasts, normal use
33 days
Worst case per day if the key leaks
20.00
Balance lasts, worst case
5 days

When something goes wrong

Each row is a thing that will happen eventually. The middle column is what the contract does about it without anyone acting.

EventWhat happens on its ownWhat you can do
The agent key leaksThe thief can spend at most the daily cap per day, only in the key's token, only to allowed payees if a list is set.Revoke the key. One transaction. Mint a new one.
The facilitator goes downSellers cannot verify, so requests get 402s. No balance moves. Nothing is lost.Any facilitator can take over. The owner or the seller can also call pay() themselves with the signed payment.
A seller takes payment and returns junkWith settle before serve, that one call is paid. The loss is bounded by max per payment.Drop the seller from the payee list or lower the per-payment limit. Sellers that settle after serve carry this risk themselves.
The balance runs outVerify fails with InsufficientBalance. The agent gets a 402 that says so and stops.Deposit more. Keys and policies are untouched.
The key expires mid-jobPayments fail with Expired. Work already paid for is unaffected.Set a new policy on the same key. The daily window carries over.
The same payment is submitted twiceThe second attempt reverts with NonceUsed. The seller is paid once.Nothing.
A seller quotes above the limitThe client refuses to sign. No request is sent, no fee is paid.Raise the limit for that key if the price is fair.

One package, both sides

The buyer wraps fetch. The seller wraps a route. Neither one needs an account with anybody.

import { createPayFetch } from 'tab'

const payFetch = createPayFetch({
  agentKey: process.env.AGENT_KEY,
  owner: '0xYourWallet',
  maxAmount: '250000',   // refuse anything over 0.25 USDG
})

const res = await payFetch('https://example.com/research', {
  method: 'POST',
  body: JSON.stringify({ q: 'NVDA guidance' }),
})
console.log(res.paid, res.payment.txHash)

A 402 is answered by signing with the key and retrying once. Anything else passes through untouched. The key is a plain private key, so the same wrapper works from a cron job, a Claude tool, or an MCP server.

import { createPaywall } from 'tab'

const wall = createPaywall({
  facilitator: 'https://tab.example/x402',
  payTo: '0xYourWallet',
  asset: USDG, vault: VAULT, chainId: 4663,
  price: '10000',        // 0.01 USDG per call
})

app.post('/research', wall.middleware(async (req, res) => {
  res.json(await research(req.body.q))
}))

The route settles before it runs by default, so the work is paid for before it starts. Set settle to after if you would rather serve first.

const wall = createPaywall({
  ...common,
  // price is computed per request: 0.002 USDG per
  // 100 characters of input, floor 0.005
  price: req => {
    const chars = Number(req.headers['content-length'] || 0)
    return String(Math.max(5000, Math.ceil(chars / 100) * 2000))
  },
})

Price can be a function of the request. The 402 states the exact amount, the client checks it against its own limit, and the signature covers that amount and nothing else.

const res = await fetch(url)
if (res.status === 402) {
  const { accepts } = await res.json()
  const offer = accepts.find(a => a.scheme === 'tab-vault')
  const payload = await signPayment({ agentKey, owner, ...offer })
  return fetch(url, { headers: {
    'X-PAYMENT': btoa(JSON.stringify({
      x402Version: 1, scheme: 'tab-vault',
      network: offer.network, payload,
    })),
  }})
}

The wire format is two JSON documents and one EIP-712 signature. A client in any language can produce it. The protocol page has every field.

Compared with the alternatives

Three ways to let an agent spend money, and what each one gives up.

Agent holds a walletPrepaid credits with one providerA tab
Who holds the moneyThe agentThe providerYou, in a contract
Per-payment limitNoneNoneYes
Daily capNoneNoneYes
Buys from any x402 sellerYesNoYes, when the seller supports the scheme
If the key leaksThe whole balanceThe creditsOne day's cap
Stop itSweep the wallet, if you canRotate the API keyRevoke, one transaction
WithdrawOnly if the agent cooperatesUsually notAny time

Questions

Is it custodial?
No. Funds move only against a signature from an active key, to the address named in that signature, within the policy. There is no admin function that moves user balances. The admin can set the fee, up to 5 percent, and nothing else.
What does the facilitator earn?
The vault fee, which is zero at launch. It is charged on top of the price so the seller always receives the full amount. The facilitator pays gas for every settlement, so a small fee will pay for that eventually, and it will be visible on chain.
Does the seller need to know about tab?
Yes, for now. Standard x402 middleware only knows the exact scheme, which needs EIP-3009 on the token. The tab middleware is a drop-in for Express-style servers, and the wire format is small enough to implement by hand.
Can a key pay another agent?
Yes. A seller is any address with an endpoint that answers 402. Two agents with tabs can hire each other, and both owners keep their limits.
Which tokens?
Any ERC-20 on Robinhood Chain. USDG is the default and the one the app page shows. A key is bound to one token when it is minted, so a tab that holds a launchpad token needs a key for that token.
Why Robinhood Chain?
USDG and tokenized stocks live there, blocks are fast enough to settle inside an HTTP request, and no x402 facilitator exists for it yet.
What happens to the daily window if I change a policy?
It stays. The window and its spend are stored separately from the policy, so raising or lowering the cap never resets what was already spent today.

Status

What is real today and what is not.

ChainRobinhood Chain, id 4663
TokenUSDG. The vault accepts any ERC-20, and a key is bound to one token.
SettlementOne transaction per payment, submitted by the facilitator, which pays the gas.
Fee0 basis points at launch, charged on top so the seller receives the full price. The contract caps it at 500.
ContractLive on Robinhood Chain since block 56350964: 0x5e97c3557f8d4094A7a0f7F2815414974becf171. 18 tests. Fee set to 0.
SDKWritten. Buyer, seller and signing helpers in one module. Not on npm yet.
AppWorking against a local chain: deposit, withdraw, mint, revoke and the receipt are tested end to end in a browser.
FacilitatorWritten and tested end to end with 19 checks including replay, tampering, a burst over the cap, and revoke. The public instance runs at https://tab-sb5f.onrender.com/x402 with a minimum price of 0.10 USDG per call.
MCPWritten. Two tools, pay_fetch and tab_status, so a Claude or any MCP client can pay with a key it was given.
NextPublic facilitator on the node service, a minimum price per call so gas is covered, then npm publish.
Sourcegithub.com/ElizenDevVini/tab