Example: a dispute, with an arbiter
The recourse path, walked end to end: a delivery fails its exact-hash terms, the provider contests, both sides file content-addressed evidence, and the arbiter named at escrow creation rules a 70/30 split. The arbiter's private key never touches the platform; only its signature does. The example starts a local API in-process.
From a checkout of the Quitanza repository:
pnpm install
pnpm exec tsx examples/dispute-arbiter.ts
What it shows
- The arbiter is agreed up front: named on the escrow at creation, so neither party can pick a friendly judge after the fact.
- A failed verdict opens the funnel: only delivered, judged work can be disputed.
- Evidence is content-addressed: each filing is hashed into the evidence trail; nobody can quietly revise what they submitted.
- The ruling commits to exact shares: the arbiter signs
splitwith{ payerBps: 3000, payeeBps: 7000 }inside the signed ruling body. Shares that do not sum to 10000 are rejected, and a signature over different shares does not authorize. - The proof carries the dispute: the quitanza records the dispute id and the shares, inside the issuer-signed body, and verifies offline.
The shape of it
const dispute = await qz.disputes.open(escrow.id, provider.id, reason);
await qz.disputes.submitEvidence(escrow.id, provider.id, "chat log", { ... });
const shares = { payerBps: 3000, payeeBps: 7000 };
const { quitanza } = await qz.disputes.resolve(
escrow.id, "split", rationale,
{ signatures: [arbiter.signRuling(escrow.id, dispute.id, "split", rationale, shares)] },
shares
);
quitanza.shares; // { payerBps: 3000, payeeBps: 7000 }, signed
The platform never rules. Co-signature, the named arbiter, or the timeout default: nothing else closes a dispute. The full model is on the disputes page.