Claude drafts jurisdiction-referenced demand letters and 0–100 client risk scores with full reasoning, wired into a self-hosted FastAPI + Next.js dashboard and a scriptable CLI.
Built by Rudrendu Paul & Sourav Nandy
---
## API Reference
Interactive OpenAPI at `http://localhost:8000/docs`. Verified against the router source directly:
```
GET /health Liveness probe
GET /health/ready Readiness (DB)
GET /api/v1/clients List
POST /api/v1/clients Create
GET /api/v1/clients/{client_id} Detail
PUT /api/v1/clients/{client_id} Update
DELETE /api/v1/clients/{client_id} Delete
GET /api/v1/invoices List
POST /api/v1/invoices Create (manual)
GET /api/v1/invoices/{invoice_id} Detail
PATCH /api/v1/invoices/{invoice_id}/status Update status
GET /api/v1/escalations Active escalations
POST /api/v1/escalations/{invoice_id}/draft AI-draft next escalation email (preview only)
GET /api/v1/escalations/{invoice_id}/history Full history
POST /api/v1/legal/demand-letter Generate demand letter
POST /api/v1/legal/demand-letter/stream Generate + stream (SSE)
GET /api/v1/evidence/{invoice_id} Evidence items
POST /api/v1/evidence/{invoice_id}/upload Manual upload
DELETE /api/v1/evidence/{item_id} Remove
POST /api/v1/risk/score AI risk score, structured JSON
GET /api/v1/analytics/overview Dashboard totals
```
---
## Comparison
Every non-`freelancer-payment-protection` row below is sourced from each vendor's own docs/help center, checked in August 2026.
| Capability | Spreadsheets | FreshBooks | HoneyBook | HubSpot | freelancer-payment-protection |
|---|:---:|:---:|:---:|:---:|:---:|
| Overdue-payment reminders | ✗ | Automatic, up to 3 per invoice, configurable timing, template-based | Automatic, 4 fixed timings (7 days before, due day, 2 days after, recurring), template-based | Automated "Payment Reminder" workflow (rule-based) | AI-drafted per stage, tone-calibrated, confidence-scored (preview only, not auto-sent) |
| Jurisdiction-referenced legal demand letters | ✗ | ✗ (not documented) | ✗ (not documented) | ✗ (not documented) | AI-drafted; 4 jurisdictions have a dedicated template (CA, NY, UK, Ontario) |
| Client/invoice risk scoring | ✗ | ✗ (not documented) | ✗ (not documented) | **Breeze Invoice Prioritization**, an AI ranking of overdue invoices by risk/age/customer value, Revenue Hub public beta as of June 2026; no published 0–100 score or per-factor reasoning | 0–100 score, 7 named factors, full AI reasoning returned per client, heuristic fallback if AI is down |
| Evidence/document storage per invoice | ✗ | ✗ (not documented) | ✗ (not documented) | ✗ (not documented) | Manual upload, Supabase Storage-backed, no ZIP export yet |
| Streaming AI generation in the UI | ✗ | ✗ | ✗ | ✗ | Real SSE streaming (verified in source, not just a UI animation) |
| Native invoicing | ✗ | Yes (core product) | Yes (core product) | Yes (Commerce/Payments) | No. Invoices are created via API/CLI, not synced from an accounting tool today |
| Background job automation (sync, scheduled escalation) | N/A | Native | Native | Native | Not implemented. See [What's Not Implemented Yet](#whats-not-implemented-yet) |
The honest read: FreshBooks and HoneyBook are stronger at the mechanical, rule-based reminder they already do well. HubSpot's June 2026 Breeze beta is the closest thing to a competing risk-scoring feature on this list and is worth watching. Nobody here drafts a jurisdiction-referenced demand letter or streams AI generation into the UI; that's the actual gap this project fills, not "full collection automation," which none of these, including this project, deliver end to end yet.
---
## Architecture
### System diagram (what's actually implemented)
```mermaid
graph TB
subgraph "Frontend: Next.js 14"
A[App Router Pages]
B[TanStack Query Cache]
C[Framer Motion UI]
D[Supabase Auth Client]
end
subgraph "Backend: FastAPI, Python 3.12"
E[FastAPI App Factory]
F[JWT Middleware]
G[slowapi Rate Limiter]
H["Routers: 8 domains"]
I[Services: business logic only]
end
subgraph "AI: Claude Sonnet 4.6"
J[packages/legal_ai/client.py]
K[Demand Letter: streaming SSE]
L[Escalation Email: structured draft]
M[Risk Scorer: JSON output]
end
subgraph "Data Layer"
T[(Supabase PostgreSQL + RLS)]
U[Supabase Storage]
W[(SQLite Dev DB)]
end
A --> E
D --> T
B --> E
E --> F --> G --> H --> I
I --> J
J --> K
J --> L
J --> M
I --> T & U
```
Celery and Redis are declared dependencies (`requirements.txt`) with no worker code in the repository today: no `apps/workers/` directory exists, and there is no scheduled job that advances an invoice's stage automatically. See [What's Not Implemented Yet](#whats-not-implemented-yet).
### Why Python for the backend, not Node
Legal document drafting uses `python-docx`/`WeasyPrint` in the code paths that are wired up for it, and the Anthropic Python SDK is the reference implementation. The Python ecosystem is also where contract-analysis tooling (NLTK, spaCy) would live for a future dispute-analysis feature.
### Why centralize all Claude calls in one file
`packages/legal_ai/client.py` (called from `apps/api/app/services/ai_service.py`) is the only place the Anthropic SDK is imported. Model version, retries, and the sync-SDK/async-FastAPI bridge live there, so upgrading the model is a one-file change.
### Why Pydantic Settings with fail-fast validation
`settings = Settings()` runs at import time. If `ANTHROPIC_API_KEY` is absent, the app raises before serving a request rather than degrading silently. The tradeoff: the settings model is strict about unrecognized fields too, which is the root cause of one of the two `.env.example` issues in the FAQ below.
Every data-returning command also takes `--json` for structured output an agent or script can parse directly:
---
## MCP Server
`freelancer-payment-protection-cli` ships a Model Context Protocol (MCP) server, so an
agent (Claude Desktop, Claude Code, or any other MCP client) can call the same commands
above (`invoice list`, `client risk`, `escalation status`, ...) as tool calls instead of
shelling out to the CLI directly.
**Install:**
```bash
pip install "freelancer-payment-protection-cli[mcp]"
```
**Claude Desktop config** (`claude_desktop_config.json`):
```json
{
"mcpServers": {
"freelancer-payment-protection": {
"command": "fpp-mcp"
}
}
}
```
The server exposes one tool, `run`, that shells out to the installed `fpp` binary with
the given argument list and returns its output as structured JSON when possible — every
`fpp` subcommand is reachable through it, not just a hand-picked subset. Example call:
`run(args=["client", "risk", "