Guides · Swarm escrow

Escrow once. Let the whole swarm draw.

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.

1 · Fund the escrow for a task

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.

orchestrator.ts · fund the escrow
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
})

2 · Register the workers

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.

orchestrator.ts · allow-list workers
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

3 · Workers draw against the shared balance

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.

worker.ts · redeem against the escrow
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
})

4 · Withdrawals thaw before they finalize

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.

worker.ts · a withdrawal in thaw
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
// }

5 · Challenges: clawback or release

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.

orchestrator.ts · challenge during thaw
// 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

6 · Return the undrawn remainder

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.

orchestrator.ts · close and reclaim
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"

What thawing does not protect against

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.

Keep going

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.

Read the quickstart Browse the API reference