Inspiration

A while ago, a distant relative of mine was in a critical condition, close to needing life support. The family turned to crowdfunding, and I did what most of us do: I forwarded the link to everyone I could.

The replies surprised me. People wanted to help, but they hesitated. Is this real? Is the bill really that much? Will the money actually reach the hospital, or just someone's account? Many of them never donated. The campaign was genuine, but nobody had a way to check it, so being genuine wasn't enough.

That stayed with me. The problem wasn't generosity, it was trust. It made me see a pattern in how small teams and independent developers handle money: it runs on manual effort and blind trust. You chase clients for unpaid invoices, guess what's fair when you pay contributors, and ask donors to take a story on faith.

So I built Upstream, an AI money agent on PayPal, around one rule: money in runs on its own; money out waits for your yes.

What it does

1. Pay your team. Pick a GitHub repo, a budget and a schedule (daily, weekly or monthly). Upstream reads the commits and splits the budget by the work done. Each contributor's weight is

$$w_i = \sqrt{c_i \cdot \ell_i}$$

where \( c_i \) is their number of commits and \( \ell_i \) the lines they changed. The square root keeps one giant commit from swamping steady work. Each share is

$$a_i = T \cdot \frac{w_i}{\sum_j w_j}$$

rounded to the cent so the shares add up to exactly \( T \). An AI reviews the actual commits and can shift a share by at most \( \pm 5 \) percentage points, and it must say why. You approve once; before every payday you get a preview to edit or skip, then PayPal Payouts pays everyone.

2. Get paid. Type "Bill Acme $500 for the API integration, due in 14 days" and a real PayPal invoice goes out. Upstream then chases it on its own: a friendly reminder every day until the due date, one the day after, then every 3 days, each firmer than the last and written in your style. After 30 days overdue it stops and asks you. Monthly clients run on PayPal Subscriptions, with a heads-up before every renewal and a one-click pause.

3. Verified causes: pay the bill, not the person. This is the feature my relative's campaign needed. The organiser uploads the hospital bill. The AI only reads it; plain code runs seven checks:

  • it's a medical bill
  • the hospital matches the campaign
  • the patient matches the campaign
  • the goal is no more than the bill
  • the bill is recent
  • the line items add up
  • the bill isn't already backing another campaign

Only verified campaigns can take donations. Each donation is a PayPal order whose payee is the hospital itself: the money goes straight there, and Upstream never holds it. Donors see the proof, while the patient appears only as initials and the bill itself is never published. Donations stop at the amount still needed.

How I built it

  • Backend: Python and FastAPI, with PostgreSQL (Neon) in production and SQLite locally.
  • Frontend: plain HTML, CSS and JavaScript, with AG Grid for the invoice ledger and payroll payment tables (sorting, filtering, search and CSV export).
  • PayPal: Payouts, Invoicing v2, Catalog + Subscriptions, Orders v2 and verified Webhooks, all in the sandbox.
  • AI: gpt-oss-120b on Groq for the billing chat (tool calling), commit reviews and reminders in your voice. A vision model reads photos of bills; PDFs are read as text.
  • GitHub REST API: commit history for payroll.
  • Hosting: Render, with an in-app scheduler plus an external wake-up job, so paydays and reminders still happen when no one has the page open.
  • Quality: a pytest suite with faked PayPal and AI responses, plus end-to-end tests against the real sandbox, run on Jenkins.

The most important design decision was splitting the work between AI and code: the AI reads and writes; code decides. The AI never chooses who gets paid or whether a bill passes. It drafts, explains and extracts, and deterministic rules with hard caps make every decision that moves money.

Challenges I ran into

  • Time-based features on a free server. Render's free tier sleeps after 15 minutes of inactivity, and GitHub Actions' scheduled jobs turned out to run hours late. I added an in-app scheduler and an external job that wakes the app every 5 minutes. Every job is idempotent, so a payday or a reminder can never run twice.
  • Demoing things that take weeks. Overdue invoices and paydays don't happen in a 3-minute demo, so I built a sandbox-only demo clock that moves time forward and shows reminders escalating on cue.
  • PayPal's sandbox details. Paid invoices weren't showing because the real webhook payload nests the invoice under resource.invoice, not at the top level. I only found it by reading real webhook payloads. I also learned that PayPal limits invoice reminders to two a day.
  • Never paying the wrong amount, or paying twice. Payroll runs move through strict states (preview → paying → paid) with conditional updates, a monthly cap, and a lock on payroll edits close to payday.
  • Making verification honest. It was tempting to let the AI decide whether a bill is legitimate. I deliberately kept it to extraction and put the judgement in code, where it can be tested.
  • Theming a third-party grid. AG Grid adds its theme styles at runtime, after my own stylesheet loads, so my light and dark colours were overridden until I made my selectors more specific.

What I learned

  • Trust is a product feature. Showing donors the checks matters as much as running them.
  • AI is most useful when it's fenced in: bounded adjustments, required explanations, and a human approval for money going out.
  • Read the real payloads. The docs and the sandbox don't always agree.
  • Reliability is mostly about the boring parts: retries, idempotency and wake-ups.

What's next

  • Verifying that a hospital's PayPal account really belongs to the hospital
  • GitHub sign-in to prove repo ownership
  • Contributors adding their own PayPal emails
  • Moving from the sandbox to live

Built With

Share this project:

Updates

Submission history