Skip to content
← All projects

An agent that escalates unpaid invoices — and never acts without you

  • LangChain
  • LangGraph
  • Groq · Llama 3.3 70B
  • FastAPI
  • React 19
  • MCP
  • Tavily
  • Vercel
InvoiceChaser walkthrough: client pushback is detected, a late fee is calculated and a firmer escalation draft waits for the freelancer to approve
The live app in action.

Freelancers lose money to invoices nobody chases. InvoiceChaser moves a dispute from a friendly reminder to a formal demand to small-claims preparation, changing its tone and its tools at each level, and pauses for approval before anything goes out.

Dynamic tool gating

A @wrap_model_call middleware filters the tool list before every model call, and @dynamic_prompt swaps the tone. At Level 1 the model doesn’t even know a final-notice tool exists — it isn’t forbidden, it simply isn’t there. Pick a level to see what the model can use:

Tone: Friendly — warm reminder

  • check_invoice_statusavailable
  • draft_polite_reminderavailable
  • escalate_disputeavailable
  • draft_formal_demand_letterhidden from model
  • calculate_late_feeshidden from model
  • draft_final_noticehidden from model
  • lookup_small_claims_procedureshidden from model
  • generate_court_filing_guidehidden from model

Every draft stops for approval

HumanInTheLoopMiddleware interrupts the run on any draft tool. State is checkpointed per invoice (thread invoice-{id}), so the run resumes exactly where it paused through POST /api/invoices/{id}/resume. Try it:

draft_formal_demand_letterLevel 2 · Formal
Payment of the outstanding invoice, plus the late fee set in your terms, is now requested within the agreed period. [Sample draft — illustrative]

Architecture: one request, end to end

Click any stage to see what happens there.
Request path
Tools & state

LangChain agent + middleware

Dynamic prompt sets tone per level, tool gating filters what the model sees, HITL middleware interrupts on draft tools.

@dynamic_prompt · @wrap_model_call · HumanInTheLoopMiddleware · context_schema

Why it’s safe to let this agent near a client

Concern Choice Why
Irreversible actions HITL interrupt on every draft tool No letter reaches a client without explicit approval
Least privilege Tools filtered per level in middleware The model can’t jump to legal threats by itself
Consistent state Typed state schema; tools return Command(update=…) Level and fees change atomically, not by prompt
Durability Checkpointer per invoice thread A paused run survives restarts and resumes cleanly
Integrations Invoice data behind an MCP server Swap the mock for QuickBooks or Xero without touching the agent
Per-user config Runtime context (business, terms, late-fee rate) Same agent, each freelancer’s own rules

What I’d build next

  • Tracing and a golden-set eval for tone and tool choice at each level
  • Real email delivery (SendGrid or Resend) and PDF export of letters
  • Live QuickBooks / FreshBooks / Xero behind the MCP server

Related writing