Inspiration
We run a real company where about 20 AI agents work every day. They write, render, deploy and answer customers, and every risky action stops for one thing: the owner's one-tap signature on a phone. That pattern turned out to be the missing piece for agents that handle money. Most "agentic commerce" demos let an agent buy something on a person's behalf. We wanted the opposite and more realistic picture: agents as employees of a company, each with a budget, a role and a boss. Atlas Treasury brings our daily signature pattern to money on PayPal.
What it does
Agents earn: when an agent finishes a client task it creates and sends a real PayPal invoice (Invoicing v2) or a checkout link (Orders v2), follows its status and sends reminders. Agents spend: when an agent needs a resource (a GPU hour, a voice-over, a server) it files a spend request tied to a task. A pure-code policy engine decides: auto (within the agent's per-payment limit, monthly budget and allowed categories, known vendor), needs_approval, or blocked. Auto payments go out through PayPal Payouts; the rest become an approval card with a QR code that opens a mobile page with the agent, amount, vendor, the policy reasons in red and the budget after payment. One tap approves or rejects.
Every ledger row has a proof chain: task, spend request, policy verdict, human decision, PayPal ids, verified webhook events and the delivered result, so "what did we pay for and what did we get" is one click. Refunds and disputes arrive by webhook; an agent drafts an analysis, and PayPal's Disputes API is called only after the owner clicks.
Verified live in the PayPal sandbox on 29 Oct 2026: agent Orion requested $38 for a new vendor, the engine flagged "New vendor", the owner approved on the phone page, and PayPal Payouts accepted the batch (6HSPCYLSWWX8Q). Agent Lyra refused to re-invoice an already-paid task, and Orion warned the owner about a possible duplicate GPU payment for the same task. Agent Vega created and sent a real sandbox invoice for $1,200; the sandbox client paid it and the signature-verified INVOICING.INVOICE.PAID webhook booked the income. A sandbox client then opened a real dispute ("40 of 48 frames delivered", PP-R-LNU-10195061); the webhook linked it to the task, the analyst agent found no proof of delivery and recommended accepting the $110 claim, and the owner clicked "Accept claim", which called PayPal's Disputes API. Live stand: https://treasury.ihor.work
How we built it
Node 22 with Hono, built-in node:sqlite (no native dependencies), vanilla ES modules on the front end and AG Grid Community for the ledger. The PayPal client is a thin fetch wrapper with an OAuth token cache, a PayPal-Request-Id on every POST and typed errors carrying PayPal's debug id. Webhooks are signature-verified through verify-webhook-signature, stored verbatim and deduplicated on event.id; a reconciler polls PayPal every two minutes so the UI never gets stuck when sandbox webhooks are late. A unique index on (kind, paypal_ref_type, paypal_ref_id) means the webhook and the reconciler can never double-book. Approval tokens are single-use, expire after 48 hours and are claimed atomically before any network call. Claude gets only our validated tools, with a hard cap of $2,000 per request, per-visitor rate limits and a daily AI cap for the public stand. Tests use Node's built-in runner with a mocked fetch.
The design rule behind it all: the LLM proposes, the policy engine disposes, the human signs.
Challenges we ran into
- PayPal Invoicing accepts only
NET_10/15/30/45/60/90payment terms. An arbitrary "due in 14 days" is rejected, so we send an explicit due date instead. - Sandbox webhooks can arrive late or twice. We made ledger writes idempotent and added a reconciler instead of trusting a single delivery path.
- Merchant account currency: a non-USD primary balance rejects USD payouts, so the code supports a settlement currency and FX rate.
- Making money rules trustworthy: it was tempting to let the model judge risk. We kept it out of the decision and used it only to propose and to explain.
- An OAuth token keeps the scopes it was issued with for about nine hours: enabling Invoicing on the app left the running server with a stale token. The client now refreshes the token once on
NOT_AUTHORIZED. PAYMENT.PAYOUTS-ITEM.UNCLAIMEDis not a failure: PayPal holds the money 30 days for a recipient without an account. We track it as money out with statusunclaimedand fail only onRETURNED.- Failed payouts must be visible. We record them with PayPal's error rather than hiding them behind a generic success state.
Accomplishments that we're proud of
- A full money loop on PayPal in one product: Invoicing, Orders, Payouts, Webhooks, Payments refunds and Disputes, with a verified end-to-end payout approved from a phone.
- Agents that behave like careful employees: one declined to double-bill, another flagged a possible duplicate payment on its own.
- A proof chain that links every payment to the task that caused it and the result it produced.
- No fake success paths: without keys the app answers 503 "not configured" instead of pretending.
- It is an open MIT repository that runs locally in five steps.
What we learned
- Deterministic policy plus a human signature makes agents safe to give a wallet; the model is most useful as proposer and explainer, not judge.
- PayPal's webhooks need defensive handling (dedupe, reconcile, idempotency keys) before you can trust a ledger built on them.
- The best approval UX is tiny: one screen, the reasons in plain language, the budget after payment, one tap.
- Real constraints in an API (payment terms, currencies) are where integration quality shows.
What's next for Atlas Treasury
- Production hardening beyond the sandbox: per-agent PayPal sub-accounts or vouchers, stronger vendor verification, configurable denylists.
- File evidence upload for disputes (PayPal documents multipart; we currently send notes only).
- Approval delivery to push channels (a webhook hook exists via
APPROVAL_WEBHOOK_URL) and multi-approver rules for large amounts. - Row grouping and richer reports for finance teams.
- Packaging the policy engine as a small library so other agent frameworks can put the same signature step in front of PayPal.
Built With
- ag-grid
- anthropic
- claude
- cloudflare
- docker
- hono
- javascript
- node.js
- paypal
- sqlite
Log in or sign up for Devpost to join the conversation.