Guides · Per-call billing
This playbook meters every agent API call as a signed receipt. The payer streams one receipt per call at web2 speed, the receiver tallies a running balance off-chain, and a single RAV redemption settles everything once the balance crosses the trust threshold.
The payer opens a stateless one-way channel and posts a deposit. The deposit is the receiver's collateral: it caps how much unpaid work the receiver is ever exposed to, so size it above the trust threshold you expect the receiver to run.
import { Payer } from "@velapay/sdk" const payer = new Payer({ signer: process.env.VELA_KEY }) const channel = await payer.openChannel({ to: "agent:research-api.v1", deposit: "$50" // collateral the receiver can verify on-chain })
Per-call billing means one action, one price. Agree the rate out of band — the receiver publishes it, the payer pins it — and both sides reference the same action id on every receipt. If the receiver changes the price, the payer sees it before the next call, not on an invoice.
const rate = { action: "api/answer", price: "$0.002" // per call · published by the receiver } channel.pinRate(rate) // refuse receipts above this price
Wrap the call site. After each successful invocation, sign a receipt against the channel and fire it to the counterparty. The signature is local and the send is fire-and-forget, so nothing chain shaped sits in the hot path.
async function answer(query) { const res = await counterparty.invoke("api/answer", query) const receipt = channel.sign({ action: rate.action, units: 1, // one call · $0.002 nonce: channel.next() }) counterparty.send(receipt) // fire-and-forget, no round-trip return res }
The receiver checks each receipt's signature and nonce, then adds it to a local running balance. Verification is a signature check, not a chain call, so it keeps pace with any call volume you can serve.
import { Receiver } from "@velapay/sdk" const receiver = new Receiver({ threshold: "$25" }) receiver.on("receipt", (r) => { if (!receiver.verify(r)) return receiver.reject(r) receiver.tally(r) console.log(receiver.balance()) // $0.002 → $0.004 → … })
The trust threshold is the most unpaid work you will do for this payer before settling. Cross it and the receiver builds a RAV — one aggregate voucher over every tallied receipt — and redeems it on-chain in a single transaction. Tuning it is its own topic; see setting trust thresholds.
const receiver = new Receiver({ threshold: "$25", // max unpaid exposure onThreshold: async () => { const rav = receiver.buildRAV() await receiver.settle(rav) // 1 tx · every receipt so far } })
After redemption the ledger shows one settled line for thousands of calls, plus whatever has tallied since. The settled amount is drawn from the payer's deposit; the unsettled remainder simply starts the next cycle toward the threshold.
const ledger = await receiver.ledger("agent:research-api.v1") console.log(ledger.settled) // $25.00 · 12,500 receipts · 1 tx console.log(ledger.unsettled) // $0.37 · accumulating again console.log(ledger.deposit) // $25.00 remaining collateral
Receipts can stop arriving while calls keep coming — a crashed payer, a drained key, or bad faith. The protocol makes this boring: the receiver's exposure is capped at the threshold, so it stops serving there, keeps every signed receipt as cryptographic proof of the work, and redeems what's owed against the deposit. The worst case is one threshold's worth of work, which is exactly the number you chose.
receiver.on("exposure", async (payer) => { receiver.pause(payer) // stop serving at threshold const rav = receiver.buildRAV() await receiver.settle(rav) // redeem what's owed · signed proof })
The quickstart walks the full loop in five steps, and the API reference documents every method used in this guide.