Obol Docs

Developer documentation

Obol is a marketplace where any API, scraper, bot, model, or data process can charge AI agents per call in USDC — with no billing system, no subscriptions, and no user accounts.

New here? Skip the reading — build your first service with AI in 2 minutes ↓

Network
Arc testnet · chain 5042002
Payment
USDC via Circle Gateway (x402)
Fee
5% Obol fee · launch promo
Two roles, one marketplace
Provider (Seller)
You own something useful — an API, a scraper, a model. You wrap it with Obol middleware and get paid per call in USDC, settled to your Arc wallet.
Buyer (Agent)
Your AI agent discovers services in the Obol marketplace, deposits USDC into Circle Gateway once, then pays per call automatically — no manual signing per request.
✨ Build with AI · 2-minute setup

Don't write a line of code.
Let your AI build your service.

Obol is built for the agent economy — so building on Obol should use an agent too. Copy the Obol Skill below, paste it into Claude, ChatGPT, or Cursor, and it will scaffold a working paid service, wire in your wallet, and walk you through deploying it. Your Arc payout address is already baked in.

1
Copy the skill
One click above — your wallet is already in it.
2
Paste into your AI
Claude, ChatGPT, Cursor — any capable model.
3
Deploy & register
Your AI hands you the server + deploy steps.
▸ Preview the skill prompt
You are helping me publish a paid service on Obol — a marketplace where AI agents
discover services and pay per HTTP call in USDC on the Arc blockchain (no accounts,
no API keys for the buyer, no invoices). I want you to build, run, and help me deploy
a working seller server, then tell me exactly how to register it.

# CONTEXT YOU NEED

- Network: Arc testnet (EVM, chain ID 5042002).
- Payment rail: Circle Gateway via the x402 standard (HTTP 402 "Payment Required").
- The seller (me) runs a normal Express server. Each paid route is wrapped with one
  middleware call that sets a price. When an agent calls without payment, the
  middleware returns 402 + payment details; the agent's SDK pays off-chain (EIP-3009,
  gasless, ~200ms) and retries; the middleware verifies and runs my handler.
- My earnings settle to MY Arc wallet address: 0xYourArcAddress
- I keep 90% of each call price; Obol's network fee is 10% (deducted, not added).

# THE STACK (use these EXACT packages — do not substitute)

  npm install express @circle-fin/x402-batching

Server skeleton (ESM, Node 18+):

  import express from "express";
  import { createGatewayMiddleware } from "@circle-fin/x402-batching/server";

  const app = express();
  app.use(express.json());

  const SELLER = process.env.SELLER_ADDRESS;            // my Arc wallet
  const gateway = createGatewayMiddleware({
    sellerAddress: SELLER,
    networks: "eip155:5042002",                         // Arc testnet
    facilitatorUrl: "https://gateway-api-testnet.circle.com",
  });

  // Each paid route: gateway.require("$<price>") sets the USDC price per call.
  app.get("/price", gateway.require("$0.001"), async (req, res) => {
    // ... my logic ...
    res.json({ /* result */, paidBy: req.payment?.payer });
  });

  app.get("/health", (_req, res) => res.json({ ok: true })); // free, unmetered
  app.listen(process.env.PORT || 4021);

# THE SERVICE I WANT TO BUILD (default — change if I tell you otherwise)

A **Crypto Price API**. It wraps CoinGecko's FREE public endpoint (no API key needed):

  GET https://api.coingecko.com/api/v3/simple/price?ids=<coin>&vs_currencies=usd

Expose one paid route:
  GET /price?coin=bitcoin   → charge $0.001 USDC → return { coin, usd, ts }
Validate the coin param, handle CoinGecko errors gracefully, and always include
`paidBy: req.payment?.payer` in the response so I can see who called.

# WHAT TO DO, STEP BY STEP

1. Create the project: package.json (type: module), the server file, a .env with
   SELLER_ADDRESS=0xYourArcAddress and PORT=4021, and a .gitignore that excludes .env.
2. Write the full server. Production-quality: input validation, try/catch around the
   upstream fetch, clear JSON errors, a brief comment on each route's price.
3. Give me the exact commands to install deps and run it locally.
4. Tell me how to expose it publicly for testing in ONE command:
     npx localtunnel --port 4021     (gives a https://<name>.loca.lt URL)
   …and how to deploy permanently to Railway or Render (free tier): push to GitHub,
   create the project, set env var SELLER_ADDRESS=0xYourArcAddress, get the public URL.
5. Tell me precisely what to enter when I register it at obol-arc.web.app/dashboard
   → Provide services → + New service:
     - Service name, Category, Description
     - Price per call (USDC), Payout address (0xYourArcAddress), Hosted URL (my public URL)
     - Input params (e.g. "coin: string")
6. Finally, give me a one-line curl I can run to confirm the route returns 402 before
   payment (proof the metering works):  curl -i <my-url>/price?coin=bitcoin

Build everything now. Output complete files I can copy verbatim — no placeholders
except where I must paste my deployed URL. Be concise but complete.
Prefer to do it by hand? The full manual walkthrough is right below — install the SDK, wrap your endpoint, deploy, and register. The AI skill just does all of it for you.

Quick start

Prefer the manual route? Here's every step by hand.

Get from zero to a live paid API endpoint in under 10 minutes.

1
Create your wallet
Go to Dashboard → Use services → click "Create my wallet". Set a PIN — Obol never holds your keys. You'll get an Arc testnet EOA address. Copy it.
2
Get testnet USDC
Visit faucet.circle.com and request USDC on Arc testnet to your new wallet address. You'll need this to test both sending and receiving payments.
3
For sellers: wrap an endpoint
Install the SDK, add one middleware call to your Express route, run the server. See Providers section for the full code.
4
For buyers: deposit & call
Deposit USDC into Gateway once via your dashboard. After that, every API call is automatically paid — no confirmation per request.
Testnet only. Obol currently runs on Arc testnet (chain ID 5042002). All USDC is testnet USDC — no real money. Get free testnet USDC at faucet.circle.com.

Providers (Sellers)

A provider runs a server that does something useful and charges agents per HTTP call. You decide what runs behind the endpoint — Obol only sees the request and response.

How it works

You add Obol middleware to any Express route. When an agent calls that route without payment, your server returns HTTP 402 (Payment Required) with Circle Gateway payment details. The agent's SDK automatically pays and retries. Your server processes the request and the earnings land in your Arc wallet.

Agent calls your URL
  → No payment header → your server returns 402 + payment details
  → Agent's SDK pays Circle Gateway (~200ms, off-chain)
  → Agent retries with payment proof
  → Your middleware verifies payment
  → Your handler runs, returns data
  → Earnings settle to your Arc address

Wrap your API

Install the SDK and add middleware to any Express route.

# In your server project
npm install @circle-fin/x402-batching express

Minimal example — single endpoint

import express from "express";
import { createGatewayMiddleware } from "@circle-fin/x402-batching/server";

const app = express();

// Replace with your UCW wallet address from the Obol dashboard
const SELLER = process.env.SELLER_ADDRESS;

const gateway = createGatewayMiddleware({
  sellerAddress: SELLER,
  networks: "eip155:5042002",                          // Arc testnet
  facilitatorUrl: "https://gateway-api-testnet.circle.com",
});

// $0.002 USDC per call to this route
app.get("/enrich", gateway.require("$0.002"), (req, res) => {
  const domain = req.query.domain || "example.com";
  res.json({
    domain,
    company: "Acme Inc.",
    // req.payment is populated after a successful payment
    paidBy: req.payment?.payer,
    txHash: req.payment?.transaction,
  });
});

app.listen(4021);
req.payment is injected by the middleware after a verified payment. It contains payer (the buyer's Arc address) and transaction (the settlement tx hash). Use these for receipts, usage logging, or per-user rate limiting.

Per-endpoint pricing

Different routes can have different prices. Call gateway.require() independently on each route.

const gateway = createGatewayMiddleware({
  sellerAddress: SELLER,
  networks: "eip155:5042002",
  facilitatorUrl: "https://gateway-api-testnet.circle.com",
});

// Cheap: basic lookup
app.get("/company/basic", gateway.require("$0.001"), (req, res) => {
  res.json({ name: "Acme Inc.", country: "US" });
});

// Medium: full profile
app.get("/company/full", gateway.require("$0.005"), (req, res) => {
  res.json({ name: "Acme Inc.", headcount: 420, revenue: "$12M", ... });
});

// Expensive: bulk batch (up to 50 domains per call)
app.post("/company/bulk", gateway.require("$0.050"), async (req, res) => {
  const { domains } = req.body;
  const results = await enrichAll(domains);   // your own logic
  res.json({ results, count: results.length });
});

// Free: health check (no middleware = no payment required)
app.get("/health", (_req, res) => res.json({ ok: true }));
Register each endpoint path + its price separately in the Obol dashboard under Per-endpoint pricing. This lets agents discover which paths exist and what each costs before calling. You can also leave a route unlisted if you only want direct callers.

API keys & secrets

When you wrap a paid third-party API, your API key stays on your server. Buyers never see it. They call your URL and pay — your server uses its key to call the upstream service on their behalf.

# Your server's environment — buyers never see any of this
SELLER_ADDRESS=0xYourArcWallet
OPENAI_API_KEY=sk-...
SOME_DATA_API_KEY=abc123
SCRAPER_PROXY_PASSWORD=xyz789
The economics: You pay your upstream API $0.01/call. You charge agents $0.015/call through Obol. You keep $0.005 per call. The agent doesn't need an API key, a credit card, or an account anywhere — they just have USDC.

Real examples

Example 1 — Wrap OpenAI completions

import OpenAI from "openai";

const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

// You pay OpenAI ~$0.002/call (GPT-4o-mini).
// You charge agents $0.004/call → $0.002 margin per call.
app.post("/complete", gateway.require("$0.004"), async (req, res) => {
  const { prompt } = req.body;
  const chat = await openai.chat.completions.create({
    model: "gpt-4o-mini",
    messages: [{ role: "user", content: prompt }],
  });
  res.json({
    result: chat.choices[0].message.content,
    paidBy: req.payment?.payer,
  });
});

Example 2 — Wrap a Puppeteer scraper

import puppeteer from "puppeteer";

// Agents pay $0.01 per page scrape. You pay server costs (~$0.001).
app.get("/scrape", gateway.require("$0.010"), async (req, res) => {
  const { url } = req.query;
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto(url, { waitUntil: "networkidle2" });
  const text = await page.evaluate(() => document.body.innerText);
  await browser.close();
  res.json({ url, text, charCount: text.length, paidBy: req.payment?.payer });
});

Example 3 — Wrap a premium weather API (you hold the key)

// Agents call your endpoint and pay $0.001.
// Your server calls the premium API with your key.
// Agents never see your key.
app.get("/weather", gateway.require("$0.001"), async (req, res) => {
  const { city } = req.query;
  const r = await fetch(
    `https://api.weatherapi.com/v1/current.json?key=${process.env.WEATHER_KEY}&q=${city}`
  );
  const data = await r.json();
  res.json({
    city: data.location.name,
    temp_c: data.current.temp_c,
    condition: data.current.condition.text,
    paidBy: req.payment?.payer,
  });
});

Deploying your server

Your server needs a permanent public HTTPS URL. Any Node.js host works. For testing you can use localtunnel (no install needed). For production deploy to Railway, Fly.io, or Google Cloud Run.

OptionCostSetup timeBest for
localtunnel (npx)Free10 secondsLocal testing only
RailwayFree tier5 minutesHackathons, demos
Fly.ioFree tier10 minutesPersistent low-traffic
Google Cloud RunFree tier15 minutesProduction
RenderFree tier5 minutesSimple deploys

Local testing with localtunnel

# Terminal 1 — run your server
SELLER_ADDRESS=0xYourArcAddress node server.mjs

# Terminal 2 — expose it publicly (no install needed)
npx localtunnel --port 4021
# → your url is: https://happy-dogs-fly.loca.lt

Deploy to Railway (permanent URL)

# 1. Push your server code to a GitHub repo

# 2. Go to railway.app → New Project → Deploy from GitHub

# 3. Set environment variables in Railway dashboard:
#    SELLER_ADDRESS = 0xYourArcAddress
#    PORT           = 4021
#    (any API keys your server needs)

# 4. Railway gives you a URL like:
#    https://your-project.railway.app
# Use that URL when registering in the Obol dashboard.

Register in the dashboard

Go to Dashboard → Provide services → + New service and fill in each field:

FieldWhat to enterExample
Service nameShort name agents see in searchCompany Enrichment API
CategoryPick the closest matchData
Price per callDefault USDC price for one call0.002
Payout addressYour Arc wallet — auto-filled if wallet created0xYourArcAddress
Hosted URLYour server's public HTTPS URLhttps://your-app.railway.app
DescriptionWhat your API does, what it returnsReturns company name, size, industry for any domain
Input paramsParam names agents use to call itdomain: string
API docs URLLink to your docs (optional)https://your-docs.example.com

Per-endpoint pricing rows (optional)

If your server has multiple routes at different prices, add one row per route. Leave empty to use the single price above for all routes.

PathPrice per callDescription
/company/basic0.001Name and country only
/company/full0.005Full profile with headcount and revenue
/company/bulk0.050Batch of up to 50 domains
Once registered, your service appears in the Marketplace where agents can discover it. Agents call your hosted URL directly — Obol doesn't proxy the traffic. Payment happens between agent and your server via Circle Gateway.

Buyers (Agents)

A buyer is any AI agent or script that discovers services on Obol and pays per call automatically using Circle Gateway.

How it works

1. Agent deposits USDC into Circle Gateway (one time, any amount)
2. Agent calls a paid API endpoint
3. Server returns 402 + payment details (seller address, amount, network)
4. Agent's SDK signs an EIP-3009 off-chain authorization (~200ms, no gas)
5. Circle Gateway verifies and records the payment
6. Agent retries the request with the payment header
7. Server verifies → processes → returns data
8. Gateway settles to seller on Arc (batched, gasless)
No gas. No wallet popup. No signing per call. The agent signs once per payment using EIP-3009 (off-chain). Circle Gateway handles verification and batch settlement. Once the Gateway balance is funded, calls are automatic.

Fund your agent

Agents need a self-custody EOA (not a UCW) because they sign payments autonomously without a PIN.

# 1. Generate a new EOA private key (keep this secret):
node -e "const {Wallet} = require('ethers'); const w = Wallet.createRandom(); console.log('address:', w.address, '\nkey:', w.privateKey)"

# 2. Fund it with testnet USDC:
#    Go to faucet.circle.com → select Arc testnet → paste the address

# 3. Set your agent key in your environment:
export OBOL_AGENT_KEY=0xYourPrivateKey
Never commit your private key to git. Use environment variables or a secrets manager. The agent key is a hot wallet — only fund it with the USDC your agent needs to spend.

SDK usage

npm install @circle-fin/x402-batching
import { GatewayClient } from "@circle-fin/x402-batching/client";

const buyer = new GatewayClient({
  chain: "arcTestnet",
  privateKey: process.env.OBOL_AGENT_KEY,
});

// One-time: deposit USDC into Gateway (from your Arc wallet)
await buyer.deposit("10");   // deposit 10 USDC

// Check your Gateway balance
const balances = await buyer.getBalances();
console.log("Available:", balances.available, "USDC");

// Pay and call any Obol-powered endpoint
const { data, transaction } = await buyer.pay(
  "https://your-provider.railway.app/enrich?domain=acme.com"
);

console.log("Response:", data);
console.log("Paid in tx:", transaction);

// The buyer.pay() call handles the full 402 → pay → retry flow.
// No manual steps. If the server returns 402, the SDK pays and retries.

Discover services from the marketplace

// List all services in the Obol marketplace
const res = await fetch("https://obol-arc.web.app/api/services");
const { services } = await res.json();

for (const svc of services) {
  console.log(svc.name, "→", svc.hostedUrl, "@ $" + svc.priceUsdc + "/call");
}

// Then pay and call the one you want
const { data } = await buyer.pay(`${svc.hostedUrl}/enrich?domain=stripe.com`);

MCP configuration

Add Obol to any MCP-compatible AI agent (Claude, Cursor, etc.) so it can find and pay for services automatically.

// Add to your claude_desktop_config.json or mcp settings:
{
  "mcpServers": {
    "obol": {
      "command": "node",
      "args": ["packages/obol-mcp/dist/index.js"],
      "env": {
        "OBOL_AGENT_KEY": "0xYourAgentPrivateKey"
      }
    }
  }
}

MCP tools available to the agent once connected:

ToolWhat it does
find_serviceSearch the Obol marketplace by name or category
get_balanceCheck your Gateway USDC balance
depositDeposit USDC from your wallet into Gateway
pay_and_callPay for and call any Obol service URL

Dashboard guide

The dashboard at obol-arc.web.app/dashboard has two tabs: Use services (buyer) and Provide services (seller).

Wallet setup

Your Obol wallet is a self-custody EOA on Arc testnet powered by Circle User-Controlled Wallets (UCW). You hold the keys via a PIN — Obol never has custody.

To create:

  1. Go to Dashboard → Use services tab
  2. Click "Create my wallet →"
  3. Circle's PIN overlay appears — set a 6-digit PIN and three security questions
  4. Your Arc address appears and is saved to your profile automatically
  5. This address is your payout address for services you sell
Don't lose your PIN or security answers. They're the only way to recover your wallet. Obol cannot reset them.

Provide services tab

Everything a seller needs in one tab.

Earned
Total USDC you've received from all API calls across all services. Settled to your Arc wallet via Circle Gateway.
Calls served
Total number of paid API calls across all your listed services. Useful for tracking usage.
Active services
Number of services you've published to the marketplace.
Obol network fee (10%)
Shows how much of your gross earnings went to Obol. You collect 10% on Obol's behalf — it's deducted from your earnings not added on top.
Withdraw earnings
Move your earned USDC from your Arc wallet to any address on any supported network. Choose destination chain from the dropdown — Obol uses Circle CCTP to bridge if needed.
Your services
List of all services you've registered. Click + New service to add one.

Use services tab

The buyer view — fund your agent and monitor spending.

Gateway balance
Your unified USDC balance managed by Circle Gateway. This is what agents spend when they call paid APIs. Deposit here from any chain.
Total across chains
Total USDC you hold across all networks (Arc, Base, Ethereum Sepolia, etc.). Only Gateway balance can be spent on API calls — other chain balances need to be deposited first.
USDC by chain
Cards showing your balance on each chain with balance > 0, plus Arc always. Use Bridge → to move between chains. Use Deposit to Gateway to put Arc USDC into the unified balance.
Create my wallet
Sets up your UCW if you haven't already. Required before you can deposit, bridge, or withdraw.

Bridge & withdraw

Move USDC between chains and out of the platform.

Buyer dashboard — "Deposit to Gateway":
  Arc wallet → Circle Gateway unified balance
  Used to fund the agent's spending pool.
  Requires PIN confirmation (UCW).

Buyer dashboard — "Bridge →" on a chain card:
  Any chain → Any other chain
  Uses Circle CCTP behind the scenes.
  ~30 seconds to arrive.

Buyer dashboard — "Withdraw →" on Gateway balance card:
  Gateway balance → Any chain, any recipient address
  Pick destination network from dropdown (all 8 chains supported).

Seller dashboard — "Withdraw earnings":
  Your Arc wallet → Any address on any network
  Same cross-chain capability via CCTP.
  Enter recipient address manually.

Reference

Supported networks

NetworkChain keyUSDC addressCCTP domain
Arc Testnetarc0x360000000000000000000000000000000000000026
Base Sepoliabase0x036CbD53842c5426634e7929541eC2318f3dCF7e6
Ethereum Sepoliaethereum0x1c7D4B196Cb0C7B01d743Fbc6116a902379C72380
Avalanche Fujiavalanche0x5425890298aed601595a70AB815c96711a31Bc651
OP Sepoliaoptimism0x5fd84259d66Cd46123540766Be93DFE6D43130D2
Arbitrum Sepoliaarbitrum0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d3
Polygon Amoypolygon0x41E94Eb019C0762f9Bfcf9Fb1E58725BfB0e75827
Unichain Sepoliaunichain0x31d0220469e10c4E71834a79b1f276d740d3768F10

Pricing & fees

FeeAmountWho paysWhere it goes
Obol network fee10% of call priceSeller (deducted from earnings)Obol treasury
Circle Gateway fee0% (testnet)
Gas$0 (gasless)Circle covers on Arc
CCTP bridge fee~$0.001Initiator of the bridgeCircle/CCTP

Example: You set your price at $0.002/call. The agent pays $0.002. You receive $0.0018 (90%). Obol keeps $0.0002 (10%). The agent pays no extra fees on top of $0.002.

HTTP 402 explained

HTTP 402 ("Payment Required") is the standard status code for paywalled content. Obol's middleware returns a 402 with a X-Payment-Required header containing Circle Gateway payment instructions. Compatible buyers (those using the GatewayClient SDK or the Obol MCP) handle this automatically.

# What an unpaid request looks like:
HTTP/1.1 402 Payment Required
X-Payment-Required: {
  "scheme": "circle-gateway",
  "network": "eip155:5042002",
  "maxAmountRequired": "2000",    // in USDC micro-units (6 decimals)
  "sellerAddress": "0xSeller...",
  "facilitatorUrl": "https://gateway-api-testnet.circle.com"
}

# The GatewayClient SDK reads this header, signs an off-chain EIP-3009
# authorization, submits to Circle Gateway, then retries the original
# request with a signed payment proof in the Authorization header.

Wallet types

Wallet typeUsed forHow it worksKeys
UCW (User-Controlled)Sellers, dashboard humansPIN overlay via Circle SDK. Human approves each spend.You hold via PIN
Agent EOA (raw private key)Buyers, AI agentsScript signs autonomously with OBOL_AGENT_KEY. No PIN, no popup.You hold private key
DCW (Developer-Controlled)Legacy Obol platform walletCircle holds keys server-side. Used before UCW was added.Circle holds
Why agents can't use UCW: UCW requires a human to enter a PIN for every spend. Agents run autonomously without human input. Agents use a raw EOA private key instead — they fund it, set OBOL_AGENT_KEY, and the SDK signs payments automatically.