Sell a service

Someone on the other side of every paid call listed a service and was paid for it; now that someone is you, with a config file, a script, or an AI assistant running the operator playbook. A quick path needs no code and a full path is for developers; both end in a listing that agents find on their own, pay per call with a signed receipt, and rate from what they were served. You issue no API keys, open no accounts for your buyers and send no invoices; the setup project that used to sit between a service and the people who wanted it is gone from your side too. The demand side, Claude or your own code paying from your wallet, is Bring your own agent.

The quickest way is the app's Earn page (/earn): pick a ready-made listing, import an OpenAPI spec, upload a skill, sell your hardware, or describe your own API, model or wrapper; set a price and a stake, make one test call through the node, and go live with one click through the node on your machine, or download the config to run it anywhere. By hand, you see every piece and run it wherever you like.

Why list

A provider integrates nothing beyond the API it already runs: the spec it publishes is the listing, every operation a tool, the key on its own node. Whoever holds a key can list a service, and the first-party listing earns the reputation: once the domain names the node the listing carries the Official mark, ranks first among equals and shows its demand. Agents buy on merit, choosing at every call on price, rating and speed, so a listing competes on what it delivers, and demand pricing tells you when to add capacity. The network takes its share on settlement, 5% of what was paid, so listing costs nothing beyond stake, and the stake can be backed: 100 ZAI of your own, and the network's staking node reviews the listing and delegates the rest. A company with an API of its own: Integrate your service directly.

A node you start serves the AIOUM network (point it at the network's deployments.json) or your local development network (scripts/devstack.sh up). Everything is identical on both except the wallet: on the AIOUM network you buy ZAI and gas instead of minting them.

Which model fits you

Four kinds of people, four paths.

You havePathWhat runs
An API key, a hosted model, or a local Ollama model, and no wish to write codeQuick paththe multi-service node from services.json
The same, but you want an AI assistant to do the setup and the day-to-dayQuick path + let an assistant do ita coding agent with this repo checked out
Custom tools you write in TypeScriptFull paththe same node software with code tools, or the single-service node
A relay business: resell other operators' services with metadata privacyprivacy designnpm run relay

What a service is, in one paragraph

A service is an on-chain listing (category, price per call, description URI, the model you declare) owned by an operator identity that has staked ZAI. Agents open an escrow session against it, send one signed receipt per call (a voucher, in the code and the metrics), and your node settles the latest receipt on-chain. The gross splits: 5% protocol fee, the share you promised your delegators (default 10%), the rest to you. Your stake is what agents trust: it caps the escrow you can hold open at once (0.1 USDC per staked ZAI at the default parameters) and is what gets slashed if a dispute goes against you. Once a session settles, the paying agent rates your operator identity on-chain from what the run saw, and the next agent reads that rating before it chooses you. Economics has the numbers and why.

Quick path (no code)

Ten minutes from "I have an API" to "agents pay me per call", in eight moves.

1. Get the repo and the node software.

git clone https://github.com/oum-dev/aioum && cd aioum/offchain && npm ci

2. Have a network. The AIOUM network: put the network's deployments.json in offchain/ and set RPC_URL. Your local development network: from the repo root, scripts/devstack.sh up (it deploys the contracts and writes offchain/deployments.json).

3. An operator wallet. Make a dedicated wallet and fund it. On the local development network, skip this: the node software uses a funded Anvil account and mints the ZAI it needs:

echo "0x$(openssl rand -hex 32)"     # private key; derive the address as in the paying-agent guide

Send it a little ETH for gas and the ZAI you stake of your own: at least minSelfStake (100 ZAI) to list. The node is live at minNodeStake (10,000 ZAI) of total stake, yours plus what is delegated to you, so either stake 10,000 yourself (stakeZai per service, or AIOUM_SELF_STAKE) or put up 100 and request backing: the Get backed to go live card on the Earn page's go-live step, aioum stake request <serviceId>, or aioum_stake_request from Claude. The network's staking node reviews the listing for real and its pool delegates the rest in seconds (how it decides). Then either OPERATOR_PRIVATE_KEY=0x... in .env, or better, an encrypted keystore via OPERATOR_KEYSTORE_FILE and OPERATOR_KEYSTORE_PASSWORD_FILE.

Where your node answers. The node writes its own address on chain when it starts, signed by your key, and every directory finds your listings there. Two ways. Behind a relay (AIOUM_NODE_TUNNEL, or "Behind a relay" on the Earn page): the node dials out, is reached at <operator>.<relay>, and this machine's address never goes on chain; the way for a machine at home. A direct address (AIOUM_NODE_PUBLIC_URL): no domain needed, at an https address on your server's IP the node serves a certificate it made itself and pins its fingerprint on chain; a name behind Caddy only for browsers (how it works).

4. Describe what you sell. The fastest way is the template gallery: ninety ready-made listings, price feeds, DEX quotes, token and memecoin data, RWA, prediction markets, on-chain reads, analytics, crypto news, Telegram / Discord / X / Farcaster, memes, plus public data, search, LLMs, local models, media and scraping, each a tested config (the crypto toolbox).

npm run templates -- list
npm run templates -- add weather-open-meteo crypto-coingecko jina-reader llm-openrouter --out services.json

The app's Earn page is the same one listing at a time: pick a template, set its price and stake, and copy the services.json it writes. Full list with what each needs: template gallery reference. To write an entry by hand, start from the example:

cp services.example.json services.json

Each entry is one listing with one or more tools built from an adapter. Keep the entries you want, delete the rest, and change three fields: category, priceUsdc, and the tool's target. The adapter depends on what you sell.

You are sellingAdapterFill in
any HTTP APIresturl with {arg} placeholders, optional headers, body, responsePath
an OpenAI-compatible model (OpenAI, OpenRouter, vLLM, LM Studio)llm-openaimodel, apiKeyEnv, optional baseUrl, system, maxTokens
a local model through Ollamaollamamodel
a model as a planner for other people's assistant agents, paid per turnllm-plannermodel, optional apiKeyEnv, baseUrl; category planner:*
any fal.ai image, video or audio modelfalmodel slug, mode: "queue" for long jobs, responsePath
any Apify Actor (scrapers, automations)apifyactor, optional static input, mode: "async" for long runs (guide)
Bright Data web access or datasetsbrightdatazone for pages and search results, or mode: "scraper" + datasetId (guide)

Keys can ride in a header with a prefix ("authorization": "Bearer env:MY_KEY") or inside a URL (&appid={env:MY_KEY}); both resolve from the environment when the node starts. Secrets never go in the file. Write "apiKeyEnv": "OPENAI_API_KEY" or a header value of "env:MY_KEY", put the real key in .env, and the node refuses to boot if it is missing.

5. Set the one setting that matters in .env: AIOUM_NODE_TOKEN=<a long random string>. It protects the operator-only endpoints (/settle, /metrics). Without it the node runs, but prints a warning and anyone can force-settle your sessions.

6. Start it.

AIOUM_SERVICES_CONFIG=./services.json npm run multinode     # or: docker compose up -d
curl -s localhost:8546/health                               # {"ok":true,...}
curl -s localhost:8546/services | head -c 600               # your listings, with ids and prices

On boot the node software stakes and registers each service not already registered, then serves paid calls, records transcripts, anchors results roots, and settles earned receipts in the background, retrying until they land. From this moment your listing is on the marketplace, and agents find it without introduction.

7. Watch the money. Your net share arrives in the operator wallet as USDC at every settlement; the delegators' share accrues in the staking contract. curl -H "Authorization: Bearer $AIOUM_NODE_TOKEN" localhost:8546/metrics shows calls served, payment rejections, upstream errors and the value of receipts not yet settled.

8. Change a price. Edit services.json and restart; the node charges and advertises the new price in /services. The on-chain listing keeps the price it was registered with: the node reuses your live listing in the same category and calls updateService only to mark one inactive when you remove it, so to change the on-chain pricePerCall, call ServiceRegistry.updateService yourself, or delist with DELETE /admin/services/:key and list again. A listing with a pricing block is the exception: it changes its own on-chain price with demand (next section).

Pricing rule of thumb. Net per call = price × (1 − 0.05 − your delegator share). At the defaults that is 85% of the price. A gpt-4o-mini completion that costs you about 0.001 USDC upstream and is listed at 0.02 USDC nets about 0.016 USDC. Price above your upstream cost plus the gas you pay to settle; settlement is batched per session, so many small calls share one transaction.

Get reviewed

Agents choose tools by their history, and a new listing has none. So every listing gets a first use: within minutes of going live a runner reviews it, and the result counts like an agent's run.

A review is a real use. The runner calls each of your tools with the sample input from your template, through the paid path: a session, a signed receipt, your price. You are paid for the calls. It checks the answer against the tool's output schema when the tool states one, and has the result judged by what it is: an image or a clip by the image judge, text by the text judge. Audio and 3D are called and timed, not judged. When a first score lands between 40 and 79, the runner rewrites how the tool is used, once, from the judge's reasons, and calls it again; the better score stands. A skill is reviewed by loading it: skill.get must answer with a SKILL.md that names the skill, describes it and carries instructions, and a self-test declared as check: in its frontmatter runs in a container with no network.

Your listing then carries one line, on its card and on its page:

LineWhat it says
Reviewed · 86 · 3 days agothe mean score of the tools that were judged, and when
Reviewed · not judged · todaythe tools answered; nothing judges their medium
Not reviewed yetno review has run
Could not review: …why, in plain words

Click the line for the reasons: each tool's score and the judge's words, latency, the price charged, the answer against the schema, who reviewed and when, and What to fix. A tool that could not be called gets no score: the line says why.

That first review is one runner's. For a score from several reviewers the chain picks at random, ask for a paid review: you deposit ZAI, staked reviewers use your listing and seal their scores.

Could not reviewWhat to do
no node serves it: its template needs a key on the node that lists itset the key on the node, restart it
its template has no sample input for itadd a sample for each tool to the template
the listing is not livestake to the minimum, or Get backed
a call is over what is left of the review budgetask with a budget of your own: aioum review request 2558 --budget 1
a tool was called with its sample input and failedthe upstream's message names the fault; fix it, ask again

Ask for a paid review on the listing's page: Paid review, the number of reviewers, and the cost in ZAI read from the contract. The chain draws bonded reviewers, each uses the listing as a customer and seals a score, and the median is on the card from then on (Paid reviews):

aioum review request 136 --reviewers 3 --quote   # what it costs
aioum review request 136 --reviewers 3           # the deposit, then the request
aioum review chain 136                           # the open requests, the results
aioum review status 136                          # your own runner's review: scores by tool, what to fix

An AI assistant asks with aioum_review_request. A listing on a node the directory does not know yet is named by the node and its key: {"nodeUrl": "https://your-node", "key": "weather"}. One request per listing per day per person; a listing under review is not queued twice. A listing that was reviewed is reviewed again when it changed (its template, its tools, its price, its node) or a day after its last review.

A tool that costs more than a review spends. A review spends 0.25 USDC at most, so a tool priced above that is reviewed on its operator's word. aioum review request 2558 --budget 1 signs the request with your wallet, and the runner takes it from the listing's operator, or from the runner's own owner, up to 5 USDC (REVIEW_BUDGET_MAX_USDC) and within what its day has left. Over HTTP the body carries budgetUsdc, at (the time now) and signature over aioum-review-request:<serviceId>:<budgetUsdc>:<at>.

What a review counts for. One review is one rated use of your listing, however many tools it called, in the same record agents' runs write to. Agents trust a listing after three rated uses at 80 or more, so the review starts the count and the first agents finish it. A review at 80 or more also puts your tool first in line for one search in ten: How new tools are tried. Under 80, setups pass the tool over until you fix what the reasons name: only a listing's latest review counts toward its rating, so a tool reviewed at 60, fixed and reviewed at 90 is rated 90. The earlier reviews stay in its history. What agents' own runs found keeps averaging as it does.

A runner spends at most REVIEW_BUDGET_USDC (0.25) on one listing, tools and judge together, and REVIEW_DAILY_BUDGET_USDC (5) a day. npm run reviews:backfill in offchain/ queues every live listing without a history, oldest first, 20 at a time (-- --limit 50 for more), and says how each ended.

Pricing that follows demand

A fixed price is one number for every hour of the day. A listing may instead carry a pricing block, and the node moves its on-chain price with the load it measures on that listing:

{
  "key": "weather", "category": "data:weather", "priceUsdc": 0.002,
  "pricing": { "base": 0.002, "max": 0.008, "targetInflight": 2, "window": 300, "step": 0.25 },
  "tools": [ "…" ]
}

base is the floor (your priceUsdc) and max the ceiling. Every window seconds the node looks at the average number of calls in flight on the listing over that window. At or above targetInflight it raises the price one step (25%); below, it lowers it one step toward base. Never above max, never below base, one change per window, and every change is an on-chain updateService the node logs with its reason:

[pricing] weather: 0.002 USDC → 0.0025 USDC (up 25%: 3.4 in flight on average over 300 s (1204 calls), target 2)

The new price applies to the next call: a paying agent reads it from your /services, or from the 402 answer, before it signs, so nobody pays more than they signed for. After a restart the node keeps the price the chain holds, clamped to base–max, rather than snapping back to priceUsdc.

From the CLI, --price-max turns it on: aioum sell add --template weather-open-meteo --price 0.002 --price-max 0.008. From Claude, aioum_sell_add takes pricingMax. On the Earn page the price step has Let the price follow demand, up to $X. GET /services on your node shows pricing (current, base, max, the last decision) and load (calls in flight, in the window, in the last hour) for every listing, fixed-price ones included.

A busy listing at a higher price is a signal, and the marketplace shows it. The indexer keeps every listing's price history from the chain's ServiceUpdated events and counts its calls per hour: the node's own count of calls served when the indexer can reach the node, else the sessions settled on-chain in the last hour (settled amount ÷ the price in force). Each listing carries one line, "price up 25% today · 340 calls/h · 3 operators serve this", on its card and its page, and GET /services/:id/demand has the trail behind it. An operator sees where money is; an agent that ranks by price picks the cheaper copy; the copy brings the price down. The whole loop is in economics.

Run someone's listing yourself

Every listing that came from the gallery names its template: template in the node's /services, in the index, on the service page. The page's Run your own copy opens Earn with that template, the listing's price (its floor when it follows demand), its ceiling and the own-stake floor filled in, and says which key the copy needs; the page shows the same as one CLI line and one aioum_sell_add call. A copy is a listing of your own: your node, your key, your stake, your price. Two copies of one template are two operators serving it, and the cheaper one gets the calls first.

Let an AI assistant do it

Any coding agent that runs commands and edits files can operate a node from this repo, so the eight moves above can be one brief. With Claude Code (npm install -g @anthropic-ai/claude-code), open the repo and give it a brief like:

You are operating an AIOUM service node from this repository. Read
docs/guides/node-quickstart.md and docs/reference/node-config.md first.

I have an OpenAI API key in the environment as OPENAI_API_KEY and an Ollama server on
localhost with llama3.2. Create offchain/services.json with two services: a gpt-4o-mini
completion at 0.02 USDC per call and a local llama3.2 completion at 0.005 USDC per call.
Generate a node token into offchain/.env, start the multi-service node against the local
development network, verify /health and /services, and report the service ids, the prices, and
the operator address that receives earnings. Never print or move private keys; reference
secrets as env:NAME.

The assistant is good at the work that used to eat an operator's evenings: it writes and validates services.json, starts and restarts the node software, reads /health and /metrics, adjusts prices and explains a dispute. Two things it must never do: hold or transmit your operator key (keep it in .env or a keystore; the assistant needs only the variable name) and stake more than you told it to. The operator playbook is its standing brief for day to day.

Full path (technical)

The full path is the quick path with the lid off: the same node software, seen one layer down, so you can write your own tools, tune what the contracts know about you, and run more than one instance.

1. Keys and funding

The node software resolves the operator signer in this order: encrypted keystore (OPERATOR_KEYSTORE_FILE + password), OPERATOR_PRIVATE_KEY, then, on the local development network only, an Anvil account index. On the AIOUM network it refuses to start without one of the first two. Fund the wallet with gas and ZAI; to stake by hand before the node software starts, the manual approve + stake is in stake & delegate. All settings: operator configuration.

2. Identity, stake, listing

Each service entry drives three on-chain facts, all idempotent at boot: an ERC-8004 identity for the operator, your own stake in the StakingManager (stakeZai, at least minSelfStake; the listing is live once total stake, own plus delegated, reaches minNodeStake, which the network's staking node supplies on request), and a ServiceRegistry listing (category, pricePerCall, descriptorURI, model). Two per-service knobs live on-chain. PaymentManager.setServiceSplit(serviceId, delegatorShareBps) is the slice of your gross that goes to people who stake on your node; put delegatorShareBps in the service's entry and the node software sets it at boot, or call it yourself. More share attracts more delegated stake, which raises the escrow you may hold open. ServiceRegistry.setCommitRequired(serviceId, true), which has no field in the file, is a promise that every paid session ends with an anchored results root, so buyers can verify what you returned. Paid settles of a flagged service revert without a root; the node software anchors automatically, so turning it on is a credibility signal at no extra work. Every function: contracts reference.

3. Tools: adapters or code

Adapters cover most reselling. For anything else, a tool is an object with a name, a description, and a run(args) returning a string (sync or async), declared in the catalogue in offchain/src/node/services.ts:

{
  key: "geocode",
  category: "data:geocode",
  priceUsdc: 0.003,
  stakeZai: 12_000,
  tools: [{
    name: "lookup",
    description: "Coordinates for a place name.",
    run: async (a) => JSON.stringify(await geocode(String(a.place))),
  }],
}

Whatever the tool returns is hashed into the session transcript, so verifiability applies to code tools and adapters alike. One boundary: tools that act as a user (their Gmail, their calendar) cannot be sold on the marketplace, because no stake makes a leaked inbox whole; that is what the buyer-side private node is for.

4. Run it

Two shapes of node. A single service runs with npm run node and serves the tools in tools.ts on AIOUM_NODE_PORT (8546). A catalogue runs with AIOUM_SERVICES_CONFIG=./services.json npm run multinode, or docker compose up -d with services.json and deployments.json next to the compose file. Behind a reverse proxy, use the deploy/Caddyfile template and set AIOUM_NODE_TRUST_PROXY=1 only then.

When one key serves more than one instance, set AIOUM_STORE=sqlite and give each instance a different AIOUM_NODE_PORT; they share one receipt and transcript store and elect a single settler, and a killed leader fails over within AIOUM_SETTLE_LEASE_MS.

Everything you must not lose lives in AIOUM_NODE_DIR: accepted receipts are earned money and transcripts are your evidence. Back it up, and wipe it only when you reset your local development network.

5. Getting paid

The settle loop redeems each session's latest receipt on-chain: promptly on close, on a timer otherwise, with fee bumps and retries that never drop an entry. POST /settle (bearer token) forces one. At settlement your net is transferred to the operator address; the delegator share goes to the staking contract, where your commission on it (StakingManager.setCommission) is claimable. One settlement in full: payments guide; the demo's money-flow report shows the exact split.

6. Disputes, from your side

A buyer can dispute an open session, or a settled one inside the challenge window, by posting a bond. If the jury finds against you, part of your stake is slashed and the buyer refunded; if not, you receive the buyer's bond. Your defence is the transcript your node already keeps: export the evidence with the verify-evidence CLI (npm run verify-evidence -- --export) and it will match the anchored root. How juries decide: arbitration; what can be proven: dispute verifiability.

7. Growing

Growth is being found and being trusted, and agents do both. Agents allowed to find their own tools search the marketplace during a run, call what fits within their budget, and may try your service against a rival's and keep the better result. A clear category (llm:, data:, media: prefixes) and an honest model string are what those searches match on. The indexer and the app rank listings by stake, reputation and price, and agents write the reputation after every settled session, so a listing that answers quickly and correctly rises and one that fails sinks. Attract delegators with a meaningful delegatorShareBps: delegated stake raises your escrow capacity, the ceiling on concurrent paying sessions. To resell rather than serve, run a relay (npm run relay) and earn the margin on every proxied call.

The operator playbook

A standing brief for whoever, or whatever, runs the node day to day. Every step maps to a command or an endpoint above.

Every few minutes, confirm that GET /health is ok:true, that RPC is reachable and that the operator wallet has gas. Watch aioum_payment_rejections_total and aioum_tool_errors_total in /metrics: neither should be climbing. Rejections mean buyers are sending bad receipts or expired sessions; tool errors mean your upstream is failing and you are refusing calls you would have been paid for, and each one costs you points in the buyer's rating.

Daily, check that aioum_vouchers_held (unsettled exposure) is small, which means settlement is landing; if it grows, check the settle loop log for reverted transactions and gas. The USDC balance of the operator wallet should have grown by roughly calls served × net price, and your upstream spend (your API bills) should be below what you earned. If not, raise priceUsdc and restart.

Weekly, compare your price and reputation against the same category in the indexer and adjust. Check for open disputes against your operator address (DisputeRaised events on the DisputeManager, or the Disputes tab under Activity on the app's ZAI page), and export evidence for any you find. Back up AIOUM_NODE_DIR.

Monthly, weigh stake against capacity: if sessions are being refused for exposure, add stake or raise the delegator share. Rotate AIOUM_NODE_TOKEN and review who can reach the box.

Never run two instances on one operator key without AIOUM_STORE=sqlite, never put a secret in services.json, and never reset your local development network without wiping AIOUM_NODE_DIR.

Troubleshooting

SymptomCause and fix
boot fails needs env var Xan adapter secret is unset; put it in .env
deployments.json not foundno deployment for this chain in offchain/; on the local development network run scripts/devstack.sh up
another … instance already runssecond process on one data dir; stop it or use the shared store
setup attempt … failed … retrying at bootthe operator wallet lacks ZAI or gas; fund it and restart
/health 503 with a hint, or node not active on sessionsthe node's total stake is under 10,000 ZAI: aioum stake request <serviceId> (or the card on the Earn page) asks the network's staking node to back it; else find delegators, or stake more of your own (AIOUM_SELF_STAKE)
calls 402 with session is not openthe buyer's session settled or expired; nothing to fix on your side
results root required on settleonly the single-service runtime (npm run node) hits this, when it was started with AIOUM_COMMIT_RESULTS=0 on a commitRequired service; re-enable it. The catalogue node always anchors the root before settling
earnings not arrivingcheck /metrics for held vouchers and the settle loop log for reverts

Your node is live, staked and paid per call. What agents buy most, in crypto, is a small set of tool families, composed into bots.

Next: Sell your GPU — your own hardware as a listing: Docker, the NVIDIA toolkit, jobs priced per job with a minute rate.