Inspiration

When food prices climb, neighbours split bulk bags of rice, oil and beans between households. A 50 lb bag split six ways costs each family about a third less than buying a small bag alone. The economics work. The logistics don't: it runs on one tired organiser, a group chat, a spreadsheet, and a pile of transfers to chase. Someone fronts the money, someone chases the late payer, someone does the maths at the end, and someone ends up out of pocket.

I wanted the organiser to be able to say one sentence — "six of us want jasmine rice, about 8 lb each, nobody pays more than $14, close Friday" — and have everything after that handled by an agent that can actually move money, inside rules the group set up front. Otu is Igbo for "one": pay in once, buy as one, get paid back.

What it does

  1. The organiser says what the group wants, in plain words.
  2. The agent drafts the pool: it reads the request, sources offers across retailers (Channel3, with a curated catalog as fallback), prices each offer per household against what a family would pay buying alone, picks the best one and explains why. It also writes the pool's mandate: per-member cap, minimum paid members, deadline, allowed price drift, reminder budget, and "the purchase waits for the organiser's approval".
  3. Members pay their share with PayPal (Orders v2 via the JavaScript SDK v6). Each share carries a small buffer for price movement, which comes back later. A real sandbox capture turns the member's row green; PayPal's signed webhook then confirms it on the ledger (blue ✓).
  4. Late members get a real PayPal invoice (Invoicing v2) with a reminder the agent writes the way a neighbour would — inside the reminder budget. A member who pays their invoice is marked paid by the INVOICING.INVOICE.PAID webhook alone.
  5. When the pool fills, the agent re-checks the live price, rescales the order to the households that actually paid, runs every rule in the mandate (funds cover it, drift ≤ 5 %, each household under the cap, one purchase only…) and asks for one approval.
  6. It pays the supplier with Payouts v1 and refunds every member's surplus to their original PayPal payment (Payments v2 refunds), exact to the cent. Pool balance ends at $0.00.
  7. If the pool misses its minimum by the deadline, everyone gets everything back automatically.
  8. The organiser console (AG Grid + AG Charts) shows every pool, member, ledger line, mandate verdict, and every PayPal webhook delivery with how its signature was verified.

How I built it

  • Front end: vanilla ES modules, no framework. The pool page renders a live "money map" (in → buy → back), the agent feed, and the PayPal button through the JS SDK v6 (<paypal-button> + payment session). The console uses AG Grid for the tables and AG Charts for the pool/ledger charts.
  • API: one zero-dependency Vercel function routes everything under /api. The agent loop is observe → decide → validate → act → record. Every money action goes through a pure, deterministic mandate engine before it can touch PayPal, and every verdict (with each rule's detail) is written to the pool's action log — so the organiser can always see why the agent did or didn't do something.
  • AI: Gemini (gemini-3.8-flash) parses the organiser's request into a structured spec + mandate, explains the offer choice, and writes each reminder; Groq is the fallback, and a deterministic rules path is the fallback for the fallback, so the money loop never depends on an LLM being up. The LLM never decides whether money moves — the mandate does.
  • PayPal (sandbox): Orders v2 create/capture through the official PayPal Server SDK (@paypal/paypal-server-sdk, generated by APIMatic; lib/paypal-sdk.js), JavaScript SDK v6, Invoicing v2 (create, send, remind), Payouts v1 (supplier), Payments v2 refunds, and Webhooks. Webhook signatures are verified offline against PayPal's certificate (cached), each event is matched back to its pool through a reference index, and idempotency keys (PayPal-Request-Id) make every money call safe to retry.
  • Sourcing: Channel3 product search, normalised into pack sizes so the agent can compute per-household cost and leftover units; curated catalog fallback.
  • Storage: Upstash Redis (pools, reference index, webhook log). Hosting: Vercel.
  • Tests: the mandate engine has a unit test suite (test/mandate.test.mjs).

Challenges I ran into

  • Letting an agent move money safely. The answer was to keep the LLM out of the decision path entirely: it reads, explains and writes prose; a deterministic mandate decides, and every decision is logged with the rule that allowed or blocked it. The "one approval" step sits exactly where a neighbour would want it — before the purchase.
  • Closing the loop with webhooks. Captures, refunds, payout items and paid invoices arrive as separate event types, minutes apart, and have to be matched back to the right pool and member. A reference index keyed on order/capture/invoice/payout ids, plus offline signature verification, made that reliable.
  • Refunding to the cent. Rescaling an order to the households that actually paid, splitting a real bulk price across them and routing each member's exact surplus back to their original capture — with rounding that always adds up to a $0.00 balance — took more care than the happy path suggests.
  • The retailer side. The supplier is a separate PayPal sandbox account standing in for a retailer (a dedicated business sandbox account was blocked by a dashboard captcha); Payouts to it succeed and it claims them.

Accomplishments that I'm proud of

  • The whole loop is real: on the live demo every pay-in, invoice, payout, refund and webhook is a real PayPal sandbox call, and the ledger ends at exactly $0.00.
  • The organiser's experience is one sentence and one approval.
  • Every agent decision is explainable from the UI.

What I learned

  • The PayPal sandbox is complete enough to run an entire money lifecycle — orders, invoices, payouts, refunds and webhooks — against real objects, which made it possible to test the agent on the same path it will run in production.
  • Agentic commerce gets trustworthy when the agent's authority is a small, inspectable set of rules the humans wrote, not the model's judgement.

What's next for Otu

  • Live retailer checkout for the purchase step (today the supplier is paid by Payout); Venmo and Pay Later for members.
  • WhatsApp/SMS front end so a group can run a pool from the chat they already use.
  • Recurring pools ("every month, same six households") and multi-currency pools.
  • A pilot with a real neighbourhood buying club in Lagos.

Built With

  • ag-charts
  • ag-grid
  • apimatic
  • channel3
  • gemini
  • groq
  • javascript
  • node.js
  • paypal
  • paypal-javascript-sdk-v6
  • paypal-orders-v2
  • paypal-payouts
  • paypal-server-sdk
  • paypal-webhooks
  • upstash-redis
  • vercel
Share this project:

Updates

Submission history