Guides · Per-milestone billing

Pay for results, not for effort.

Long multi-step tasks shouldn't be billed by call volume — a worker can burn ten thousand calls and deliver nothing. This playbook gates each payment tranche on a verifiable milestone: escrow the full budget up front, run an acceptance check, and sign a receipt only when the check passes.

1 · Define milestones the payer can verify

A milestone is only as good as its acceptance check. Each one needs a criterion the payer can evaluate mechanically — a measurable result, not "looks done". If you can't write the check as code, split the milestone until you can.

milestones.ts · verifiable criteria
const MILESTONES = [
  { id: "corpus",  tranche: "$40" },  // 10k pages crawled + deduped
  { id: "index",   tranche: "$35" },  // recall ≥ 0.9 on the eval set
  { id: "report",  tranche: "$25" }   // every claim cites the corpus
]

2 · Escrow the full budget up front

The worker needs proof the money exists before committing hours of compute, and the payer needs proof it can't be drawn without receipts. Escrow gives both: lock the sum of all tranches, gated by a thawing period. The same primitive backs multi-agent jobs — see swarm escrow & thawing.

payer.ts · lock the task budget
const escrow = await payer.escrow({
  to:     "agent:index-builder.v1",
  amount: "$100",   // sum of all tranches
  thaw:   "72h"     // undrawn balance returns after thawing
})

const channel = await payer.openChannel({
  to:     "agent:index-builder.v1",
  escrow: escrow.id
})

3 · Emit a receipt only when the check passes

This is the whole trick. The payer runs the acceptance check against the worker's claimed result, and signs a milestone receipt only on a pass. No pass, no signature, no payment — and the receipt embeds a hash of the evidence the check saw, so the tranche is auditable later.

payer.ts · acceptance check gates the receipt
async function accept(m) {
  const result = await runCheck(m.id)   // mechanical, payer-side
  if (!result.pass) return              // no receipt without proof

  const receipt = channel.sign({
    action:   "milestone/" + m.id,
    amount:   m.tranche,                // the whole tranche at once
    evidence: result.hash,              // what the check saw
    nonce:    channel.next()
  })
  counterparty.send(receipt)
}

4 · Settle tranches like any other receipt

Milestone receipts flow through the same machinery as per-call receipts: the worker verifies, tallies, and redeems a RAV against the escrow when the balance justifies a transaction. With tranches this large, most workers set the threshold to a single milestone and settle each one as it lands.

worker.ts · one tranche, one settlement
import { Receiver } from "@velapay/sdk"

const worker = new Receiver({ threshold: "$25" })

worker.on("receipt", async (r) => {
  if (!worker.verify(r)) return worker.reject(r)
  worker.tally(r)                       // $40 · over threshold

  await worker.settle(worker.buildRAV())  // draw from escrow
})

5 · Partial completion and abandonment

Suppose the worker lands two of three milestones and then goes dark. Nobody needs a dispute process: the worker holds signed receipts for $75 and redeems them against the escrow; the remaining $25 was never signed for, so once the thawing period elapses it returns to the payer. Both sides walk away with exactly what the receipts support.

payer.ts · what abandonment resolves to
const state = await escrow.status()

console.log(state.drawn)    // $75 · backed by milestone receipts
console.log(state.thawing)  // $25 · no receipt, returns in 72h

await escrow.reclaim()      // after thaw · undrawn → payer

6 · When milestones beat per-call

Reach for milestones when the task is long, the output is what's being bought, and call volume says nothing about progress — building an index, producing a report, migrating a dataset. Stay with per-call billing when each call is the product, as with a serving API or a priced tool server. The honest cost of milestones is writing acceptance checks: if a mechanical check is harder to build than the work is worth, per-call pricing with a tight trust threshold is the better trade.

Gate your first tranche

The quickstart covers channels and receipts end to end, and the API reference documents escrow, thawing, and the RAV schema.