Inspiration

Chasing a late invoice in Germany comes with a small legal calculation. Default interest depends on whether the debtor is a consumer, the 40 euro flat fee only applies to businesses, and every part payment or refund changes the claim. An AI that writes the whole letter can get a figure wrong without anyone noticing, and one that sends it on its own is worse. So the numbers and dates live in plain code with tests, the model only gets the wording, and the send button stays with a person.

What it does

Mahnwerk sits on top of PayPal Invoicing (sandbox). It creates and sends real sandbox invoices and follows them through PayPal webhooks. When an invoice is overdue, its rules engine proposes the next step: payment reminder, 1st Mahnung, 2nd Mahnung, handover notice, or a fee invoice when the principal is paid but interest is still owed. Each proposal lands in an approval queue with an AI-written letter, a "why now" explanation and a guardrail inspector that lists what was checked. Only after approval does anything go out: a real PayPal sandbox reminder, or an in-app letter when the daily reminder quota is used up.

The case page shows the PayPal evidence (invoice status, payment link and a QR code of it), a what-if calculator that shows the claim at another date without changing anything, and a hold button for after a dispute or an extension request: while a case is on hold nothing is proposed and an approved step refuses to send.

Who it is for

Freelancers, creators and small businesses in Germany who invoice through PayPal and have no legal department or collections agency. They know they are owed money. They often do not know how much interest they may charge, from which day, or what to write, so the easy option is to send nothing. Mahnwerk gives them the numbers, a letter in German, and a clear next step, and leaves the decision with them.

Try it as a judge

No login. Open https://mahnwerk.vercel.app, then either "See the example workspace" (read-only, seven cases in different stages) or "Try the live demo" (your own private sandbox workspace). In your own workspace: create an invoice, move the demo clock forward 14 days, open the approval queue, read the AI letter, approve it, then use the customer simulator on the case page to pay, refund or dispute through the PayPal sandbox. The README has the full walkthrough. The PayPal sandbox allows 2 invoice reminders per day for all visitors together; beyond that the app sends an in-app letter and says so.

How PayPal is used

Invoicing v2 (create, send, remind, record payment, refund, cancel), webhooks with signature verification (paid, refunded, cancelled, updated, capture refunded, customer disputes), and a PayPal pay link in every letter, also as a QR code on the case page. The PayPal invoice is the source of truth. Every webhook triggers a re-fetch and a reconcile, so late, duplicated or reordered events cannot corrupt the books. The sandbox cannot create real disputes through the API, so the dispute button is simulated and labelled as such.

How AI is used

Claude Sonnet drafts letter wording, classifies debtor replies (with prompt-injection handling), explains why a step is proposed, and answers questions in a read-only copilot. The model never sees real names or amounts, only placeholders. Code fills in every figure, a validator checks the result, and a template takes over if validation fails. Approval is bound to the exact wording on screen: if a new draft arrives while you read, or the claim changed since the draft was written, the approval is refused or the template with current figures is used. AI calls are capped per workspace and per day.

How I built it

Next.js 16, TypeScript, Supabase (Postgres, row level security, anonymous auth), Vercel. A pure rules engine (decimal arithmetic, property-based tests), a versioned case state machine with an audit row for every transition, an idempotent leased executor, and a service layer shared by the UI and the API. 335 tests run offline. Built with Claude Code during the submission period, with every design and scope decision made by me.

Challenges

  • PayPal returns refunds without any id, so reconciliation needed a stable positional key (found by testing live, fixed, covered by a test).
  • An AI draft could replace the displayed text a moment after page load. Approval is now bound to a fingerprint of the shown wording, and a stale approval is refused.
  • Current Claude models reject forced tool choice, so the client uses automatic tool choice plus an explicit instruction and treats a missing tool call as a failed draft.
  • German law is not a spec. Every modelling choice (default start on day 31 after the due date, when a Mahnung takes effect, payment allocation) is written down in the README as a limit.
  • An independent review of the finished project found problems I then fixed: an AI budget that one visitor could exhaust for everyone, open write policies on the database, and a stale draft that could be approved.

Accomplishments that I'm proud of

A working flow on real sandbox invoices from create to reminder to Mahnung to payment, interest figures I checked by hand, and a guardrail inspector in the UI that shows each check.

What I learned

Keeping money and dates out of the model took less code than I expected. It also made the AI part easy to test: a draft either passes the validator or a template replaces it.

What's next for Mahnwerk

E-invoice formats (XRechnung, mandatory in Germany from 2027 and 2028), real email delivery, an export for the court dunning procedure, more jurisdictions.

AI tools used

  • Claude API (Claude Sonnet): letter drafting, reply classification, explanations, copilot (inside the product)
  • Claude Code: used to write the code under my direction

Hackathon demo, sandbox only, fictional data, not legal advice.

Built With

Share this project:

Updates

Submission history