ReCoup is a closed-loop AI revenue recovery agent for Razorpay-shaped UPI Autopay mandate failures. It diagnoses decline codes, picks one bounded intervention, executes via a simulator-backed world, and reports incremental recovered GMV vs a dumb baseline.
Incremental ₹ recovered on a frozen synthetic merchant batch (seed 42) vs a Stripe-style baseline (fixed 24h retry, technical immediate retry, static hard-decline deny list, no customer repair).
npm install
cd console && npm install && cd ..
npm test
npm run eval
npm run validate:external
npm run dev # API on :3000
npm run dev:console # Merchant UI on :5173 (proxies /api → :3000)Or run both together:
npm run start:projectWindows PowerShell:
.\scripts\start.ps1This installs dependencies (if missing), starts the API on :3000 and console on :5173.
Options:
npm run start:project -- --skip-install # skip npm install check
npm run start:project:eval # run eval first, then startOr run both manually:
npm run dev:allBuild everything (API + console for single-port deploy):
npm run build:all
npm start # serves console at / when console/dist exists- Eval report:
eval/report.jsonandeval/report.md - Merchant console:
http://localhost:5173(dev) orhttp://localhost:3000(after build)
CSV / Webhook → Normalizer → Diagnoser → Policy Gate → Decider → Executors → Simulator / Razorpay
↓
Audit Ledger (JSON store)
| Method | Path | Description |
|---|---|---|
| GET | /health |
Health check |
| POST | /api/ingest/csv |
Ingest batch |
| POST | /api/ingest/adapters |
Ingest checkout / invoice / PTP / voice fixtures |
| POST | /api/webhooks/razorpay |
Razorpay test-mode webhook ingest |
| GET | /api/cases |
List cases |
| GET | /api/cases/:id |
Case detail + audit trail |
| POST | /api/cases/:id/run-to-completion |
Run ReCoup orchestrator |
| POST | /api/cases/:id/retry/live |
Live retry (Razorpay if configured) |
| POST | /api/eval/run |
Run ReCoup vs BASELINE eval |
| GET | /api/eval/latest |
Latest eval metrics |
| GET | /api/metrics |
Recovered ₹ dashboard |
| GET | /api/catalog/failures |
Failure gallery by mapped class |
| GET | /api/adapters/razorpay |
Razorpay adapter status |
Legacy routes without /api prefix remain for backward compatibility.
Set env vars to enable live retries instead of simulator-only:
RAZORPAY_KEY_ID=rzp_test_...
RAZORPAY_KEY_SECRET=...Without keys, all scoring runs on the simulator — no production access required.
Validates decline-code mapping against public UPI failure labels — not incremental ₹ recovery.
npm run validate:external # fetches HF mock-upi-txn-data (cached under data/external/)
npm run validate:external:offline # uses committed sample in data/samples/Reports: data/external/diagnoser-validation.json and .md
Source dataset: deepakjoshi1606/mock-upi-txn-data (not committed — cache is gitignored).
- Tabs by feature: Dashboard · Autopay · Checkout · Invoice · PTP & Voice · Feature gallery · Failure gallery
- Dashboard: seed-42 north-star vs live store; recovered ₹ by rail / decline class; feature-gallery scoreboard
- Each feature tab shows only that rail’s cases (with diagnosis + audit)
- Actions: ingest batch → Autopay; load adapters → Checkout/Invoice/PTP; load feature gallery; run eval
UPI Autopay mandate debit is the scored rail (seed 42 eval). Checkout drop-off, B2B invoices, Hinglish voice, and promise-to-pay plug into the same diagnose → decide → policy → execute loop as adapters. They do not mix into the north-star eval.
- Checkout —
SEND_PAYMENT_LINK, neverSILENT_RETRY - Invoice — dunning notify, never a UPI debit
- Voice — channel +
hi-encopy on the existing repair action - Promise-to-pay —
promisedAtcase state; early debit/chase denied
Load demo fixtures from the console (Load feature gallery, Load adapter fixtures) or POST /api/ingest/features / POST /api/ingest/adapters.
- Hard declines never retried
- Attempt and contact caps enforced by policy gate
- Idempotent executors (no double-debit)
- Append-only audit ledger per case
MIT