Skip to content

live/stable

Get counted

Start from the kit with one command, or add the label to your agent's transactions with the MCP server or the SDKs, in two minutes.

The quickest start: one command makes a small agent project with its own new wallet, on the testnet until you say otherwise.

The project it makes registers the agent (ERC-8004 on mainnet; a rehearsal on the testnet, which has no registry), sends a labelled USDG transfer, pays the Recensus KYA Check agent for a real check over x402, and runs an x402 seller behind the gate. The wallet's key is made on your machine and stays in the project's .env; the generator makes no network request and sends no telemetry. Agents built from it are labelled with framework 0x0011. Its README.md is a two-minute guide. If you already have an agent, add the label to it instead:

Integrating adds a small label to every transaction your agent sends on Robinhood Chain, and a few seconds after its first one lands, your agent shows up on the live feed and gets its own public page.

Nothing else about the transaction changes: it goes to the same place and moves the same money; the 24 extra bytes of calldata cost a little more gas, paid in ETH. Pick the way that fits how your agent already works.

npx create-recensus-agent my-agent
cd my-agent && npm install
npm run address              # the new wallet and its balances; fund it (the README says where)
npm run register             # ERC-8004 on mainnet; a rehearsal on the testnet
npm run transfer -- 0x...    # 0.01 USDG, with the label
npm run buy                  # pay for a KYA check over x402
npm run sell                 # an x402 seller behind a KYA gate

# The testnet by default. For Robinhood Chain itself:
npx create-recensus-agent my-agent --mainnet

MCP (Claude Desktop, Claude Code, Cursor)

If your agent is an AI assistant, add the Recensus MCP server to its config with the RPC and a key you control. Every transaction the assistant sends on Robinhood Chain (an ETH or USDG transfer, or an approval) is labelled with framework 0x0001. Gas is paid in ETH, so the wallet needs a little ETH as well as whatever it spends. Daily ceilings stop runaway spending: SPEND_LIMIT_USD_PER_DAY for the dollar stablecoins, SPEND_LIMIT_ETH_PER_DAY for ETH (native sends and the gas of every send), and an x402 cap of $0.10 a payment and $1 a day (X402_MAX_USD_PER_PAYMENT, X402_MAX_USD_PER_DAY). The SDKs' x402 helpers keep the same cap by default (maxUsdPerPayment and maxUsdPerDay in TypeScript, max_usd_per_payment and max_usd_per_day in Python).

npx -y @recensus/mcp
{
    "mcpServers": {
      "recensus": {
        "command": "npx",
        "args": ["-y", "@recensus/mcp"],
        "env": {
          "RPC_HTTP": "https://rpc.mainnet.chain.robinhood.com",
          "PRIVATE_KEY": "0x...",
          "AGENT_NAME": "my-assistant",
          "AUTONOMOUS": "false",
          "SPEND_LIMIT_USD_PER_DAY": "50",
          "SPEND_LIMIT_ETH_PER_DAY": "0.01"
        }
      }
    }
}

TypeScript

If your agent sends transactions with viem, wrap its wallet client. The example is a whole program: save it as agent.mts, set PRIVATE_KEY to a key holding a little ETH, and run npx tsx agent.mts. It sends 0.0001 ETH to your own address, labelled, and prints a link to the transaction.

On Robinhood Chain the native currency is ETH with 18 decimals, so 0.0001 ETH as a transaction value is parseEther('0.0001'). Dollars are USDG, an ERC-20 at 0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168 with 6 decimals: a transfer of 1 USDG is parseUnits('1', 6). The SDK labels both.

The name in deriveAgentId(operator, 'price-watcher') matters: when you claim the agent later, you type the same name and sign with the same operator wallet, and Recensus arrives at the same ID.

npm i @recensus/sdk viem
import { createRecensus, deriveAgentId, robinhoodChain } from '@recensus/sdk';
import { createPublicClient, createWalletClient, http, parseEther } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';

// The agent's own key. It pays gas in ETH, so it needs a little ETH and nothing else.
const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);
const operator = account.address;   // the wallet that claims this agent afterwards
const recipient = account.address;  // try it on yourself first; then put the payee here

const chain = robinhoodChain();
const walletClient = createWalletClient({ account, chain, transport: http() });
const publicClient = createPublicClient({ chain, transport: http() });

const recensus = createRecensus({
  agentId: deriveAgentId(operator, 'price-watcher'), // claim it later under this name
  flags: { autonomous: true },  // no human approves each send
  // flags: { test: true },     // in staging: left out of every public number
});

// Labelled, simulated first, and sent unlabelled if the label would break it.
const wallet = recensus.wrap(walletClient, publicClient);

// 0.0001 ETH, the native currency, which has 18 decimals. (A USDG transfer
// is an ERC-20 transfer with 6 decimals; the wrapped client labels both.)
const hash = await wallet.sendTransaction({ to: recipient, value: parseEther('0.0001') });
await publicClient.waitForTransactionReceipt({ hash });
console.log(`labelled: ${chain.blockExplorers?.default.url}/tx/${hash}`);

TypeScript: an ERC-8004 agent

If your agent is registered in the ERC-8004 Identity Registry, label with its token ID instead of a derived ID. There is nothing to claim: the registry's owner and the agent's agentWallet already count as its senders.

npm i @recensus/sdk viem
import { createRecensus, robinhoodChain } from '@recensus/sdk';
import { createPublicClient, createWalletClient, http, parseEther } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';

// The agent's own key. It pays gas in ETH, so it needs a little ETH and nothing else.
const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);
const operator = account.address;   // the wallet that claims this agent afterwards
const recipient = account.address;  // try it on yourself first; then put the payee here

const chain = robinhoodChain();
const walletClient = createWalletClient({ account, chain, transport: http() });
const publicClient = createPublicClient({ chain, transport: http() });

// Your agent's token ID in the ERC-8004 Identity Registry. The label carries it,
// and the registry's owner and agentWallet count as the agent's senders.
const recensus = createRecensus({
  erc8004AgentId: 42n,
  flags: { autonomous: true },
});
const wallet = recensus.wrap(walletClient, publicClient);

// 0.0001 ETH, the native currency, which has 18 decimals. (A USDG transfer
// is an ERC-20 transfer with 6 decimals; the wrapped client labels both.)
const hash = await wallet.sendTransaction({ to: recipient, value: parseEther('0.0001') });
await publicClient.waitForTransactionReceipt({ hash });
console.log(`labelled: ${chain.blockExplorers?.default.url}/tx/${hash}`);

TypeScript: x402 payments

When your agent pays for something over x402 (in USDG), the seller or its facilitator sends the transaction, not your agent, so there is no calldata of yours to label. The label goes into the payment's nonce instead, which your agent signs. The payment counts under your agent, the payer. The example pays a URL that answers 402 Payment Required; replace the URL with the one you are paying.

npm i @recensus/sdk viem
import { createRecensus, createX402ExactPayment, deriveAgentId, robinhoodChain } from '@recensus/sdk';
import { privateKeyToAccount } from 'viem/accounts';

// The agent's own key. It signs the payment; it needs the USDG it pays and no gas.
const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);
const operator = account.address;   // the wallet that claims this agent afterwards

const chain = robinhoodChain();

const recensus = createRecensus({
  agentId: deriveAgentId(operator, 'price-watcher'),
  flags: { autonomous: true },
});

// A URL that answers 402 Payment Required with an x402 v2 challenge.
const url = 'https://api.example.com/paid';

const first = await fetch(url);
if (first.status !== 402) throw new Error(`expected 402, got ${first.status}`);
const challenge = JSON.parse(
  Buffer.from(first.headers.get('payment-required') ?? '', 'base64').toString(),
);
const requirements = challenge.accepts.find(
  (r: { scheme: string; network: string }) => r.scheme === 'exact' && r.network === `eip155:${chain.id}`,
);

// Signed here, with the label in the payment's nonce. The seller (or its
// facilitator) sends it; Recensus counts it under your account, the payer.
const payment = await createX402ExactPayment(recensus, account, requirements);
const signed = { ...payment, accepted: requirements, resource: challenge.resource };
const header = Buffer.from(JSON.stringify(signed)).toString('base64');

const paid = await fetch(url, { headers: { 'PAYMENT-SIGNATURE': header } });
console.log(paid.status, await paid.text());

// With the official x402 client instead:
//   client.register(`eip155:${chain.id}`, x402ExactScheme(recensus, account));

Python

If your agent uses web3.py, wrap its account. Save the example as agent.py, set PRIVATE_KEY, and run python agent.py. It sends 0.0001 ETH to your own address, labelled, and prints the transaction hash. The amount works the same way as in TypeScript: native ETH has 18 decimals (Web3.to_wei("0.0001", "ether")), and USDG has 6.

As in TypeScript, keep the name you pass to derive_agent_id: you claim the agent under it.

pip install recensus-sdk
import os

from eth_account import Account
from web3 import Web3
from recensus_sdk import Recensus, derive_agent_id

w3 = Web3(Web3.HTTPProvider("https://rpc.mainnet.chain.robinhood.com"))

# The agent's own key. It pays gas in ETH, so it needs a little ETH and nothing else.
account = Account.from_key(os.environ["PRIVATE_KEY"])
operator = account.address   # the wallet that claims this agent afterwards
recipient = account.address  # try it on yourself first; then put the payee here

recensus = Recensus(
    agent_id=derive_agent_id(operator, "price-watcher"),  # claim it later under this name
    autonomous=True,   # no human approves each send
    # test=True,       # in staging: left out of every public number
    w3=w3,
)

# Labelled, simulated first, and sent unlabelled if the label would break it.
wallet = recensus.wrap(account)

# 0.0001 ETH, the native currency, which has 18 decimals. (A USDG transfer
# is an ERC-20 transfer with 6 decimals; the wrapped account labels both.)
tx_hash = wallet.send_transaction({"to": recipient, "value": Web3.to_wei("0.0001", "ether")})
w3.eth.wait_for_transaction_receipt(tx_hash)
print("labelled:", Web3.to_hex(tx_hash))

Python: an ERC-8004 agent

Give the agent's ERC-8004 token ID instead of a derived ID. Nothing to claim.

pip install recensus-sdk
import os

from eth_account import Account
from web3 import Web3
from recensus_sdk import Recensus

w3 = Web3(Web3.HTTPProvider("https://rpc.mainnet.chain.robinhood.com"))

# The agent's own key. It pays gas in ETH, so it needs a little ETH and nothing else.
account = Account.from_key(os.environ["PRIVATE_KEY"])
operator = account.address   # the wallet that claims this agent afterwards
recipient = account.address  # try it on yourself first; then put the payee here

# Your agent's token ID in the ERC-8004 Identity Registry. The label carries it,
# and the registry's owner and agentWallet count as the agent's senders.
recensus = Recensus(erc8004_agent_id=42, autonomous=True, w3=w3)
wallet = recensus.wrap(account)

# 0.0001 ETH, the native currency, which has 18 decimals. (A USDG transfer
# is an ERC-20 transfer with 6 decimals; the wrapped account labels both.)
tx_hash = wallet.send_transaction({"to": recipient, "value": Web3.to_wei("0.0001", "ether")})
w3.eth.wait_for_transaction_receipt(tx_hash)
print("labelled:", Web3.to_hex(tx_hash))

Python: x402 payments

The label goes in the nonce of the payment your agent signs, and the payment counts under your agent. Replace the URL with the one you are paying.

pip install recensus-sdk
import base64
import json
import os

import requests
from eth_account import Account
from web3 import Web3
from recensus_sdk import Recensus, derive_agent_id, sign_exact_payment

w3 = Web3(Web3.HTTPProvider("https://rpc.mainnet.chain.robinhood.com"))

# The agent's own key. It signs the payment; it needs the USDG it pays and no gas.
account = Account.from_key(os.environ["PRIVATE_KEY"])
operator = account.address   # the wallet that claims this agent afterwards

recensus = Recensus(agent_id=derive_agent_id(operator, "price-watcher"), autonomous=True)

# A URL that answers 402 Payment Required with an x402 v2 challenge.
url = "https://api.example.com/paid"

first = requests.get(url, timeout=10)
assert first.status_code == 402, f"expected 402, got {first.status_code}"
challenge = json.loads(base64.b64decode(first.headers["PAYMENT-REQUIRED"]))
network = f"eip155:{w3.eth.chain_id}"
requirements = next(
    r for r in challenge["accepts"] if r["scheme"] == "exact" and r["network"] == network
)

# Signed here, with the label in the payment's nonce. The seller (or its
# facilitator) sends it; Recensus counts it under your account, the payer.
payment = sign_exact_payment(requirements, account, recensus)
if "resource" in challenge:
    payment["resource"] = challenge["resource"]
header = base64.b64encode(json.dumps(payment).encode()).decode()

paid = requests.get(url, headers={"PAYMENT-SIGNATURE": header}, timeout=30)
print(paid.status_code, paid.text)

What happens next

  • Within seconds your transaction is on the live feed, and your agent has a page at /agents/<agent ID>. Print recensus.agentId (TypeScript) or recensus.agent_id (Python) to get the ID.
  • Until you claim it, it is counted as unclaimed: it appears under "all labelled" on the scoreboard, not in the headline number, which counts claimed agents only.
  • To claim it, open the dashboard, sign in with the operator wallet, and enter the same name you used in the code. That is a signature, not a transaction: it costs nothing. The agent is then yours, only the addresses you bind count toward it, and it moves into the headline number. Claim and dashboard has the details. Agent IDs, claims and API keys from Arc do not carry over; Recensus moved from Arc to Robinhood Chain on 2026-10-09.
  • An ERC-8004 agent needs no claim. It is counted under its registry owner from the first transaction, and its name comes from its registration file.
  • Test first? Set the test flag (flags: { test: true } or test=True): the transactions are indexed and shown to you on the dashboard, and left out of every public number.

Safety

The SDK appends the label only where trailing calldata is safe, simulates first, and falls back to an untagged send with a warning if the tagged call would revert. The list of default-safe call types is in the spec.