Skip to content

Documentation

Vault402 docs

Vault402 adds HTTP 402 payments to any Express API. Agents pay on Robinhood Chain and get the response, with no accounts or API keys.

Install

Requires Node.js 18+. Developers install the SDK. An agent can install itself with one command, which also writes a new wallet key to .env.agent (mode 600, added to .gitignore).

$ npm install vault402
terminal
npx vault402 init --out .env.agent

Networks

Robinhood Chain is an Ethereum Layer 2 built on Arbitrum, and gas is paid in ETH. The public RPCs are rate-limited, so pass rpcUrl with a provider endpoint in production.

NetworknetworkChain IDPublic RPC
Mainnet"robinhood"4663rpc.mainnet.chain.robinhood.com
Testnet"robinhood-testnet"46630rpc.testnet.chain.robinhood.com

Tokens

ETH and WETH are built in. Describe USDC, Stock Tokens or any other ERC-20 by address, which you can find on the Robinhood Chain explorer. Add eip3009 to tokens that support transferWithAuthorization to enable gasless payments.

tokens.ts
import { defineToken } from 'vault402';

export const USDC = defineToken({
  symbol: 'USDC', address: '0x…', decimals: 6,
  eip3009: { name: 'USD Coin', version: '2' },
});

export const TSLA = defineToken({
  symbol: 'TSLA', address: '0x…', decimals: 18,
});

Sell an API

Unpaid requests get a 402 that lists every accepted token. Your handler only runs after the payment is confirmed on-chain.

server.ts
import { requirePayment, ETH } from 'vault402';

app.get('/premium-data',
  requirePayment({
    network: 'robinhood',
    payTo: '0xYourWallet',
    price: [
      { token: USDC, amount: '0.25' },
      { token: ETH,  amount: '0.0001' },
    ],
    settlementKey: process.env.SETTLER_KEY, // enables gasless USDC
  }),
  (req, res) => res.json({ data: '…', paidBy: req.vault402?.payer })
);
OptionMeaning
priceOne { token, amount } or a list. Human units.
accessone-time (default), pass, or credits.
settlementKeySubmits EIP-3009 authorizations; needs a little ETH.
storeMemoryStore by default. Use Redis/DB for several instances.
confirmationsBlocks to wait before accepting. Default 1.

Pay as an agent

The budget is required. The agent only pays in tokens listed in it, and only up to the limits you set. It never pays twice for one request.

agent.ts
import { createAgentClient, ETH } from 'vault402';

const agent = createAgentClient({
  account: process.env.AGENT_PRIVATE_KEY,
  network: 'robinhood',
  budget: [
    { token: USDC, maxPerPayment: '1', maxTotal: '20' },
    { token: ETH,  maxPerPayment: '0.001' },
  ],
});

const res = await agent.fetch(url); // pays a 402 if it fits the budget

Payment schemes

transaction

The agent sends the transfer itself, with the paymentId in its calldata, then sends the tx hash. Works with ETH and any ERC-20. The agent pays gas.

authorization

The agent signs an EIP-3009 transferWithAuthorization with nonce = paymentId. The server submits it and pays the gas, so the agent needs no ETH.

Access models

accessOne payment buys
{ type: 'one-time' }This request (default)
{ type: 'pass', durationSeconds: 3600 }Unlimited calls for an hour
{ type: 'credits', credits: 100 }100 calls, including the one that paid

For passes and credits the server returns an access token. The agent client saves it and sends it as X-VAULT402-ACCESS on later calls.

How it works

  1. 01

    The agent requests data. The server responds 402 with a single-use paymentId and the accepted tokens.

  2. 02

    The agent pays on Robinhood Chain. It either sends a transfer that includes the paymentId or signs an EIP-3009 authorization with it.

  3. 03

    The agent retries with the header X-PAYMENT: base64(json).

  4. 04

    The server checks the receipt (or settles the authorization), marks the paymentId as used, and returns X-PAYMENT-RESPONSE.

Resources