Guides · Swarm escrow
When a task fans out across many workers, you don't want a funding decision per agent. Escrow one shared balance for the task, allow-list the workers, and let the contract enforce the cap. Withdrawals pass through a thawing period, so payers keep a dispute window even after the work is done.
The orchestrator locks the task budget in the escrow contract before anything fans out. The amount is the hard ceiling on what the swarm can earn, and the thaw window is the dispute period every withdrawal will respect later.
import { Payer } from "@velapay/sdk" const payer = new Payer({ signer: process.env.VELA_KEY }) const escrow = await payer.openEscrow({ task: "task:index-crawl.2026-07", amount: "$1,200.00", // shared balance for the whole swarm thaw: "72h" // dispute window on every withdrawal })
The escrow only honors receipts from keys you allow-list. Register the swarm's worker identities up front; anything else that shows up with a receipt fails verification, no matter how well it's signed.
await escrow.register([ "agent:crawler.a", // allow-listed worker keys "agent:crawler.b", "agent:parser.v3" ]) // unlisted keys cannot draw — their receipts fail verify, // so a compromised sibling can't quietly join the payroll
Each worker tallies receipts exactly as in the quickstart, then redeems its RAV against the escrow instead of a personal channel. Concurrency is the contract's problem, not yours: the escrow enforces the global cap even when workers race to settle, so simultaneous redemptions can never overdraw the balance.
import { Receiver } from "@velapay/sdk" const worker = new Receiver({ threshold: "$150" }) worker.on("receipt", (r) => worker.tally(r)) const rav = worker.buildRAV() await worker.settle(rav, { escrow: "task:index-crawl.2026-07" // draw from the pool })
A settled draw is not instantly spendable. Every withdrawal queues in a thawing state for the window you set when funding — 72 hours here — during which the payer can challenge it with evidence. If the window passes quietly, the withdrawal finalizes on its own.
const w = await worker.withdraw("$180.00") console.log(await escrow.status(w.id)) // { // state: "thawing", // amount: "$180.00", // thawEnds: "2026-07-05T09:00:00Z", // 72h window // challenge: null // open to disputes // }
During the thaw, the payer files a challenge with evidence — duplicate receipts, mismatched nonces, receipts signed outside the task window. A challenge that's upheld claws the disputed amount back into the escrow. A challenge that's rejected releases the withdrawal on its original schedule, and frivolous challenges cost the payer the filing bond.
// payer side · dispute a thawing withdrawal with evidence await payer.challenge(w.id, { reason: "duplicate-receipts", evidence: bundle // signed receipts + conflicting nonces }) // upheld → clawback: disputed amount returns to escrow // rejected → release: withdrawal finalizes on schedule
When the task is done, close the escrow. The contract waits for every outstanding thaw to resolve — finalized or clawed back — then returns whatever the swarm never earned to the payer's wallet. Nothing is stranded, and nothing leaves early: the same thaw that protects you from bad withdrawals also stops you from pulling funds out mid-task.
const receipt = await escrow.close() // waits for all thawing withdrawals to resolve, then // returns the undrawn remainder to the payer console.log(receipt.returned) // "$214.60"
Thawing is a dispute window for payment integrity — forged receipts, replayed nonces, draws for work that never ran. It is not a quality guarantee. If a worker delivers bad output that passes your own acceptance checks, the receipts are honest and a challenge will rightly fail. The fix for that lives upstream: gate receipts on verifiable results, as in the per-milestone billing guide, and keep per-worker budgets small enough that a bad actor's honest-looking draws stay survivable.
The quickstart covers the receipt loop every worker runs inside the escrow, and the API reference documents every escrow, thaw, and challenge method shown here.