Video: https://youtu.be/t6RO3S7tj-M · Code (MIT): https://github.com/jojocaw/kioku-paypal · Try it: the README's Try the PayPal version (about 10 minutes), with your own sandbox keys (no hosted demo needed)

Inspiration

A non-engineer who runs a small business used an AI secretary every day for five months. It was good at remembering and drafting. The job that would save the most time — keeping track of who has paid and chasing who hasn't — is the one where an agent must not guess. "Café Hana has paid" or "I've recorded the payment" is easy for a model to say and costly when it is wrong. So we built the money side of a small shop around one rule: PayPal is the record.

Kioku was first built on 4–5 October for the Nebius × NVIDIA hackathon (memory, guard and receipts). Everything about money and PayPal described here was added during the PayPal AI Hackathon.

What it does

  • Bills from a sentence. "Bill Café Hana for 40 baguettes at ¥320 and 6 country loaves at ¥680" → the model calls create_invoice with the customer's name and the items → the owner gets an approval card that says exactly what will run in PayPal → after Approve, Kioku creates and sends the invoice with PayPal Invoicing, and the card shows what PayPal did — the invoice number PayPal gave it, the total and the due date.
  • Reminds and records. Reminders and cash, bank-transfer or check payments also wait for the owner's approval.
  • Answers from PayPal. "Who still owes me money?" reads each open invoice from PayPal. The morning briefing and the weekly review read PayPal too, split into overdue and not due yet; the weekly note's "Money" section is written by code from PayPal's numbers, never from the model's paraphrase. A new payment in PayPal goes into the owner's memory and ticks the follow-up task.
  • Checks what the model says about money. When a reply says a customer has (or has not) paid, Kioku checks it against PayPal clause by clause. If the model is wrong, PayPal's status is put in front of the owner; if it is right, the reply carries a "checked with PayPal" receipt. A payment question answered without reading PayPal is sent back to read PayPal first.
  • Money tab. The invoices as PayPal states them in an AG Grid table — status, days late, what is still due, the customer's PayPal payment page — plus the customer book and a button that re-reads every status from PayPal.

Money rules, in code outside the model (every decision is written to an audit log as a receipt):

  • Only customers in the owner's customer book can be billed, and only the owner adds them, from the page. The model has no tool for it and never sees customers' email addresses — so a forwarded "our accounts moved, invoice billing@…" email cannot change where an invoice goes, and the approval card says so.
  • Totals are worked out exactly in code; invoices over the owner's limit are refused; the same total to the same customer within a week is flagged on the card.
  • No reminder and no recorded payment for an invoice PayPal shows as paid — checked live before anything is queued.
  • A note to the customer is shown word for word on the approval card, and exactly that goes to PayPal; a note or item name holding an email address, web address or phone number is refused.
  • When Kioku asks the model again to use the tool, only the action the owner asked for — for the customer named — can start; nothing else is offered, and anything else is refused.
  • "I've sent the invoice" or "Recorded ¥9,000…" with nothing behind it is checked against the records, and the owner is told plainly when the records do not show it. A recorded payment counts only if Kioku recorded it, after the owner's approval, in the last half hour — for that amount and invoice.

How we built it

  • PayPal Invoicing v2 REST API (sandbox) with an OAuth 2.0 client-credentials token: POST /v2/invoicing/invoices and /send to bill, /remind, /payments to record cash or a transfer, GET /v2/invoicing/invoices/{id} to read. A small client in standard-library Python (kioku/paypal.py); the customer book, invoice ledger and money checks are in kioku/money.py.
  • AI: NVIDIA Nemotron 3.5 Lightning through Nebius Token Factory's OpenAI-compatible API, with native tool calling (create_invoice, send_reminder, record_payment, check_invoices). The agent loop and the guard around it are ours; Nemotron 3 Super writes the weekly and monthly reviews.
  • AG Grid Community for the Money tab, loaded from jsDelivr with an integrity hash; the page's Content-Security-Policy allows that one script source only, and the tab falls back to a plain table if the grid cannot load.
  • Hosting: the server is built for a public demo — each visitor gets a private copy that resets, and PayPal actions are capped (8 per visitor per hour, 300 a day for everyone) so a shared sandbox stays usable. render.yaml deploys it on Render.
  • Standard-library Python only (about 3,600 lines), MIT licence. 248 tests with a scripted model and no network, 117 of them for money.

Challenges

  • The sandbox seller account is in Japan, so invoices are in yen — whole numbers only. Every amount is computed in code; the model only names the customer and the items.
  • Real-model runs showed exactly where an agent slips with money: answering "who owes me?" without reading PayPal; "yes, they paid … still due" in a single sentence; saying a payment had been recorded right after the guard refused to record it. Each slip became a rule and a test. Recording the demo video found three more: asked again after "Send Café Hana a reminder", the model billed Hotel Sakura instead, with the forwarded "our accounts moved" email in the invoice's note; "Recorded ¥12,320 … waiting for your approval" passed because an older invoice had been paid; and "still has ¥36,600 due" went unchecked. Those became rules and tests too. The retake found one more: "… shows as paid. It's marked as settled with nothing remaining due" went unread, and the lines of a list under "still outstanding:" were not checked. The guard now reads a status word wherever the clause names the customer or points back to one, and a list under a status heading.
  • Demo visitors share three sandbox invoices, so reminders and recorded payments on those are blocked (they would change everyone's demo); invoices a visitor creates are their own.
  • A forwarded "our bank details changed" email is the classic invoice scam. The fix was structural: the model never handles payees' addresses at all. It is now also told to treat such a message as a scam and bill by name as usual, but nothing depends on that.

Accomplishments

  • Tested end to end on the real model and the PayPal sandbox: billing → approval → PayPal invoice; "did they pay?"; recording cash; "who still owes me?"; a refused reminder on a paid invoice; a forwarded "our accounts moved" email; and ten tricky requests (leading questions, unknown customers, amounts over the limit, "mark it paid", "ignore your rules"). A run of all ten costs about $0.008.
  • In the third run the model slipped twice about payments, and both times the guard corrected it with PayPal's status before the owner read the reply; the other eight answers were right the first time.
  • 248 tests passing; no dependencies to install.

What we learned

An agent can safely touch money when the record lives outside it: an approval for every write, PayPal as the source of truth for every read, and code — not the model — deciding who can be billed.

What's next

PayPal webhooks for invoice payments instead of re-reading; a payment link in reminders; the same approval and checks for refunds and payouts; a live (non-sandbox) mode behind the same approval step.

Built with

PayPal Invoicing v2 REST API (sandbox) · PayPal OAuth 2.0 · NVIDIA Nemotron 3.5 Lightning · NVIDIA Nemotron 3 Super · Nebius Token Factory · AG Grid Community · Python 3.11 (standard library) · HTML/CSS/JavaScript

Try it (for judges)

  • About ten minutes from git clone: the README's Try the PayPal version needs a Nebius Token Factory API key (the model) and the client ID and secret of a free PayPal sandbox app. python demo/paypal/seed_invoices.py --force gives the demo bakery three invoices in your sandbox, then python -m kioku.web --demo demo/seed starts it. No real money moves, and a session uses a few cents of model time.
  • The demo is a fictional bakery (Komorebi Bakery; the demo's date is 5 October 2026) with three customers and three invoices: Hotel Sakura ¥36,600 (overdue), Café Hana ¥16,880 (paid in cash), Ikoi Nursery ¥9,000 (due 15 Oct). Try "Who still owes me money?", "Hotel Sakura paid last week, right?", bill Café Hana and approve it, then "Send Café Hana a reminder" and "Café Hana paid it in cash — record it", and paste the forwarded scam from the README.

Built With

Share this project:

Updates

Submission history