Inspiration

Anyone who bills by the session (tutors, coaches, consultants, freelancers) runs two systems: a calendar for the work and a separate process for getting paid. The calendar already knows when the work is done, yet invoicing still waits on someone remembering to do it. Then comes the worst part: noticing who hasn't paid and writing a follow-up that's firm without being awkward. I wanted the schedule itself to be the trigger: when the session ends, the bill goes out.

What it does

Bell to Bill is a Bryntum Scheduler timeline where rows are clients and bars are sessions, colored by billing state:

  • Booked → Ended → Invoiced: the moment a session crosses the Now line, a PayPal invoice is created and sent, with a line item and note written by Gemini from the session notes. The bar locks.
  • Chasing: unpaid invoices get PayPal reminders on a policy, and each one is firmer than the last (friendly → firm → final). Gemini adapts each reminder to how long the invoice has been open, how quickly that client usually pays, and what else they owe.
  • Paid: when PayPal reports the payment, the bar turns green and the balance clears.

A Gemini agent can also run the schedule from plain English: "I'm sick tomorrow, move my sessions to the next free evening slots." The bars slide on the timeline as it works. It can book, move, cancel, invoice and remind, but it can never mark an invoice as paid. Only PayPal can.

How I built it

  • Frontend: Bryntum Scheduler 7 on Vite. Drag, resize and create validators enforce a one-person rule across rows (you can't be in two places, even for different clients). Billed sessions are read-only, and the time axis hides the night hours so a working day fits on screen.
  • Backend: Node and Express. The server pushes every change to the browser over server-sent events, and the browser applies them record by record, so changed bars flash in place.
  • PayPal Invoicing v2 (sandbox): generate an invoice number, create, send, remind, record payments, check status.
  • Gemini (@google/genai): structured JSON output checked with Zod for invoices and reminders, plus a function-calling loop over 10 tools that call the same code as the timeline.

The billing rules are simple. A session of $h$ hours at rate $r$ is invoiced for

$$\text{amount} = h \times r,$$

and with a first-reminder delay $d_1$ and a repeat interval $d$, reminder $k$ goes out at

$$t_k = t_{\text{sent}} + d_1 + (k-1)\,d, \qquad k = 1, \dots, k_{\max},$$

with defaults $d_1 = d = 3$ days and $k_{\max} = 3$.

Challenges I ran into

  • Free-tier AI is unpredictable. The stronger Gemini models often returned 503 or 504 errors or hit their quota, and one agent request took over two minutes. I added a fallback chain led by gemini-3.1-flash-lite, a 30-second timeout, and a 3-minute cooldown that skips a busy model. Agent replies dropped to 2–6 seconds. Every AI call also has a template fallback, so an invoice always goes out.
  • Gemini 3 thought signatures. Multi-step tool use broke until I passed the model's turns back exactly as returned and pinned the conversation to whichever model answered first.
  • Demoing something that takes days. Real billing waits for sessions to end and for reminders to come due. Instead of shrinking the policy to minutes, I built a demo clock: the server's time is real time plus an offset that can be fast-forwarded, and the billing loop runs immediately after each jump. The PayPal calls and Gemini text stay real; only the waiting is skipped.
  • Detecting payment without webhooks. PayPal webhooks need a public URL, which a local app doesn't have. Our first version checked for payment inside the same loop that did the slow AI work, so a payment could take about 25 seconds to show up. Moving payment checks to their own loop, checking every open invoice at once every 3 seconds, brought that down to about 2 seconds.

What I learned

  • The calendar is the billing system. Once a session is a record with a start, an end and a rate, invoicing and chasing are just state changes on it.
  • Agents need hard limits as much as capabilities. Giving the model the same validated operations as the UI, and returning errors to it instead of hiding them, made it re-plan around clashes on its own.
  • AI tools speed up building but don't replace testing. I built Bell to Bill with Claude Code as a coding assistant, and running every flow in a real browser against the real sandbox is what surfaced several bugs and revised product and design decisions.

What's next

PayPal webhooks, accounts for multiple users, recurring bookings, and a client-facing page where clients book and pay in one step.

Built With

Share this project:

Updates

Submission history