402tools402 .dev
sell-side · live
sell-side v1 · live on base mainnet

Earn USDC from AI agent calls.

Publish any REST endpoint. Get paid per call in USDC. Wallet-only sign-up — no KYC, no account, no email. Take rate 3 % paywall · 4 % proxy. We absorb gas. Settlement runs daily at 00:00 UTC straight to your wallet.

Sellers live
0
Revenue to date
$0
Take rate
3 / 4 %paywall / proxy
Settlement
24 h00:00 UTC
how it works

Three steps. No human review.

No form, no email, no KYC. Sign an EIP-712 message with your Base wallet, drop an upstream URL, set a price. Your endpoint is listed in /v1/_meta in under 10 seconds.

01
Register a slugPOST /v1/_seller/register with wallet + slug + EIP-712 signature. Status active on first byte. Slug pattern ^[a-z0-9-]{3,32}$.
Wallet-only · no PII
02
Add an endpointPOST /v1/_seller/<wallet>/endpoints with upstream_url, atomic_price, mode (paywall or proxy). Three regional probes verify HTTP 200 + valid JSON + p95 < 5s.
Live in ~10s
03
Get paid dailySettlement cron at 00:00 UTC. USDC sent direct to your wallet on Base. Dust < $1 carries over. tools402 absorbs the gas (~$0.005 per tx).
Daily · 00:00 UTC
Sign once → get paid forever (full code, TypeScript + viem)

One file, ~90 lines. Save as index.ts, edit the 5 lines marked // TODO change me, run bun add viem && bun run index.ts. The script generates a wallet (or reuses your saved .tools402-seller.pk), registers your slug, publishes your endpoint, prints the live URL. Idempotent — safe to re-run if anything fails.

// requires: viem 2.x (run: bun add viem) import { existsSync, readFileSync, writeFileSync } from "node:fs"; import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"; import { keccak256, toBytes } from "viem"; // ─── TODO change me — 5 lines ───────────────────────────────────────────── const SLUG = "your-slug"; // ^[a-z0-9-]{3,32}$ — globally unique const PATH_SUFFIX = "your-path"; // ^[a-z0-9-]{3,64}$ — suffix after /v1/<slug>/ const UPSTREAM_URL = "https://your-api.example.com/x"; // your live HTTPS endpoint (must return 200 + JSON) const ATOMIC_PRICE = 1000; // INT, atomic USDC units — 1000 = $0.001 const DESC = "What your endpoint does (max 200 chars)."; // ────────────────────────────────────────────────────────────────────────── // Constants — do not edit (source: src/lib/seller-auth.ts:19,25) const API = "https://api.tools402.dev"; const DOMAIN = { name: "tools402", version: "1", chainId: 8453 } as const; const TYPES = { SellerAction: [ { name: "wallet", type: "address" }, { name: "action", type: "string" }, { name: "payloadHash", type: "bytes32" }, { name: "timestamp", type: "uint256" }, ], } as const; // ─── PK : load from .tools402-seller.pk (gitignored) or generate ───────── const PK_FILE = ".tools402-seller.pk"; let pk: `0x${string}`; if (existsSync(PK_FILE)) { pk = readFileSync(PK_FILE, "utf-8").trim() as `0x${string}`; } else { pk = generatePrivateKey(); writeFileSync(PK_FILE, pk, { mode: 0o600 }); console.log(`[onboard] new wallet saved to ${PK_FILE} — add this file to .gitignore, never commit`); } const account = privateKeyToAccount(pk); const WALLET = account.address; console.log(`[onboard] wallet: ${WALLET}`); // ─── Idempotence : check /v1/_meta before each mutating call ───────────── const meta = await fetch(`${API}/v1/_meta`).then((r) => r.json()) as { endpoints: { path: string; seller: string }[]; }; const alreadySeller = meta.endpoints.some((e) => e.seller === WALLET); const targetPath = `/v1/${SLUG}/${PATH_SUFFIX}`; const alreadyPublished = meta.endpoints.some((e) => e.path === targetPath); // ─── 1 · Register slug (skipped if wallet is already a seller) ────────── if (!alreadySeller) { const ts1 = Math.floor(Date.now() / 1000); const sig1 = await account.signTypedData({ domain: DOMAIN, types: TYPES, primaryType: "SellerAction", message: { wallet: WALLET, action: "register", payloadHash: keccak256(toBytes(SLUG)), timestamp: BigInt(ts1) }, }); const r1 = await fetch(`${API}/v1/_seller/register`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ wallet: WALLET, slug: SLUG, signature: sig1, timestamp: ts1 }), }); if (!r1.ok) { console.error(`[onboard] register failed (${r1.status}):`, await r1.json()); console.error(`[onboard] wallet still saved in ${PK_FILE} — fix and re-run`); process.exit(1); } console.log(`[onboard] register: OK — slug "${SLUG}" is yours`); } else { console.log(`[onboard] register: skipped (wallet already a seller)`); } // ─── 2 · Add endpoint (skipped if path already published) ─────────────── // Schema canonical — source: src/routes/_seller/endpoints.ts:37 (6 fields, atomic_price is NUMBER) if (!alreadyPublished) { const endpoint = { path_suffix: PATH_SUFFIX, upstream_url: UPSTREAM_URL, atomic_price: ATOMIC_PRICE, unit: "call", desc: DESC, mode: "paywall", // "paywall" (3 %) or "proxy" (4 %) }; const ts2 = Math.floor(Date.now() / 1000); const sig2 = await account.signTypedData({ domain: DOMAIN, types: TYPES, primaryType: "SellerAction", message: { wallet: WALLET, action: "add_endpoint", payloadHash: keccak256(toBytes(JSON.stringify(endpoint))), timestamp: BigInt(ts2) }, }); const r2 = await fetch(`${API}/v1/_seller/${WALLET}/endpoints`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ ...endpoint, signature: sig2, timestamp: ts2 }), }); if (!r2.ok) { console.error(`[onboard] add_endpoint failed (${r2.status}):`, await r2.json()); console.error(`[onboard] slug "${SLUG}" still yours, wallet still in ${PK_FILE} — fix and re-run`); process.exit(1); } console.log(`[onboard] add_endpoint: OK`); } else { console.log(`[onboard] add_endpoint: skipped (${targetPath} already published)`); } // ─── 3 · Done — your endpoint is live ────────────────────────────────── console.log(`[onboard] ✅ live: ${API}${targetPath}`); console.log(`[onboard] test: curl -X POST ${API}${targetPath} -H "content-type: application/json" -d '{}' # → 402 + x402 quote`); console.log(`[onboard] daily settlement at 00:00 UTC → USDC to ${WALLET}`);

Both API calls fire within ~50 ms each. The endpoint flips from pending to active as soon as three regional probes return 200 OK with a valid JSON body and p95 latency < 5 s. From bun run to first billable call: typically < 30 seconds. Add .tools402-seller.pk to your .gitignore immediately — that file holds the private key that owns your slug and receives your USDC settlements.

How the 3 health probes work

Right after register, we hit your endpoint from three different regions (US-east, EU-west, AP-south) with a synthetic payload. Pass criteria : HTTP 200 + valid JSON body + total latency p95 < 5s. All 3 must pass within 30 seconds or your endpoint stays in pending.

# Sample probe response we expect HTTP/1.1 200 OK Content-Type: application/json { "ok": true, "echo": "probe-7f02a" }
Paywall vs proxy : which mode

Paywall (3 %) — tools402 returns an Ed25519-signed JWT (60s TTL) to the buyer ; the buyer hits your URL with the JWT in the header ; you verify it with @tools402/client.verifyPaywallJWT(). Lowest take rate. You handle hosting + bandwidth.

Proxy (4 %) — tools402 relays the request through our infra (50 MB body limit, 30s timeout). Higher take rate but zero infra setup for the seller. Useful for static / cheap endpoints or unknown traffic.

Settlement dust threshold and gas

If your daily net is under $1 USDC, the amount carries over to the next day. Once you cross $1, the full carry-over balance is sent in one transfer. tools402 absorbs the gas cost (~$0.005 per Base transfer, projected ~$750/month at 5 000 sellers).

What if my endpoint goes down

We run a passive monitor on listed endpoints. If your endpoint returns 5xx for > 50 % of probes over 10 minutes, your listing flips to degraded (still callable but flagged). Sustained outage > 1 hour ⇒ suspended until you POST /v1/_seller/<wallet>/resume. No revenue lost during suspension : in-flight settlements still ship.

two rails · one dev

Already on Stripe? Keep it.

Stripe for human payments. tools402 for agent payments. Same dev, two rails. tools402 is not a Stripe replacement — it's the second rail that lets the same endpoint accept both customer segments. You keep your existing Stripe Connect setup for humans (cards, your KYC, your dashboard) and bolt tools402 on the side for autonomous agents (wallet-only, no KYC, USDC settlement).

H
Stripe (humans)Your existing Stripe Connect account. Cards, Apple Pay, monthly invoices. Your KYC, your dashboard, your dispute flow. We don't touch any of it.
2.9% + 30¢ · your setup
A
tools402 (agents)Wallet-only signup, no email, no KYC on our side. USDC paid per call on Base, Polygon, or Solana. Daily settlement to your wallet at 00:00 UTC.
3 / 4% · no KYC here
=
Same endpointOne upstream URL, two payment surfaces. Same business logic, same code path. Pick the rail per call based on who's knocking — humans get a Stripe checkout link, agents get a 402 quote.
One dev · two markets

We complement Stripe, we don't replace it. Stripe handles your human checkout (KYC, cards, recurring). tools402 handles the agent rail (wire-protocol HTTP 402, USDC on-chain, settlement to your wallet). The seller controls both — we don't custody anything on either side.

your stats

One signed URL. Your dashboard, your tools.

No web UI. No login. We give you a signed URL (EIP-712, your wallet) that returns your stats as JSON — pipe it into Grafana, Observable, or just curl | jq.

~/seller · curl /v1/_seller/<your-wallet>/stats?sig=… | jq schema
// Response shape — real values appear once you publish and traffic flows. { "wallet": "0x<your-wallet>", "slug": "<your-slug>", "revenue_24h_usdc": "<decimal>", "revenue_total_usdc": "<decimal>", "calls_24h": <int>, "calls_total": <int>, "take_rate": "3%", "top_endpoints": [ { "path": "/v1/<your-slug>/<path>", "calls_24h": <int> } ], "perf": { "p50_ms": <int>, "p95_ms": <int>, "p99_ms": <int> }, "next_settlement": { "in": "<duration>", "net_usdc": "<decimal>", "gas_absorbed": true } }

The dashboard is the data. Pick your visualization. JSON beats lock-in. Pipe it anywhere — Grafana, Observable, Notion, a Google Sheet, your own React app.

take rate

3 % paywall. 4 % proxy. Nothing else.

No subscription. No setup fee. No platform fee. No payout fee. The take rate is the only money we make on sell-side. RapidAPI charges 25 % flat off-chain with monthly settlement — we ship 8× cheaper on-chain in 24 h.

Marketplace Take rate Settle
tools402multi-facilitator · OSS router · zero KYC 3 / 4 % 24 h on-chain
A Apitoll1 facilitator · no failover 3 % on-chain
P PayAPI MarketFeatured tier · $49/mo addon 3 / 2 % on-chain
X xpay.sh1 facilitator 2.5 % on-chain
R RapidAPIoff-chain fiat · monthly invoice 25 % + 30 d
live sellers

Community sell-side is open. Be the first to publish.

No community endpoint has shipped yet. The catalogue is empty by design — community sell-side opened on 2026-05-12 and we don't seed it with fake listings. The first wallet to POST /v1/_seller/register gets the founding-seller slot. Live count is fetched from /v1/_seller/count and shown in the hero stat above.

Endpoint Price Calls / 7d
/v1/<your-slug>/<your-path>No community listings yet · be the first

Founding-seller slot is open. The catalogue surfaces real wallets and real traffic. We don't seed it. The first published endpoint shows up here as soon as 3 health probes pass.

moderation · compliance

Open by default. Suspended on abuse.

Permissionless on day one — anyone with a Base wallet can list. We don't pre-approve. Auto-suspend triggers at 5 community reports in 24 h on the same endpoint (POST /v1/_seller/report). Settlement is non-custodial: USDC moves buyer-wallet → facilitator broadcaster → seller-wallet on-chain, with no holding period on our infra.

No KYC. Non-custodial. Wallet-only identity. Settlement is on-chain (USDC) within 24 h, with no buyer-funds holding on our infra. Sellers receive directly to their wallet — we never hold the position.

publish in 30 seconds

Sign once. Get paid forever.

Fund a Base wallet, sign the EIP-712 register message, drop your upstream URL. First settlement runs at the next 00:00 UTC. The OSS SDK @tools402/client handles every step.