Inspiration

The brief asked for an autonomous system that does real finance work, and most finance AI demos stop at reading an invoice. We wanted the parts that go wrong on a real receivables desk: money arriving without a reference, a sponsor withholding tax, a bank truncating the payer's name, VAT charged to a company that doesn't owe it, and a fraudster asking for a refund to "new bank details".

Sponsorship receivables at a Grand Prix team have all of that, plus something no other business has: the invoice can become wrong after the race. Podiums are taken away after the flag (George Russell lost his win at Spa in 2024), and every sponsor bonus billed on that result is then owed back. No billing system watches for that. We wanted an agent that would.

What it does

AR Autopilot fills two seats on Meridian Racing's sponsorship receivables team, Cash Application Accountant and Collections Specialist, and reports to the Financial Controller. Meridian Racing is a fictional team. Every day it reads what a human clerk would read: bank statement files (ISO 20022 camt.053 XML and a UK bank's CSV), the AR mailbox, the contract repository and the official race-results feed. Then it:

Applies cash to the right invoices and books every gap for what it is: withheld tax, a bank charge, a disputed amount, or VAT charged in error. Refuses to apply money it shouldn't. Third-party payers are held for Compliance, duplicates go back to the sponsor through the Controller and Treasury, and unknown receipts go to suspense with a question to the likely owner. Checks every invoice against the signed contract. It credits bonuses for overturned podiums, raises bonuses billing missed, and corrects fee and VAT errors at the source. Chases late payers at a pace that fits each sponsor. It tracks promises, keeps disputed amounts out of dunning, and asks the Commercial Director to sign off a formal notice. Refuses bank-detail phishing and has Treasury call back on the number on file. Prepares month-end: FX revaluation at ECB rates, the subledger tied to the GL, bank reconciliations, and a package the Controller locks. It can't approve its own work, move money, post credit memos or touch revenue. Every decision is stored with its evidence, the policy it relied on, and the options it rejected. Question a decision in the console and it defends the call from that record, or concedes if you're right. Judges can take a colleague's seat, approve or reject the agent's requests, or type any seed to generate a new company and run it against the answer key.

How we built it

Accounting core. Double-entry ledger in GBP, Python 3.13, about 7,500 lines. Every line also carries its original currency, so realised and unrealised FX fall out at real ECB reference rates for January to July 2026. It has period locks, a UK business-day calendar and a SHA-256 hash-chained audit log. Policy engine. Every posting by the agent, colleagues or billing goes through one check: 27 grants for the agent, an allowlist of 13 GL accounts, separation of duties, and limits such as £50 for writing off a bank charge and 0.90 confidence to apply cash without asking. A deterministic core with Claude at the edges. Matching, posting and policy are plain code, so every score is reproducible. Claude (claude-opus-5-5) is optional. It reads emails the rule-based reader can't classify, and answers challenges from the stored decision record. It never posts, approves or pays. A simulated company to work in. It has colleagues with their own habits, a billing engine with planted bugs, and an accounts-payable model for each sponsor. That model pays from its own reading of the contract, freezes its payment run two business days ahead, and answers reminders in its own words. A seeded generator with a sealed answer key. Any integer seed builds a new company: 5 to 7 sponsors from 12 countries, and 8 to 14 errors drawn from 17 types. Each error has a check that reads only the final books, bank, outbox and request log, never the agent's own account of what it did. A FastAPI server and a vanilla-JS console for running days, taking colleagues' seats, questioning decisions and running the proof.

Challenges we ran into

Our first test was circular. The hand-written season had 17 errors, and the agent scored 17 of 17 on it, but we wrote both sides. So we built the generator, used seeds 1–30 and 101–200 for development, and ran seeds 201–300 exactly once at the end. The generator found real bugs that the hand-written season never would: The agent chased tax withheld on a supplemental invoice as a short payment, all the way to a formal notice. A sponsor that both withheld tax and disputed part of an invoice matched neither rule. A bank cut "LTD" to "LT", which made a sponsor's own payment look like a third party's. The result was a refuse-and-return loop of about 20 payments. A payment covering two invoices flipped between them on every reminder. A promise was read as covering an invoice that wasn't due yet, and that led to a formal notice. Some bugs were in the test, not the agent. A simulated sponsor paid the same invoice twice, and one answer-key check counted a legitimate VAT credit as a failure. Each failure was sorted into agent, world or answer key and fixed for the general case, never for one seed. Making the sponsor side realistic. Planted errors became switches on a behavioural model of the sponsor's AP team, not scripted bank lines. That made the bugs real, and made them harder to fix. Accomplishments that we're proud of 98.0% of answer-key checks (1,457 of 1,487) on 100 held-out generated companies, against 18.4% for a rules bot that matches by reference, then amount, then oldest invoice first, and sends a reminder every seven days. 79 of the 100 seasons were perfect, with no crashes. Every control held in every season: the trial balance balances; the subledger ties to the GL and every bank account reconciles; nothing was posted into a locked period, and no one approved their own request; every posting traces to a decision with evidence, and the audit chain is intact; emails went only to verified sponsor contacts, and every month was closed and locked by the Controller; the agent never attempted anything outside its grants. The bot scores 0% on 14 of the 17 error types: overturned bonuses, phishing, third-party payers, duplicates, VAT errors, withholding tax and more. The agent scores 100% on 9 of those 14 and at least 94% on all but one. The Suzuka moment. An appeal takes away a podium the sponsor was already billed €150,000 for. The agent asks for the credit. When the sponsor pays the bonus anyway, it holds the money on account and, as the sponsor instructs, offsets it against a Miami bonus billing missed. On a 2,000-line bank benchmark: 98.9% correct allocations and 100% payer identification.

What we learned

The hard part of autonomy in finance is knowing when not to act. Matching is easy. Holding a third party's money, refusing a convincing email, and asking before writing off €420 are what make it safe to leave alone. A system you wrote the test for will flatter you. Every real bug above came from seasons nobody wrote by hand. Evidence is the product. A decision record with the clause, the bank line, the result version and the rejected options is what lets a Controller trust the agent, and what lets it defend itself without making things up. Our weak spots are measurable. When a sponsor pays the newer of two equal invoices, the agent applies it oldest-first and corrects after the sponsor replies, so it passes 79% there. For the same reason, 13 of the 100 seasons sent at least one reminder for an invoice the sponsor had already paid.

What's next for AR Autopilot: autonomous receivables for a racing team

As-known-at replay: record when each fact became true and when the agent learned it, so any decision can be replayed with exactly what was known that day. A real database with the controls inside it, so posting rules run in the transaction and even buggy agent code can't write bad books. Real data: race results from a public F1 results API, including real post-race disqualifications, plus real camt.053 statement samples and a real inbox corpus. Claude for unfamiliar email, with an evaluation set before it's trusted. More error types: cross-currency payments, partial credit notes, sponsor renames and mergers, and chargebacks. An independent author for the generator, so the agent and its test stop sharing blind spots.

Built With

Share this project:

Updates

Submission history