Inspiration
Payment on the internet still works on trust: you pay, then you hope the work arrives. Escrow fixes part of that, but someone still has to read the work and decide. That doesn't scale when the worker is an AI agent shipping code at 3 a.m. for another agent.
We started from a darker question: How can we build a pyramid scheme, and why isnt the agent ic economy truly autonomous? Agents should be able to pay themselves with no human in the loop. This is how it started, follow along in the grok chat: https://x.com/i/grok/share/bGVnYWN5LWNvcHk_55eaae1c-66be-4849-b9bc-f34d4f797151
So we flipped it. Money should move only when the work stands up to a test the buyer signed before any work began. No pass, no pay. And the last payment in every chain should come from a signal no agent controls: a real person using the product.
The name says it: the work has to stand.
What it does
Stood is an open-source payment release gate on PayPal.
- The buyer signs once. A mandate lists the milestones, a cap and a deadline. Each milestone carries acceptance tests that are frozen and hashed at signing time.
- Money is held, not paid. When work starts on a milestone, Stood places a PayPal authorization (Orders v2,
intent: AUTHORIZE). The builder sees "held, not paid". - The builder submits a commit. Stood runs the signed tests against that exact commit in an isolated runner. The runner returns an Ed25519-signed report bound to the repository, the commit, the test-manifest hash and the runner image.
- Rules decide. Models only report. A deterministic decision table turns the evidence into one of three verdicts:
- RELEASE: capture the authorization, so the builder is paid.
- REFUSE: void the authorization and return a punch list ("3 signed tests were skipped").
- WAIT: a person reviews it, and nothing moves.
- The final milestone needs an outside signal. Under the
code.final@1profile, the last payment is released only after the buyer actually uses the delivered product.
The release rule for a milestone $m$ is deliberately boring:
$$ \text{release}(m) \iff \underbrace{H(\text{tests}m) = H{\text{signed}}}{\text{tests untouched}} \;\land\; \underbrace{\text{passed}_m = \text{total}_m}{\text{no skips, no failures}} \;\land\; \underbrace{t_{\text{now}} + 5\,\text{min} < t_{\text{expiry}}}_{\text{hold still capturable}} \;\land\; \big(\text{final}(m) \Rightarrow \text{used}(m)\big) $$
Anything uncertain becomes WAIT, never a guess.
Yard is the companion that shows the gate in use: "Yard builds. Stood pays." You describe an app, Yard's planner drafts a blueprint (milestones, frozen tests, budget), and builders (humans or agents) claim work orders. Every payment still goes through Stood, with no shortcuts. Yard is a caller of Stood's public API, like any other platform.
How we built it
- A TypeScript monorepo (pnpm + Turborepo) with a pure, I/O-free domain core: money, tranche state machine, decision table, evidence profiles. Domain code has 100% branch coverage, enforced in CI, and was written test-first.
- PayPal Orders v2 / Payments v2 through the pinned PayPal Server SDK: authorize, capture, void, and reauthorize from day 4 within the 29-day hold window. Every PayPal call is committed to a Postgres operation ledger before it's sent, so a crash or a lost reply can be reconciled by reading PayPal's status, never by blindly retrying a capture.
- A Hono HTTP API with signed (Bearer + HMAC) requests, platform-scoped idempotency and exact replay of stored responses.
- Postgres + Drizzle: an atomic operation ledger, versioned tranche streams, a recovery record that replays only pure domain transitions, and a reconciliation queue using
SKIP LOCKEDleases. - Signed runner reports (Ed25519), bound to the frozen contract, with a five-minute freshness window.
- A mock-first provider layer. We didn't have sandbox keys at first, so every outside service sits behind a port with three adapters: fake, simulator and live. Our PayPal simulator speaks the real wire protocol, so the real SDK runs against it over HTTP. It injects timeouts, 5xx and rate limits, and lost replies that arrive after the money moved. Production builds can't import a fake: a dependency rule and a release-artifact test enforce it.
- Docker-only development and a strict pre-commit gate: gitleaks, actionlint, zizmor, markdownlint, a link checker, Biome, a banned-hype-words check, DCO, and the full Docker test, coverage and build run on every commit and in CI.
- A GitFlow workflow with release-please, and a landing page and changelog on GitHub Pages.
Challenges we ran into
- "Did the capture happen?" The hardest bug class in payments is the reply you never got. We designed for it from day one: stable request IDs, an operation ledger, an
AMBIGUOUSstate, and reconciliation that needs complete matching proof from PayPal before it records any outcome. - Holds don't last forever. A PayPal authorization has a 3-day honor period and a 29-day limit. That forced explicit timers: reauthorize from day 4, a five-minute safety margin before captures, and safe expiry.
- Keeping models out of the money path. The AI is great at reporting ("these tests were skipped"). It must never decide that money moves. We encoded that as an architectural rule, enforced in code, not as a guideline.
- Agent pyramids. If agents can pay agents, they can farm each other. Reputation counts only releases from buyers outside the operator's own tree, and the final payment in every chain requires real human use.
- Building without keys. We built a faithful simulator, then caught it being more lenient than PayPal in places (for example, it allowed a second reauthorization). We fixed it, and the next step is checking it against recorded sandbox behaviour.
- Two clocks. Simulated time and real time drifted apart in the first mock runs. Now Stood reads one controllable clock from the simulator.
Accomplishments that we're proud of
- The birth of an entirely new project for the disabled community called Talk to crew. Crew is the agantic engineering team that gets paid via stood.
- 4,432 Clones so far on the project
- An honest UI and API: every simulated result is labelled as simulated, and nothing claims to be paid until PayPal confirms the capture.
- A gate that any platform can call, demonstrated by building our own companion product, Yard, strictly through the public API.
What we learned
- Write the failure path first. Most of the real engineering in payments lives in the unclear cases: timeouts, retries, expiry, partial knowledge.
- "Rules move money; models only report" is a design constraint that makes AI safer and simpler to test.
- A simulator is only useful if it is as strict as the real thing. Being more lenient is a bug.
- Tests make a better contract than prose. When "done" is a signed, hashed test suite, there's nothing to argue about.
What's next for Stood: agents pay agents only when the work stands
- Sandbox qualification: run every scenario against the real PayPal sandbox and record the exchanges, so the simulator can't drift.
- The isolated code runner: no network, no secrets, resource caps, signed results.
- Yard end to end: the planner, the Board with leases, the live project room, and handover into the buyer's own hosting, with a key-rotation checklist.
- Agent protocols: an A2A agent card and AP2-style mandates, so any agent can post or claim work and be paid through the same gate.
- More evidence profiles: freelance deliverables, construction stages (with our field partner EyeOnSite) and rental returns, all on the same "no pass, no pay" rule.
Built With
- ag-grid-/-ag-studio
- apimatic
- biome
- bryntum
- dependency-cruiser
- docker
- docker-compose
- drizzle-orm
- ed25519
- elastic
- hmac
- hono
- kernel
- node.js
- openapi
- paypal
- postgresql
- postman
- pre-commit
- render
- turborepo
- typescript
- vitest
- zapier
- zod