# PayPilot > An AI dunning agent that recovers failed subscription payments. When a recurring charge fails, PayPilot turns the `invoice.payment_failed` event into a grounded recovery action: it diagnoses why the payment failed, scores churn risk, picks a retry strategy, schedules the retry, and drafts a warm dunning email - in a single API call. PayPilot is a portfolio project by Ivan S that demonstrates a production-shaped agentic system: a seven-node LangGraph state machine with retrieval-augmented generation (RAG) over a dunning playbook, exposed through a FastAPI endpoint. The full suite of 749 automated checks (725 tests plus 24 evals) runs offline with no API key and no network, including adversarial prompt-injection and PII-masking cases. ## How it works - Seven-node LangGraph flow: `retrieve_context`, `assess_risk`, `diagnose_reason`, `choose_strategy`, `schedule_retry`, `draft_message`, `finalize`. - RAG grounding: FAISS + OpenAI embeddings when a key is set, an offline lexical retriever otherwise, over an editable dunning playbook. The playbook feeds the LLM drafting path; the default path renders committed templates, so editing the playbook changes output only when PAYPILOT_LLM_DRAFT=1 is set. - Deterministic policy: the retry action, cadence, and churn-risk escalation come from fixed rules tables, not the LLM, so the policy is stable and unit-testable. On the default path no model runs at all: the diagnosis and email come from committed, human-reviewed templates. With PAYPILOT_LLM_DRAFT=1 an LLM writes those two fields and nothing else. - Risk-aware escalation: churn risk is scored from the dunning attempt number and the customer's recent failure streak; high risk tightens the retry cadence and marks the strategy escalated. On the default path the email copy itself is a fixed per-failure-code template, so escalation changes the plan and the schedule, not the wording. - Revenue math: every response quantifies the amount at risk, recovery likelihood, expected recovered value, and annual value at risk. ## Key facts - Problem domain: involuntary churn and dunning (recovering failed recurring payments). - Stack: Python 3.11, LangGraph, LangChain, FastAPI, pydantic, plus FAISS when a key is set (the default demo path uses a lexical retriever). - Runs free with no API key via a deterministic mock mode; live model drafting needs BOTH OPENAI_API_KEY and PAYPILOT_LLM_DRAFT=1. ## FAQ - What is PayPilot? PayPilot is an AI dunning agent that recovers failed subscription payments. It turns a failed-payment event into a grounded recovery action: a diagnosis of why the charge failed, a churn-risk score, a retry strategy, a scheduled retry, and a drafted dunning email. - How does PayPilot decide the retry strategy? The retry action and cadence come from a deterministic rules table keyed on the failure code, not the language model, so the policy is stable and unit-testable. When churn risk is high, the cadence tightens automatically. - Does the live demo need an API key? No. With no OpenAI key set, PayPilot runs a deterministic mock mode using grounded playbook templates and a lexical retriever, so the demo works offline, free, and with no signup. Adding an API key alone changes nothing: live model drafting also requires PAYPILOT_LLM_DRAFT=1. The default path renders committed copy and performs no chat inference. - What technology does PayPilot use? A seven-node LangGraph state machine with retrieval-augmented generation (RAG) over a dunning playbook, served through a FastAPI endpoint. It is written in Python 3.11 and runs its full suite of 749 automated checks (725 tests plus 24 evals) offline. - How does PayPilot measure recovered revenue? Every response quantifies the amount at risk, the recovery likelihood, the expected recovered value, and the annual revenue at risk if the customer churns. Recovery likelihood is an illustrative constant per failure code, not a measured rate; actual recovered revenue is measured separately in the recovery ledger and reported at /report. - Is PayPilot GDPR and SOC2-ready? Yes, with controls evidenced in code: PII is masked before the model and never stored in the ledger, scripted and tested jobs export and erase a customer's data end to end, records auto-expire on a configurable retention window, an append-only audit log records every money and auth event, and webhooks and admin routes are authenticated. It is also EU AI Act limited-risk with a configurable AI-assistance disclosure on generated emails. See docs/compliance. SOC2-ready means the controls are built and evidenced, not that a certification audit has been performed. ## Links - Live demo: https://paypilot.fly.dev/ - Source code: https://github.com/IvanSFlowGit/paypilot - Author (available to hire): https://www.linkedin.com/in/ivansflow/ ## Licence Source-available, NOT open source. PolyForm Noncommercial License 1.0.0. Free to read, run, fork and study for any noncommercial purpose, explicitly including evaluating the author's work for hiring. Running PayPilot to recover payments for a business, or shipping it inside a paid product or service, requires a separate commercial licence from the author. Copyright (c) 2026 Ivan S (github.com/IvanSFlowGit). - Load test write-up (https://paypilot.fly.dev/loadtest): a concurrency ladder against the Stripe webhook ingest path, measured 2026-08-08. Throughput peaked at 414 requests per second at 4 concurrent and fell to 140 at 64. Moving the database entirely to memory changed nothing at the collapse point, so disk is not the ceiling. Four uvicorn workers bought 39 percent rather than 400, so a serialisation point survives process separation. The audit trail held at every concurrency on one worker and on four. Includes the two places the author's own measuring instruments were wrong.