Skip to content

shanayg15/ReCourse

Repository files navigation

Recourse ⚖️

An Autonomous Agent That Catches Institutions Breaking Their Own Rules — and Files the Appeal

Team: Shanay Gaitonde · Sahiel Bose  ·  Multi-agent systems · TypeScript · LangChain

Next.js TypeScript Turborepo LangChain Tests Runs with zero keys License: MIT

A 13-agent, two-phase pipeline that reads an institutional denial letter, retrieves the institution's own published rules, catches where the denial contradicts those rules, drafts a cited appeal, runs the high-stakes math in a sandbox, clears a compliance guardrail, and — after a human approval gate — files the appeal and tracks the deadline. Most insurance denials are never appealed; the ones that are often win. Recourse closes that gap. It runs end-to-end on deterministic mocks with zero API keys.


The Demo: Why This Matters

  1. Maria Mitchell, 54, HER2+ breast cancer. Her insurer denies Trastuzumab (Herceptin) as "not medically necessary." She photographs the one-page denial letter and uploads it.
  2. Recourse OCRs the letter and extracts the payer, plan, denial code, and the appeal deadline — a 180-day window with 166 days remaining (computed as real code, not guessed).
  3. It retrieves the payer's own published medical policy for Herceptin — the exact coverage criteria the plan publishes.
  4. It pulls Maria's clinical facts and runs a criterion-by-criterion match: HER2+ confirmed (IHC 3+), Stage II, ECOG 1 — she meets all three.
  5. THE CATCH: the denial said "not medically necessary," but under the payer's own Policy §4.2, Maria qualifies on every listed criterion. The agent surfaces a provable self-contradiction.
  6. It drafts a formal appeal that quotes the payer's policy number back at them and lists each met criterion.
  7. The deadline and eligibility math run as real code in an isolated sandbox — never produced by the LLM.
  8. A compliance guardrail verifies: deadline not blown, every citation resolvable, no PHI leaked, format correct.
  9. Dr.'s advocate Dana reviews the catch and the letter on the dashboard and clicks Approve & File.
  10. The filing agent submits and returns a confirmation number — and if the portal is down, it automatically reroutes to fax without losing the case. The deadline goes on watch; a re-denial auto-escalates to external review, and the winning argument is written to memory.

The contradiction engine catches a tired clinician — or a scared patient — would never spot: a denial that violates the insurer's own published policy. Then it proves it with a citation and files the appeal in under a minute.


The Catch — Structured Contradiction Detection

This is the technical core, and what makes Recourse not "an LLM that writes a letter." Letter-writers are everywhere; an agent that catches an institution violating its own published policy is not.

   retrieved rule   (the payer's own coverage criteria, parsed into a checklist)
 + extracted fact   (the patient demonstrably meets each one)
 + stated reason    (the denial claims they don't)
 ─────────────────────────────────────────────────────────────────────────────
 = a provable contradiction  →  a citable argument

The matcher is pure, deterministic logic — fully real with no API keys — and classifies every catch into one of three patterns:

Pattern Meaning Strength
self_contradiction Patient meets every published criterion, yet was denied Strongest — the demo case
invented_criterion The denial leans on a requirement the policy doesn't list Strong — insurer invented a hurdle
admin_error Wrong code, wrong plan, or missing records Correctable

Because it's deterministic, it's pinned by a labeled eval set (see Benchmarks & Evals) rather than vibes.


Architecture

Two phases, split by a human approval gate — nothing is filed until a person approves it. Every step is wrapped in retry → fallback orchestration (a failed portal submit reroutes to fax automatically).

flowchart TD
    subgraph INTAKE["Patient Intake (:3002)"]
        I1["Upload the denial letter"]
    end

    subgraph DRAFT["API · POST /api/agents/draft — Phase 1: Analyze & Draft"]
        A1["1 · Intake & OCR"]
        A2["2 · Classifier"]
        A3["3 · Context Pull"]
        A4["4 · Evidence Retriever"]
        A5["5 · Contradiction Engine · THE CATCH"]
        A6["6 · Strategy / Memory"]
        A7["7 · Drafting"]
        A8["8 · Compute & Form-Fill"]
        A9["9 · Guardrail"]
    end

    subgraph GATE["Human Approval Gate · Dashboard (:3003)"]
        G1["Advocate reviews the catch + letter, clicks Approve & File"]
    end

    subgraph FILEP["API · POST /api/agents/file — Phase 2: Verify & Act"]
        F10["10 · Identity / Auth"]
        F11["11 · Filing (portal → fax fallback)"]
        F12["12 · Audit · SHA-256 chain"]
        F13["13 · Tracker / Escalator"]
    end

    subgraph RECORDS["Case & Audit (:3004)"]
        AU1["Status timeline + tamper-evident log"]
    end

    I1 -->|userId| A1
    A1 --> A2 --> A3 --> A4 --> A5 --> A6 --> A7 --> A8 --> A9
    A9 -->|"needs_human_approval"| G1
    G1 -->|approverToken| F10
    F10 --> F11 --> F12 --> F13
    F13 -->|"tracking / escalated"| AU1
Loading

Engineering ideas worth a second look:

  • No hallucinated arithmetic — appeal deadlines and eligibility scores are executed as real code (optionally shipped to an isolated sandbox), never produced by the model.
  • Immutable, tamper-evident audit — every step is recorded in a SHA-256 hash chain (each event's prevHash = the previous payloadHash), with a verifyChain() that detects tampering.
  • Human-in-the-loop by design — the draft → file split means an agent never acts unilaterally.
  • Live progress — Phase 1 streams agent-by-agent progress to the UI over NDJSON, with a graceful fallback to a plain request.
  • Graceful degradation everywhere — see below.

The 13-Agent Pipeline

# Agent Role Technology
1 Intake & OCR OCR the denial letter; store the original artifact Nebius + Tigris
2 Classifier Letter → structured denial (payer, plan, code, deadline window) LangChain + Claude
3 Context Pull Patient's clinical facts + prior case history Insforge + Brain2
4 Evidence Retriever Fetch the payer's published policy → structured criteria Apify
5 Contradiction Engine (the catch) Criterion-by-criterion match; classify the contradiction Rule-based + Daytona
6 Strategy / Memory Which argument has beaten this payer before Brain2
7 Drafting Write the cited appeal letter LangChain + Claude
8 Compute & Form-Fill Sandboxed deadline/eligibility math; fill + validate the form Daytona
9 Guardrail Compliance pass — deadline, citations, PHI, format Opsera
10 Identity / Auth Verify the approver may file on this case Insforge
11 Filing (the action) Submit on the payer portal; fall back to fax Rtrvr.ai
12 Audit SHA-256 hash-chained, tamper-evident trail Tigris + crypto
13 Tracker / Escalator Watch the deadline; escalate on re-denial; learn the outcome Insforge + Brain2

Orchestration (retry → fallback → recovery) wraps every step. Agents 5 (the matcher) and 12 (the hash chain) are pure local logic — fully real regardless of keys.


Runs With Zero Configuration

npm install && npm run dev

…boots the API + three UIs and runs the entire pipeline on deterministic mocks with no .env file. Every external integration sits behind one pattern:

export async function fetchPayerPolicy(payer: string, service: string): Promise<PayerPolicy> {
  if (process.env.APIFY_TOKEN) {
    try { /* REAL: call the provider, parse, return */ } catch { /* fall through to the mock */ }
  }
  return mockPayerPolicy(payer, service); // deterministic — same output every run
}

Drop a real key into .env and that integration activates automatically — no code changes. A bad or expired key never crashes anything; the real call is wrapped in try/catch and falls back to the mock. Each integration becomes an independent, swappable seam, and the project is demoable on any machine.


Quick Start

git clone https://github.com/shanayg15/Recourse.git
cd Recourse
npm install
npm run dev        # boots all 4 apps concurrently (Turborepo)
App URL Who uses it
API http://localhost:3001 Internal — the agent pipeline + all routes
Patient Intake http://localhost:3002 Patient uploads a denial letter
Dashboard http://localhost:3003 Advocate reviews the catch + letter, approves & files
Case & Audit http://localhost:3004 Status timeline + tamper-evident activity log

To go live, copy .env.example to .env and add keys one integration at a time — start with ANTHROPIC_API_KEY.


API Reference

POST /api/agents/draft — run Phase 1 (agents 1–9)

curl -X POST http://localhost:3001/api/agents/draft \
  -H "Content-Type: application/json" \
  -d '{ "userId": "u_maria", "vertical": "health" }'
{
  "success": true,
  "case": {
    "id": "case_maria",
    "status": "needs_human_approval",
    "vertical": "health",
    "memberName": "Maria Mitchell",
    "service": "Trastuzumab (Herceptin)",
    "payer": "BlueCross Demo Health",
    "contradiction": {
      "type": "self_contradiction",
      "citedPolicySection": "4.2",
      "metCriteria": ["HER2-positive (IHC 3+)…", "Stage II–IV…", "ECOG 0–2"],
      "deadlineDaysLeft": 166           // computed live from the notice date
    },
    "draftAppeal": { "id": "draft_case_maria", "citations": ["…Policy §4.2…"] },
    "computed": { "deadlineDaysLeft": 166, "eligibility": "1" },
    "guardrail": { "passed": true, "checks": [ /* deadline, citations, PHI, format */ ] }
  },
  "pipeline": { "agentsExecuted": 9, "status": "needs_human_approval" }
}

POST /api/agents/draft/stream — same, with live progress

Streams newline-delimited JSON: one {type:"progress"} frame as each agent starts/finishes, then a final {type:"done", response} frame. Powers the dashboard's live trace.

POST /api/agents/file — run Phase 2 (agents 10–13), after human approval

curl -X POST http://localhost:3001/api/agents/file \
  -H "Content-Type: application/json" \
  -d '{ "caseId": "case_maria", "approverToken": "advocate_dana" }'

Returns a Filing (confirmationNumber, channel: "portal" | "fax") and the full SHA-256 auditTrail.

Other routes

Route Purpose
GET /api/cases/:id Case + documents + tamper-evident audit trail
POST /api/cases/:id/simulate-response Demo-only: re-denial → escalation path
POST /api/demo/portal Demo-only: toggle a portal outage to show the retry → fax fallback

Demo tokens

Token User Role
patient_maria Maria Mitchell Patient
advocate_dana Dana Rivera Patient advocate (approves & files)
admin_ops Operations Admin

Pass as approverToken or Authorization: Bearer <token>.


Benchmarks & Evals

The contradiction matcher is deterministic, so its quality is pinned by a labeled eval set — no API key required. npm run test runs 11 tests across the workspace:

Metric Target Result
Self-contradicting denials caught 5 / 5 5 / 5
False appeals (filed when the patient doesn't qualify) 0 0
Appeal deadline computed correctly across windows 3 / 3 3 / 3
Real-vs-mock LLM seam (real branch when keyed, fallback otherwise) pass
End-to-end draft route on mocks pass

Eval cases live in packages/agents/test/eval.test.ts — add your own labeled scenarios.


Running Tests

npm run test        # all workspaces (Vitest) — no API key required
npm run typecheck   # strict TypeScript across the monorepo
npm run lint        # ESLint
npm run build       # production build, all 4 apps

The critical test — the catch:

it("flags a self_contradiction when the patient meets every published criterion", async () => {
  const { finding } = await run({
    caseId, context: MARIA_CONTEXT, policy: DEMO_POLICY,
    classification: DEMO_CLASSIFICATION, nowISO: "2026-05-31T00:00:00.000Z",
  });
  expect(finding.type).toBe("self_contradiction");
  expect(finding.citedPolicySection).toBe("4.2");
  expect(finding.metCriteria).toHaveLength(3);
});

Tech Stack

Layer Technology
Monorepo Turborepo + npm workspaces
Framework Next.js 15 (App Router)
Language TypeScript 5 (strict everywhere), ESM
Agents / LLM LangChain prompt templates + Claude (@anthropic-ai/sdk)
Live progress NDJSON streaming over fetch (graceful fallback)
Sandboxed execution Daytona (deadline + eligibility math)
Data extraction Apify
Compliance guardrail Opsera (MCP)
Web action / filing Rtrvr.ai
Auth / database Insforge (in-memory store as the keyless fallback)
Object storage / audit Tigris + SHA-256 hash chain
Semantic memory Brain2
Model inference / OCR Nebius
Orchestration Kalibr-style retry + fallback
Tests Vitest
Deployment Render (render.yaml blueprint)

Workspace packages ship raw TypeScript (no build step) — Next transpiles them just-in-time and Vitest inlines them.


Monorepo Structure

Recourse/
├── apps/
│   ├── api/             # :3001 — the agent pipeline + all routes
│   ├── patient-intake/  # :3002 — upload a denial letter
│   ├── dashboard/       # :3003 — review the catch + appeal, Approve & File
│   └── case-audit/      # :3004 — status timeline + tamper-evident log
├── packages/
│   ├── types/           # Shared data model + API contracts (single source of truth)
│   ├── agents/          # The 13 agent run()s (one file each) + the eval set
│   ├── sponsors/        # Provider clients — real-or-mock by env key
│   ├── orchestrator/    # runDraftPipeline / runFilePipeline + withRetry
│   ├── seed/            # Maria's record, canned policy, demo tokens, in-memory db
│   ├── ui/              # Shared design system + typed API client + live trace
│   └── config/          # Tailwind preset, ESLint preset, tsconfig base
├── render.yaml          # Render Blueprint: API + 3 UIs as services
└── .env.example         # every integration key, blank

Deployment

A render.yaml blueprint deploys the API + three UIs as Render web services. Each builds the whole workspace and serves on Render's $PORT; all integration keys are declared blank (sync: false) so the deploy runs on mocks out of the box. Fill them in the Render dashboard to go live, then paste the deployed API URL into each UI's NEXT_PUBLIC_API_URL.


The Platform Thesis

One engine, five markets. The same 13-agent spine serves any domain where a high-stakes "no" can be overturned — swap only the evidence source (agent 4) and the filing target (agent 11):

Vertical Evidence source Filing target
Health insurance (demo) Payer medical policy + clinical guideline Payer appeal portal
Eviction defense Local housing statute + notice rules Court e-filing portal
Government benefits Program eligibility rules Benefits re-application portal
Bank charges / subscriptions Cardholder agreement / dispute rules Bank dispute form
Auto / home claims Policy language Insurer claim portal

License

MIT — © 2026 Shanay Gaitonde, Sahiel Bose

About

Multi-agent pipeline that catches insurers contradicting their own policy and files a cited appeal behind a human approval gate.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors