Documentation

How to use Omni402

Omni402 turns any HTTP API into a pay-per-call endpoint on Celo. This page covers all three ways in: selling an API as a human, buying one as an agent, and wiring the marketplace into an MCP client.

For humans

Sell an API

Wrap any API in one command and get paid per call in USDC.

For agents

Pay for a call

A wallet-native client that answers 402 challenges automatically.

For AI clients

Use the MCP server

Let Claude or GPT discover the market and buy on its own.

01

Core concepts

Omni402 has two sides and one settlement layer. Everything else is detail.

  • The gateway (x402ify) sits in front of an API you already have access to. Unpaid requests get an HTTP 402 naming the price. Paid requests are proxied upstream with your key injected server-side.
  • The buyer is a wallet. It answers the 402 by signing an EIP-3009 authorization and retrying. No account, no API key.
  • The facilitator is Celo's hosted x402/MPP service. It verifies the signature, submits the transfer on-chain, and pays the gas. It never custodies funds: USDC moves buyer to seller inside the token contract.
Why this matters

Your upstream API key never leaves your machine, and no private key is needed on the server. The buyer signs; the facilitator settles.

The request lifecycle

1. agent  → GET /pay/celo-token-prices          (no payment)
2. server → 402 Payment Required + price in USDC
3. agent  → signs EIP-3009 authorization        (off-chain, no gas)
4. agent  → retries with the credential attached
5. server → facilitator settles on Celo
6. server → 200 OK + data + Payment-Receipt (tx hash)
02

Install and configure

Node 20 or newer, and pnpm. Clone the repo and install:

git clone https://github.com/eddiemessiah/omni402
cd omni402
pnpm install
cp .env.example .env

To settle real payments you need two secrets in .env: a facilitator API key and an MPP server secret. Create the API key once at x402.celo.org (connect a wallet, click Create API key). Generate the server secret locally:

openssl rand -base64 32
Without those two values

The gateway still runs and serves a correct 402 challenge, but it cannot settle. That is challenge-only mode, which is useful for local development and tests.

Develop on Celo Sepolia first by setting X402_NETWORK=celo-sepolia. Testnet USDC comes from faucet.circle.com. Switch to celo only when you want real money to move.

03

Sell an API (for humans)

The fastest path. Point the wrapper at any API, set a price, and give it the wallet that should receive the money:

pnpm wrap https://api.coingecko.com/api/v3 \
  --price 0.001 \
  --wallet 0xYourReceivingWallet \
  --sample "/simple/price?ids=celo&vs_currencies=usd"

If the upstream needs a key, pass it and the gateway injects it only after a payment settles. It never reaches the buyer:

# as a query parameter
pnpm wrap https://api.etherscan.io/v2/api --price 0.01 \
  --wallet 0xYourWallet --query "apikey=$ETHERSCAN_KEY"

# or as a header
pnpm wrap https://api.example.com --price 0.01 \
  --wallet 0xYourWallet --header "Api-Key: $SECRET"

Selling several APIs at once

Add an entry to lanes.json and the hub serves it at /pay/<slug> on one public port, alongside the dashboard. A lane whose key is missing is skipped with a warning rather than started, so a buyer is never charged for a call that cannot succeed.

{
  "name": "Celo Token Prices",
  "upstream": "https://api.coingecko.com/api/v3",
  "price": "0.001",
  "asset": "USDC",
  "sample": "/simple/price?ids=celo&vs_currencies=usd",
  "description": "Live CELO / cUSD / cEUR prices."
}

Then start everything with one command:

pnpm build   # build the dashboard once
pnpm hub     # hub + all lanes + dashboard on :4021

The startup banner tells you which mode you are in. Look for settlement MPP live before expecting real payments.

04

Buy as an agent

The buyer needs a wallet holding a little USDC and no native gas, because the facilitator sponsors settlement. From the command line:

BUYER_PRIVATE_KEY=0x… pnpm --filter @omni402/x402ify exec tsx bin/buy.ts \
  "http://localhost:4021/pay/celo-token-prices/simple/price?ids=celo&vs_currencies=usd" \
  --network celo --max 0.01

It prints the response, the settlement transaction hash, and a Celoscan receipt link. Programmatically, createBuyer patches fetch so any gated URL is paid transparently:

import { createBuyer } from "@omni402/x402ify";

const buyer = createBuyer({
  privateKey: process.env.BUYER_PRIVATE_KEY,
  network: "celo",
  maxAmount: "1"            // never pay more than 1 USDC per call
});

const { status, body, reference } = await buyer.pay("https://your-api/premium");
console.log(status, reference); // 200, 0x… settlement tx

The autonomous agent

The repo ships a buyer that runs on a loop, picking endpoints from the catalog and paying for them. Every tick is a real x402 settlement on Celo.

BUYER_PRIVATE_KEY=0x… X402_NETWORK=celo \
PUBLIC_BASE=https://your-app.up.railway.app \
pnpm agent
Use two wallets

Keep the buyer and the seller separate. If both sides are the same address the payment is a self-transfer and proves nothing. Fund a throwaway buyer with a few dollars of USDC.

05

The MCP server

This is what makes any AI assistant a paying customer. The server speaks the Model Context Protocol over stdio and exposes two tools:

ToolWhat it doesCosts money
list_paid_apisReturns the catalog: name, price per call, network, and the exact URL to pay.No, discovery is free
paid_fetchPays the per-call price for an endpoint and returns the data plus the settlement transaction.Yes, in USDC

Wiring it into a client

The repo includes a .mcp.json that Claude Desktop, Claude Code, and other MCP clients can pick up. Point PUBLIC_BASE at wherever your hub is reachable:

{
  "mcpServers": {
    "omni402": {
      "command": "node",
      "args": ["--import", "tsx", "apps/mcp/src/server.ts"],
      "env": {
        "X402_NETWORK": "celo",
        "PUBLIC_BASE": "https://your-app.up.railway.app",
        "BUYER_PRIVATE_KEY": "0x…"
      }
    }
  }
}

Or run it directly:

BUYER_PRIVATE_KEY=0x… X402_NETWORK=celo pnpm mcp

Talking to it

Once connected, ask the assistant something it cannot answer for free. It will discover the endpoint, pay for it, and show you the receipt:

  • "What paid APIs are available on Omni402?"
  • "Get me the current CELO price using omni402 and show the settlement transaction."
  • "Buy the on-chain data endpoint but never spend more than 0.01 USDC."
Safety

paid_fetch accepts a maxPrice cap and refuses anything above it. Discovery costs nothing, so an assistant can browse the market before spending.

06

Agent identity (ERC-8004)

Payments answer "can this agent pay". Identity answers "who is this agent". Omni402 registers an agent on Celo's ERC-8004 Identity Registry, which mints a portable NFT identity that is browsable and reputation-ready.

Publish a registration file describing your agent (the repo's agent.json is a working example), then register it. The signing wallet owns the identity and needs a little CELO for gas:

AGENT_PRIVATE_KEY=0x… X402_NETWORK=celo \
pnpm register-8004 https://raw.githubusercontent.com/you/repo/main/agent.json

It prints your agent ID, the transaction, and an 8004scan link. The reference agent for this project is agent #9765.

RegistryNetworkAddress
IdentityCelo mainnet0x8004A169FB4a3325136EB29fA0ceB6D2e539a432
IdentityCelo Sepolia0x8004A818BFB912233c491871b3d84c89A494BD9e
07

Dashboard and endpoints

The hub serves everything on a single port, so local and production run the identical code path.

PathWhat it serves
/The live dashboard: revenue, paid calls, per-API income, payments feed with explorer receipts, calls-by-hour.
/payA public catalog of every payable endpoint currently running.
/pay/<slug>A payable API. Unpaid requests get a 402; paid requests get the upstream response.
/api/stateJSON snapshot of all aggregated stats, which is what the dashboard renders.
/api/healthLiveness probe used by the container healthcheck.
/wsWebSocket stream of payment and request events, pushed live to the dashboard.

You can see the 402 for yourself without a wallet. This returns the payment requirements as JSON:

curl -i https://your-app.up.railway.app/pay/celo-token-prices
08

Environment reference

VariableNeeded forMeaning
X402_NETWORKalwayscelo for mainnet or celo-sepolia for testnet.
SELLER_PAY_TOlive settlementThe wallet that receives the USDC. No private key required.
MPP_SECRET_KEYlive settlementServer secret that signs and verifies challenges and receipts. Keep it stable across redeploys.
X402_API_KEYlive settlementFacilitator credits key from x402.celo.org, sent as X-API-Key.
PUBLIC_BASEdeploymentYour public URL, so the MCP catalog advertises payable URLs correctly.
BUYER_PRIVATE_KEYbuying onlyThe agent wallet that pays. Never put this on the seller.
PORThostsInjected by Railway, Render, and Fly. Do not set it yourself.
Secrets

.env is gitignored. Never commit a private key, and never paste one into a chat or an issue.

09

Deploy it

One container serves the dashboard, the API, the WebSocket, and every payable endpoint. Build it from the repository root:

docker build -t omni402 .
docker run -p 4021:4021 --env-file .env omni402

On Railway, create one service from the repo with the root directory set to / so it uses the root Dockerfile. Set the same variables as .env plus PUBLIC_BASE, leave PORT alone, then generate a domain. The healthcheck at /api/health keeps traffic off a half-booted container.

Do not split the monorepo

Railway may offer to create a service per package. Decline. The MCP server is a stdio process rather than a web service, and the dashboard is served by the hub, so a single container is correct.

10

Troubleshooting

The gateway returns 402 forever

The buyer is not paying, or settlement is off. Check the startup banner: if it says challenge-only, then X402_API_KEY or MPP_SECRET_KEY is missing. Also confirm the payout address is a real address rather than the zero address.

Settlement fails with an insufficient balance

The buyer wallet needs USDC, not CELO. Gas is sponsored, but the payment itself is real money, so the balance has to cover the price.

A lane did not start

Its upstream key is missing. The hub skips such lanes deliberately, because a gateway with no upstream key would take the buyer's money and then fail.

USDT signatures are rejected

USDT has no on-chain version(), so its EIP-712 domain must be set explicitly to name Tether USD and version 1. Omni402 pins this per asset already; the error usually means a custom asset was configured by hand.

The unknown endpoint returns the landing page

An unrecognized /pay/<slug> returns a JSON 404 with a hint. If you get HTML instead, the lane router did not initialize, which usually means the hub failed to read lanes.json.