Guides · Per-tool billing
One agent run touches many tools, and they don't cost the same to serve. This playbook prices each tool invocation distinctly on a single channel: a rate card per tool, a tool id on every receipt, receiver-side rate checks, and a per-tool cost report at the end.
The rate card is the contract:
one price per tool id, published by the receiver and pinned by the payer. Keep tool ids
stable and versioned — a receipt for tools/code.exec should mean the same
thing next month as it does today.
export const RATES = { "tools/web.search": "$0.0004", // cheap, high volume "tools/pdf.extract": "$0.0012", "tools/vision.ocr": "$0.0025", "tools/code.exec": "$0.0060" // sandboxed · 15× a search }
On the payer side, the tool id is the receipt's action and the pinned rate rides along. Nothing else about the flow changes: sign locally, fire to the counterparty, keep going.
async function invoke(tool, args) { const res = await counterparty.invoke(tool, args) const receipt = channel.sign({ action: tool, // e.g. "tools/code.exec" rate: RATES[tool], units: 1, nonce: channel.next() }) counterparty.send(receipt) return res }
The receiver never trusts the number on the receipt. Every incoming receipt is checked against its own copy of the card: unknown tool ids are rejected outright, and an off-card rate — stale card, buggy payer, or someone trying their luck — never reaches the tally.
receiver.on("receipt", (r) => { const card = RATES[r.action] if (!card) return receiver.reject(r) // unknown tool if (r.rate !== card) return receiver.reject(r) // off-card rate receiver.tally(r) // value = units × card rate })
One channel, many rates. The tally is still a single unpaid balance climbing toward one trust threshold — searches and sandbox runs just contribute different amounts per receipt. Settlement doesn't care either: the RAV aggregates every receipt regardless of rate, and one transaction settles the whole mix.
const run = receiver.tallyFor("agent:planner.v3") run.balance() // $1.42 across every tool run.balance("tools/code.exec") // $0.90 of it · 150 receipts run.balance("tools/web.search") // $0.28 of it · 700 receipts
Because every receipt carries a tool id, cost attribution falls out for free. Group the tally by action and you can see which tools earn their keep, bill your own customers per capability, or hand an agent operator a line-itemed view of where a run's budget went.
const report = receiver.report({ groupBy: "action" }) // tools/code.exec 150 calls $0.90 63% // tools/web.search 700 calls $0.28 20% // tools/pdf.extract 200 calls $0.24 17%
Two honest anchors. Cost-plus: start from what an invocation costs you to serve — compute, licenses, upstream API fees — and add a margin; sandboxed execution lands well above a cached search this way, which is the point. Value-based works only where the output has a measurable price to the caller, and it's easy to overreach with. In practice: cost-plus for commodity tools, value-based sparingly for genuinely scarce capabilities, and revisit the card when your report from step 5 shows a tool consistently under water. Publish changes ahead of time — payers pin rates, so a silent bump just means rejected receipts.
The quickstart covers the receipt loop end to end, and the API reference documents rate pinning, tallying, and reporting.