How Clause works
Architecture
Buyer browser ──PayPal JS SDK popup──▶ PayPal Sandbox
Buyer / Seller / AI agent ──REST + SSE──▶ Clause server (Express)
MCP clients (Claude Desktop/Code) ──stdio──▶ src/mcp.js ──HTTP──▶ Clause server
Clause server
├─ pacts.js state machine · spend gate · settlement · disputes · scheduler
├─ ai.js Claude: compiler · arbiter · tribunal · seller agent
├─ jobs.js runs AI inline, or as Render Workflows tasks
├─ paypal.js Orders v2 · Authorizations · Captures · Refunds
├─ audit.js SHA-256 hash chain · fingerprints
└─ store.js JSON file store
Pact lifecycle
| Pact status | Meaning |
|---|---|
awaiting_funding | Compiled; waiting for the buyer's PayPal approval |
active | Authorization hold placed; milestones delivered in order |
completed | Every milestone settled; final capture closed the hold |
cancelled | Remaining hold voided and returned to the buyer |
Milestone states: pending → reviewing → released (100%), settled (partial), revision_requested, escalated (buyer decides), refunded, voided.
Spend gate
Claude only recommends. Money moves automatically only when all five deterministic checks pass (spendGate() in src/pacts.js):
- The arbiter recommends release.
- Every acceptance criterion is
met(a criterion the model skips counts asunverifiable). - Confidence ≥
AUTO_RELEASE_MIN_CONFIDENCE(default 75%). - No red flags (e.g. prompt injection, plagiarism signals).
- A live PayPal authorization hold exists.
Otherwise funds stay held: the buyer can approve (fully or partially), or send the milestone to the tribunal.
PayPal APIs
| Step | Endpoint |
|---|---|
| Create escrow order (one line item per milestone) | POST /v2/checkout/orders with intent: AUTHORIZE |
| Place the hold after approval | POST /v2/checkout/orders/{id}/authorize |
| Release a milestone | POST /v2/payments/authorizations/{id}/capture, final_capture=false until the last |
| Return what's left | POST /v2/payments/authorizations/{id}/void |
| Extend long pacts | POST /v2/payments/authorizations/{id}/reauthorize |
| Appeal of released funds | POST /v2/payments/captures/{id}/refund |
Every money call sends a PayPal-Request-Id, and changes to a pact are serialized, so a retry or double click can't capture twice.
Claude roles
| Role | Call | Notes |
|---|---|---|
| Contract compiler | Structured output (Zod), effort medium | Milestones, amounts, objective criteria, risks, clarifying questions |
| Arbiter | Structured output + images, effort high | Per-criterion verdict with quoted evidence; deliverable treated as untrusted |
| Tribunal advocates | Two parallel calls, effort low | Buyer side and seller side; told to concede weak points |
| Tribunal judge | Structured output, effort high | Awards a percentage based on criteria actually met |
| Seller agent | Text, effort medium | Produces the deliverable; uses arbiter feedback on revisions |
Audit trail & receipts
Each pact event stores prevHash and hash = SHA-256(prevHash | canonical(event)). Money events include the PayPal ID and amount inside the hashed data. Deliverables and verdicts get their own SHA-256 fingerprints. GET /api/pacts/{id}/verify recomputes the chain and reports the first broken event; receipts re-run it when opened.
REST API
| Method | Path | Purpose |
|---|---|---|
| POST | /api/pacts/draft | Compile { text } into a draft (nothing saved) |
| POST | /api/pacts | Create a pact from { draft, sellerKind } |
| GET | /api/pacts, /api/pacts/{id} | List / get pacts (with money, schedule and audit status) |
| GET | /api/pacts/{id}/stream | Server-sent events with live pact updates |
| POST | /api/pacts/{id}/paypal/order | Create the PayPal order; returns orderId and approveUrl |
| POST | /api/pacts/{id}/paypal/authorize | Place the hold after approval |
| POST | /api/pacts/{id}/milestones/{mid}/submit | Multipart text, images, links; runs the arbiter and spend gate |
| POST | /api/pacts/{id}/milestones/{mid}/agent | AI seller agent does the work, then submits |
| POST | /api/pacts/{id}/milestones/{mid}/release | Buyer approval, { percent } |
| POST | /api/pacts/{id}/milestones/{mid}/dispute | Tribunal; { claim, by: "buyer" } or { response, by: "seller" } |
| POST | /api/pacts/{id}/cancel | Void the remaining hold |
| POST | /api/pacts/{id}/refresh, /reauthorize | Sync authorization status from PayPal / extend the hold |
| GET | /api/pacts/{id}/verify | Recompute the audit hash chain |
| GET | /api/pacts/{id}/receipts/{mid} | Verifiable receipt for a settled milestone |
| GET | /api/analytics/data | Tables and relationships for the analytics dashboard |
| POST | /api/ai/turn | One AG Studio agent turn executed by Claude |
| POST | /api/maintenance/sweep | Cron sweep (header x-cron-secret) |
| GET | /api/config, /api/health | Client config; health check |
MCP tools
| Tool | What it does |
|---|---|
draft_pact | Compile a deal into a draft contract (nothing created) |
create_pact | Create the pact and PayPal order; returns the approval URL for the human buyer |
get_pact, list_pacts | Status, milestones, verdicts, money held and released |
submit_deliverable | Seller delivers; returns verdict and spend-gate result |
release_milestone | Buyer approval (use only with explicit human approval) |
open_dispute | Send a milestone to the AI tribunal |
Integrations
Bryntum Gantt: delivery timeline
public/timeline.js. Milestones are bars coloured by money state (held, reviewing, captured, partial, refunded), linked finish-to-start, with PayPal ledger events as time ranges. The server's scheduler adds a revision buffer whenever the arbiter requests a revision and flags milestones forecast past their deadline as at risk. Installed as @bryntum/gantt@npm:@bryntum/gantt-trial (trial watermark).
AG Studio (AG Grid): escrow analytics
public/analytics.js, src/analytics.js, src/studioAi.js. Four related tables, a dashboard generated with Studio's own add_widget/configure_widget tools, a custom LLM adapter that runs Studio's built-in agents on Claude server-side, and a custom Escrow risk analyst agent with Clause tools that delegates chart-building to Studio's lead agent. Theme mapped to Clause's design tokens. Trial mode unless AG_STUDIO_LICENSE_KEY is set.
Render: hosting, Workflows and cron
render.yaml, src/workflows.js, src/jobs.js, scripts/sweep.js. The AI agents run as Render Workflows tasks with retries (the tribunal fans its two advocates out in parallel); money never moves inside a task. An hourly cron job reauthorizes holds past PayPal's 3-day honor period and flags overdue milestones.
Project layout
src/ server, pacts, paypal, ai, audit, jobs, workflows, analytics, studioAi, mcp, store
public/ app (vanilla JS), timeline (Bryntum), analytics (AG Studio), styles
scripts/ smoke, seed-demo, sweep, build-dashboard
test/ lifecycle tests against a fake PayPal API, bridge and workflow tests
site/ this documentation (GitHub Pages)
Limitations
- Buyer and seller roles are switched in the UI; production would add authentication and separate links.
- Storage is a JSON file; swap in a database for production.
- PayPal authorizations are honored for 3 days and valid for 29; long pacts rely on the reauthorize sweep.
- Next steps: PayPal webhooks for capture reconciliation, Payouts to split one pact across several sellers, Disputes API sync.