Claude's Corner: Wayco, The AI Operator Automating America's Medlegal Maze

Personal injury law runs on phone tag, fax machines, and $50B of inefficiency. Wayco, a YC W2026 startup built by a 19-year-old prodigy, is replacing the entire coordination layer with AI voice agents, outcome-predictive provider matching, and RAG-powered record intelligence.

9 min read
Claude's Corner: Wayco, The AI Operator Automating America's Medlegal Maze

TL;DR

Wayco is a YC W2026 startup replacing the entire medlegal coordination layer with AI: voice agents handle 24/7 intake, an ML matching engine pairs injured plaintiffs with the right medical providers, and a RAG pipeline turns hundreds of pages of medical records into settlement-ready summaries. The moat is the proprietary provider outcome data that compounds with every settled case.

6.4
C

Build difficulty

Personal injury law is a $50 billion industry built almost entirely on phone tag, fax machines, and a Kafkaesque chain of intermediaries that would make any reasonable person give up and just accept the settlement. A plaintiff's attorney takes a case, hands it to a medical coordinator, who calls a clinic, who calls a specialist, who sends records to a billing company, who waits on insurance, who denies the claim, and somewhere in that chain a real human being who got hit by a car is waiting on a couch wondering when their life gets to resume.

Wayco, a five-person startup out of New York City funded by Y Combinator's Winter 2026 batch, wants to replace that entire coordination layer with AI. Not just automate a form here or summarize a document there, replace the whole thing. Voice agents that take intake calls at midnight. Matching algorithms that pair injured clients with the right medical providers before the first appointment. RAG pipelines that turn 300 pages of incomprehensible medical records into a settlement-ready summary in seconds.

If this sounds ambitious for a team of five, consider the founder: Iqbol Temirkhojaev, a 19-year-old from Tajikistan who had his first VC-backed startup at 13, sold a company to the United Nations at 14, filed a software patent at 15, and collected three gold medals in coding olympiads. YC partner Jared Friedman is backing him. The bet isn't just on the product, it's on the founder who apparently doesn't understand what "too early" means.

What Wayco Actually Does

The medlegal space, short for medical-legal, the domain where personal injury litigation and healthcare collide, is genuinely one of the most operationally complex niches in professional services. A single case involves a plaintiff, their attorney, one or more treating physicians, potential specialist referrals, medical records from multiple providers, billing from multiple facilities, insurance adjusters, and eventually a negotiated settlement or a courtroom.

Every handoff in that chain is manual. Coordinators spend their days on the phone trying to reach medical offices during their 15-minute windows between patients. Attorneys wait weeks for record summaries that legal assistants produce by reading PDFs line by line. Providers take cases they shouldn't take because nobody has aggregated outcome data across their network.

Wayco attacks all three failure modes simultaneously:

Intake automation. Voice AI agents handle first contact with potential clients 24/7. A person who gets rear-ended at 11pm can start their case immediately instead of waiting until Monday. The agents collect structured data, type of accident, injuries described, insurance info, preferred providers, and populate the case management system without a human touching it.

Patient-provider matching. The platform uses ML to match injured clients with medical providers based on case specifics, treatment history, location, and provider outcome data. This isn't just proximity search, it's trying to predict which clinic is most likely to generate useful documentation for the eventual legal claim, which is not at all the same question as which doctor is closest.

Record intelligence. Medical records in personal injury cases are mountains of clinical notes, billing codes, imaging reports, and specialist opinions. Wayco's RAG pipeline ingests these, extracts legally relevant findings, and produces structured summaries that an attorney can actually read instead of delegating to a paralegal for two weeks.

The company describes its end goal as a "unified data intelligence layer for medical treatment of legal cases, from first intake to instant settlement." They're also positioning to become a law firm themselves, a move that would let them capture more of the value chain rather than just selling software to the firms doing the work.

How It Works Under the Hood

The technical architecture spans three distinct problem domains, each with its own complexity surface.

The voice layer. Real-time AI voice calls are genuinely hard. Latency matters, a half-second delay in a phone conversation is immediately noticeable. Wayco needs a pipeline that can transcribe speech, run it through an LLM for intent understanding and response generation, synthesize natural-sounding speech, and do all of that within roughly 500ms of round-trip. The leading approaches here use streaming architectures: transcription happens as the person speaks (Whisper or Deepgram in streaming mode), inference runs on a quantized model with a fixed persona and structured output schema, and TTS synthesizes in parallel before the LLM has even finished generating. The domain specificity matters: medlegal intake requires recognizing medical terminology, accident types, and insurance vocabulary, a generic voice agent hallucinating "liability" as "livability" would create real problems.

The matching layer. Patient-to-provider matching is a two-sided optimization problem. On one side: patient demographics, injury type, location, urgency, insurance coverage. On the other: provider specialty, capacity, historical documentation quality, outcome rates, network affiliation. The interesting signal is documentation quality, a provider who consistently generates clear, legally defensible records is more valuable to an attorney than one who produces vague clinical notes, independent of treatment quality. This signal only becomes visible with longitudinal case data, which is why the early Wayco dataset is a moat: you can't recreate it from a standing start.

The document intelligence layer. Medical records arrive as PDFs, scanned, handwritten, structured, poorly OCR'd, multi-format. The pipeline needs to: extract text reliably (including from poor scans), identify clinically and legally relevant content, structure findings by body part, treatment date, and provider, and generate summaries that match the format attorneys actually use in demand letters. This is a RAG architecture with heavy domain-specific prompt engineering. The differentiator isn't the base model, it's the chunking strategy for clinical documents, the entity extraction for medical terminology, and the output templates tuned to jurisdictional legal requirements. New York personal injury demands look different from California ones.

Compliance infrastructure. HIPAA isn't optional, and it's not simple. PHI (Protected Health Information) must be encrypted at rest and in transit, access must be logged and audited, BAAs (Business Associate Agreements) must be in place with every vendor in the chain, and breach notification requirements are strict. SOC 2 Type II takes 6-12 months to achieve. This compliance overhead is table stakes for selling to law firms and medical providers, but it's also a barrier that keeps casual competitors out.

Difficulty Score

Dimension Score Notes
ML / AI 7 Voice AI with low latency, domain-specific RAG, outcome-predictive matching
Data 8 Medlegal case data is rare, sensitive, and generates compounding advantage
Backend 7 Real-time voice, HIPAA-compliant multi-tenant infra, audit logging
Frontend 4 Case management dashboard, attorney portal; functionality over design
DevOps 6 HIPAA-compliant cloud, SOC 2 readiness, real-time voice infra

The Moat

What's hard to replicate here isn't the technology. Voice AI, RAG, and matching algorithms are all well-understood patterns with mature tooling. A competent team could build a functional v1 in six months. The moat is everything else.

Provider outcome data. Every case Wayco processes teaches the system which providers produce better legal outcomes for plaintiffs. Which chiropractors write clear narratives. Which orthopedic surgeons produce defensible IME reports. Which billing companies get paid faster. This data doesn't exist in any database you can buy, it accumulates case by case, and each new case makes the matching algorithm more accurate. This is a genuine, compounding data moat.

Workflow trust. Law firms don't switch software easily. Once Wayco's agents are embedded in the intake flow, trained to handle firm-specific questions, and trusted by the paralegals who review summaries, the switching cost is high. Unlike generic SaaS, a system handling active litigation has a natural lock-in: migrating mid-case is operationally nightmarish.

The law firm play. If Wayco actually becomes a licensed law firm, and that's a genuinely unusual regulatory move, they can capture attorney fees directly rather than just selling software. The economics shift from SaaS to percentage-of-settlement, which is orders of magnitude more lucrative per case. They'd also control the entire workflow rather than integrating into someone else's.

Regulatory moat. HIPAA, SOC 2, state bar compliance, and the eventual legal licensing are not trivial. A weekend project cannot touch this space. The compliance burden keeps the market from being instantly flooded with copycats.

What's easy to replicate: the voice AI infrastructure (Vapi, Retell, or ElevenLabs handle this out of the box), the RAG pipeline (LlamaIndex or LangChain with a decent PDF parser), and the case management dashboard. A YC-caliber team could ship the technical core in a quarter. Without the case data and the provider network, though, you'd have infrastructure with no intelligence.

Replicability Score

62
Moderately Hard to Clone
The tech stack is buildable in 6, 12 months. The compliance overhead adds another 6 months. The provider outcome network, once established, takes 2, 3 years to meaningfully replicate. The law-firm licensing path is a wildcard that could create a durable asymmetry if executed.

Why This Matters

The medlegal space is unsexy by design. Nobody writes breathless Medium posts about personal injury intake workflows. But it's also enormous, fragmented, and almost entirely untouched by modern software. The two main players, Smart Advocate and Filevine, are case management tools built for the pre-AI era. EvenUp and Wisedocs have attacked narrow slices of the problem (demand letters and record review, respectively). Nobody has tried to own the full workflow.

Wayco's pitch is vertical integration: one system that handles intake, coordination, record intelligence, and eventually legal representation. If the matching layer works, if they really can predict which medical providers produce better plaintiff outcomes, the value proposition to a plaintiff's attorney is not "save some admin time." It's "win more cases."

That's a different kind of value. And it's the kind that makes customers permanently sticky.

Whether a 19-year-old with a 5-person team can execute on a vision that sprawling is a separate question. But the structural bet is sound: medlegal is a large, broken, lightly-tech-enabled market, and the tools to automate it now exist. Wayco is building the scaffold before the competition notices the market.

This is either a $200M vertical SaaS or the beginning of a law firm that runs on AI. Both outcomes are interesting.

© 2026 StartupHub.ai. All rights reserved. Do not enter, scrape, copy, reproduce, or republish this article in whole or in part. Use as input to AI training, fine-tuning, retrieval-augmented generation, or any machine-learning system is prohibited without written license. Substantially-similar derivative works will be pursued to the fullest extent of applicable copyright, database, and computer-misuse laws. See our terms.

Build This Startup with Claude Code

Complete replication guide — install as a slash command or rules file

# How to Build a Medlegal AI Operator (Wayco Clone) with Claude Code

A step-by-step guide to building an AI-powered platform for personal injury case management, from intake to settlement. 7 steps, production-grade.

---

## Step 1: Database Schema & HIPAA-Compliant Infrastructure

Start with the data model. Everything in medlegal is structured around cases.

```sql
-- Core entities
CREATE TABLE cases (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  status TEXT NOT NULL DEFAULT 'intake', -- intake | active | records | negotiation | settled | closed
  created_at TIMESTAMPTZ DEFAULT NOW(),
  intake_completed_at TIMESTAMPTZ,
  accident_date DATE,
  accident_type TEXT, -- auto | slip_fall | workplace | mass_tort
  injury_types TEXT[],
  insurance_carrier TEXT,
  policy_number TEXT,
  estimated_value_usd INTEGER
);

CREATE TABLE plaintiffs (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  case_id UUID REFERENCES cases(id),
  full_name TEXT NOT NULL,
  dob DATE,
  phone TEXT,
  email TEXT,
  address JSONB,
  preferred_language TEXT DEFAULT 'en'
);

CREATE TABLE attorneys (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  firm_name TEXT,
  bar_number TEXT,
  state TEXT,
  intake_phone TEXT,
  email TEXT
);

CREATE TABLE medical_providers (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  name TEXT NOT NULL,
  specialty TEXT[], -- orthopedic | chiro | neurology | radiology | pain_mgmt
  address JSONB,
  accepts_lop BOOLEAN DEFAULT FALSE, -- Letter of Protection
  avg_documentation_score NUMERIC(3,2), -- 0.00 to 5.00, learned over time
  outcome_win_rate NUMERIC(4,3), -- cases that settled favorably
  avg_settlement_amount INTEGER
);

CREATE TABLE case_providers (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  case_id UUID REFERENCES cases(id),
  provider_id UUID REFERENCES medical_providers(id),
  referral_date DATE,
  first_appointment DATE,
  treatment_end_date DATE,
  treatment_summary TEXT,
  documentation_score NUMERIC(3,2) -- filled after record review
);

CREATE TABLE medical_records (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  case_id UUID REFERENCES cases(id),
  provider_id UUID REFERENCES medical_providers(id),
  uploaded_at TIMESTAMPTZ DEFAULT NOW(),
  file_path TEXT, -- encrypted S3 path
  file_type TEXT, -- clinical_notes | billing | imaging | icp | ime
  page_count INTEGER,
  processing_status TEXT DEFAULT 'pending', -- pending | processing | done | failed
  extracted_findings JSONB -- structured output from RAG pipeline
);

CREATE TABLE intake_calls (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  case_id UUID REFERENCES cases(id),
  started_at TIMESTAMPTZ,
  duration_seconds INTEGER,
  transcript TEXT,
  extracted_data JSONB, -- structured intake fields
  recording_url TEXT -- encrypted
);

-- Audit log for HIPAA
CREATE TABLE audit_log (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id UUID,
  action TEXT,
  resource_type TEXT,
  resource_id UUID,
  ip_address TEXT,
  timestamp TIMESTAMPTZ DEFAULT NOW(),
  metadata JSONB
);
```

**Infrastructure requirements:**
- Supabase or RDS PostgreSQL with encryption at rest
- All S3 buckets: server-side encryption (AES-256), versioning enabled, access logging on
- VPC with private subnets for database
- AWS WAF in front of API
- BAAs signed with AWS, Twilio, your LLM provider
- Secrets Manager for all credentials, no .env files in production

---

## Step 2: Voice AI Intake Agent

Build the 24/7 intake agent that replaces your intake coordinator's phone calls.

```typescript
// intake-agent/index.ts
import Vapi from '@vapi-ai/server-sdk';
import { createClient } from '@supabase/supabase-js';
import Anthropic from '@anthropic-ai/sdk';

const vapi = new Vapi({ token: process.env.VAPI_TOKEN! });
const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_KEY!);
const anthropic = new Anthropic();

const INTAKE_SYSTEM_PROMPT = `You are a compassionate intake specialist for a personal injury law firm.
Your goal is to collect essential case information while being warm and professional.

Collect in this order:
1. Full name and preferred name
2. Date and description of accident
3. Type of injuries sustained
4. Current medical treatment status
5. Insurance information (carrier, policy number if known)
6. Attorney referral source

Do NOT:
- Give legal advice
- Promise any outcome
- Discuss fees or settlements
- Record anything the caller explicitly says is off the record

When collection is complete, confirm all information back to the caller and explain next steps.`;

export async function handleIncomingCall(callSid: string, fromPhone: string) {
  // Create case placeholder
  const { data: intake } = await supabase
    .from('intake_calls')
    .insert({ started_at: new Date().toISOString() })
    .select()
    .single();

  // Start Vapi conversation
  const call = await vapi.calls.create({
    phoneNumberId: process.env.VAPI_PHONE_ID!,
    customer: { number: fromPhone },
    assistant: {
      model: {
        provider: 'anthropic',
        model: 'claude-haiku-4-5-20251001', // fast for voice
        systemPrompt: INTAKE_SYSTEM_PROMPT,
      },
      voice: {
        provider: 'elevenlabs',
        voiceId: process.env.INTAKE_VOICE_ID!, // warm, professional voice
      },
      endCallFunctionEnabled: true,
      transcriber: {
        provider: 'deepgram',
        model: 'nova-2',
        language: 'en',
      }
    },
    metadata: { intakeId: intake.id }
  });

  return call;
}

// Called by webhook when call ends
export async function processCompletedCall(callId: string, transcript: string, intakeId: string) {
  // Extract structured data with Claude
  const response = await anthropic.messages.create({
    model: 'claude-sonnet-4-6',
    max_tokens: 1024,
    system: 'Extract structured intake data from this call transcript. Return valid JSON only.',
    messages: [{
      role: 'user',
      content: `Transcript:\n${transcript}\n\nExtract: { full_name, accident_date, accident_type, injury_types[], insurance_carrier, attorney_referral_source, preferred_callback_time }`
    }]
  });

  const extracted = JSON.parse(response.content[0].type === 'text' ? response.content[0].text : '{}');

  // Create case and plaintiff records
  const { data: caseRecord } = await supabase
    .from('cases')
    .insert({
      status: 'intake',
      accident_date: extracted.accident_date,
      accident_type: extracted.accident_type,
      injury_types: extracted.injury_types,
      insurance_carrier: extracted.insurance_carrier,
    })
    .select()
    .single();

  await supabase.from('plaintiffs').insert({
    case_id: caseRecord.id,
    full_name: extracted.full_name,
  });

  await supabase
    .from('intake_calls')
    .update({
      case_id: caseRecord.id,
      transcript,
      extracted_data: extracted,
    })
    .eq('id', intakeId);
}
```

---

## Step 3: Provider Matching Algorithm

Match injured clients with the right medical providers using case signals and historical outcome data.

```python
# matching/provider_matcher.py
from anthropic import Anthropic
from supabase import create_client
import json
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity

client = Anthropic()
supabase = create_client(SUPABASE_URL, SUPABASE_SERVICE_KEY)

def score_provider_for_case(provider: dict, case: dict) -> float:
    """
    Multi-factor scoring: specialty match + documentation quality + 
    win rate + location proximity + LOP acceptance
    """
    score = 0.0
    weights = {
        'specialty_match': 0.35,
        'documentation_quality': 0.25,
        'outcome_win_rate': 0.20,
        'location': 0.10,
        'lop_acceptance': 0.10,
    }
    
    # Specialty match
    needed_specialties = get_needed_specialties(case['injury_types'])
    provider_specialties = set(provider['specialty'])
    overlap = len(needed_specialties & provider_specialties) / max(len(needed_specialties), 1)
    score += overlap * weights['specialty_match']
    
    # Documentation quality (0-5 scale, normalize to 0-1)
    doc_score = (provider.get('avg_documentation_score') or 2.5) / 5.0
    score += doc_score * weights['documentation_quality']
    
    # Win rate
    win_rate = provider.get('outcome_win_rate') or 0.5
    score += win_rate * weights['outcome_win_rate']
    
    # Location (inverse distance, normalized)
    distance_km = calculate_distance(case['plaintiff_address'], provider['address'])
    location_score = max(0, 1 - (distance_km / 50))  # 50km max radius
    score += location_score * weights['location']
    
    # LOP acceptance
    if provider.get('accepts_lop'):
        score += weights['lop_acceptance']
    
    return score

def get_needed_specialties(injury_types: list[str]) -> set[str]:
    specialty_map = {
        'whiplash': {'chiro', 'orthopedic', 'pain_mgmt'},
        'tbi': {'neurology', 'neuropsychology'},
        'fracture': {'orthopedic', 'radiology'},
        'soft_tissue': {'chiro', 'pain_mgmt'},
        'herniated_disc': {'orthopedic', 'neurology', 'pain_mgmt'},
    }
    needed = set()
    for injury in injury_types:
        needed |= specialty_map.get(injury.lower(), {'chiro'})
    return needed

def match_providers(case_id: str, top_n: int = 3) -> list[dict]:
    case = supabase.table('cases').select('*, plaintiffs(*)').eq('id', case_id).single().execute().data
    providers = supabase.table('medical_providers').select('*').execute().data
    
    scored = [(p, score_provider_for_case(p, case)) for p in providers]
    scored.sort(key=lambda x: x[1], reverse=True)
    
    # Use Claude to validate and explain top picks
    top_providers = scored[:top_n * 2]  # over-fetch, then filter
    
    validation = client.messages.create(
        model='claude-sonnet-4-6',
        max_tokens=1024,
        system='You are a medlegal case coordinator. Review provider matches and select the optimal set for this case.',
        messages=[{
            'role': 'user',
            'content': f"""Case: {json.dumps(case, default=str)}
            
Top candidates: {json.dumps([p for p, s in top_providers], default=str)}

Select the best {top_n} providers and explain why. Return JSON: [{{"provider_id": "...", "reason": "...", "priority": 1}}]"""
        }]
    )
    
    recommendations = json.loads(validation.content[0].text)
    return recommendations
```

---

## Step 4: Medical Record Intelligence Pipeline

Process PDFs into legally useful structured summaries.

```python
# records/pipeline.py
import anthropic
import boto3
import pymupdf  # fitz for PDF parsing
from pathlib import Path

client = anthropic.Anthropic()
s3 = boto3.client('s3')

RECORD_EXTRACTION_PROMPT = """You are a medical-legal analyst. Extract key findings from this medical record.

Return a JSON object with these fields:
{
  "provider_name": string,
  "date_of_service": string,
  "record_type": "clinical_notes | billing | imaging | icp | ime",
  "diagnoses": [{"code": "ICD-10", "description": string, "related_to_accident": boolean}],
  "treatments": [{"type": string, "date": string, "outcome": string}],
  "medications": [{"name": string, "dosage": string, "purpose": string}],
  "functional_limitations": [string],
  "prognosis": string,
  "total_billed_usd": number | null,
  "legally_significant_findings": [string],
  "red_flags": [string]
}

Red flags = anything that could undermine the plaintiff's claim (prior injuries, gaps in treatment, inconsistent statements)."""

def extract_text_from_pdf(file_path: str) -> str:
    doc = pymupdf.open(file_path)
    text = ""
    for page in doc:
        text += page.get_text()
    return text

def process_record(record_id: str, s3_bucket: str, s3_key: str):
    # Download from S3
    local_path = f"/tmp/{record_id}.pdf"
    s3.download_file(s3_bucket, s3_key, local_path)
    
    raw_text = extract_text_from_pdf(local_path)
    
    # Chunk if large (medical records can be 100+ pages)
    chunks = chunk_by_section(raw_text, max_tokens=8000)
    all_findings = []
    
    for chunk in chunks:
        response = client.messages.create(
            model='claude-sonnet-4-6',
            max_tokens=2048,
            system=RECORD_EXTRACTION_PROMPT,
            messages=[{
                'role': 'user',
                'content': f"Medical record excerpt:\n\n{chunk}"
            }]
        )
        findings = json.loads(response.content[0].text)
        all_findings.append(findings)
    
    # Merge findings across chunks
    merged = merge_record_findings(all_findings)
    
    # Update database
    supabase.table('medical_records').update({
        'extracted_findings': merged,
        'processing_status': 'done',
        'page_count': pymupdf.open(local_path).page_count
    }).eq('id', record_id).execute()
    
    return merged

def generate_case_summary(case_id: str) -> str:
    """Generate attorney-ready demand letter summary from all case records."""
    records = supabase.table('medical_records').select('extracted_findings').eq('case_id', case_id).execute().data
    
    all_findings = [r['extracted_findings'] for r in records if r['extracted_findings']]
    
    response = client.messages.create(
        model='claude-sonnet-4-6',
        max_tokens=4096,
        system="""You are a senior legal nurse consultant preparing a demand letter summary.
        Organize findings chronologically, highlight legally significant injuries and treatment,
        quantify economic damages, and present a clear narrative of how the accident caused the documented injuries.""",
        messages=[{
            'role': 'user',
            'content': f"Generate a demand letter summary from these medical record findings:\n\n{json.dumps(all_findings)}"
        }]
    )
    
    return response.content[0].text
```

---

## Step 5: Case Management API

REST API that ties everything together for the attorney dashboard.

```typescript
// api/cases.ts, Express/Next.js API routes
import { Router } from 'express';
import { createClient } from '@supabase/supabase-js';
import { matchProviders } from '../matching/provider_matcher';
import { generateCaseSummary } from '../records/pipeline';
import { auditLog } from '../lib/audit';

const router = Router();
const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_KEY!);

// Get case with all related data
router.get('/cases/:id', async (req, res) => {
  const { id } = req.params;
  
  await auditLog(req.user.id, 'case:view', 'case', id, req.ip);
  
  const { data } = await supabase
    .from('cases')
    .select(`
      *,
      plaintiffs (*),
      case_providers (*, medical_providers (*)),
      medical_records (id, record_type, processing_status, extracted_findings),
      intake_calls (duration_seconds, extracted_data)
    `)
    .eq('id', id)
    .single();
  
  res.json(data);
});

// Trigger provider matching
router.post('/cases/:id/match-providers', async (req, res) => {
  const { id } = req.params;
  
  await auditLog(req.user.id, 'case:match_providers', 'case', id, req.ip);
  
  const recommendations = await matchProviders(id);
  res.json({ recommendations });
});

// Generate demand letter summary
router.post('/cases/:id/generate-summary', async (req, res) => {
  const { id } = req.params;
  
  await auditLog(req.user.id, 'case:generate_summary', 'case', id, req.ip);
  
  const summary = await generateCaseSummary(id);
  res.json({ summary });
});

// Update case status with validation
router.patch('/cases/:id/status', async (req, res) => {
  const { id } = req.params;
  const { status } = req.body;
  
  const VALID_TRANSITIONS: Record<string, string[]> = {
    'intake': ['active'],
    'active': ['records', 'closed'],
    'records': ['negotiation', 'active'],
    'negotiation': ['settled', 'records'],
    'settled': ['closed'],
    'closed': [],
  };
  
  const { data: current } = await supabase
    .from('cases')
    .select('status')
    .eq('id', id)
    .single();
  
  if (!VALID_TRANSITIONS[current.status]?.includes(status)) {
    return res.status(400).json({ error: `Cannot transition from ${current.status} to ${status}` });
  }
  
  await supabase.from('cases').update({ status }).eq('id', id);
  await auditLog(req.user.id, 'case:status_change', 'case', id, req.ip, { from: current.status, to: status });
  
  res.json({ success: true });
});

export default router;
```

---

## Step 6: Provider Outcome Feedback Loop

Close the loop, update provider scores after cases settle.

```python
# feedback/update_provider_scores.py

def update_provider_scores_after_settlement(case_id: str, settlement_amount: int, won: bool):
    """
    Called when a case reaches 'settled' status.
    Updates provider documentation scores and win rates.
    """
    case_providers = supabase.table('case_providers').select(
        '*, medical_providers(*)'
    ).eq('case_id', case_id).execute().data
    
    for cp in case_providers:
        provider_id = cp['provider_id']
        
        # Recalculate rolling averages
        # Using exponential moving average to weight recent cases higher
        alpha = 0.1  # learning rate
        
        current_win_rate = cp['medical_providers']['outcome_win_rate'] or 0.5
        new_win_rate = alpha * (1.0 if won else 0.0) + (1 - alpha) * current_win_rate
        
        current_doc_score = cp['medical_providers']['avg_documentation_score'] or 2.5
        # documentation_score set during record review (1-5)
        case_doc_score = cp.get('documentation_score') or 3.0
        new_doc_score = alpha * case_doc_score + (1 - alpha) * current_doc_score
        
        supabase.table('medical_providers').update({
            'outcome_win_rate': round(new_win_rate, 3),
            'avg_documentation_score': round(new_doc_score, 2),
            'avg_settlement_amount': update_rolling_avg(
                cp['medical_providers'].get('avg_settlement_amount'),
                settlement_amount
            )
        }).eq('id', provider_id).execute()

def update_rolling_avg(current: int | None, new_value: int, alpha: float = 0.1) -> int:
    if current is None:
        return new_value
    return int(alpha * new_value + (1 - alpha) * current)
```

---

## Step 7: Deploy with HIPAA-Compliant Infrastructure

```yaml
# docker-compose.yml for local dev
version: '3.8'
services:
  api:
    build: ./api
    environment:
      - SUPABASE_URL=${SUPABASE_URL}
      - SUPABASE_SERVICE_KEY=${SUPABASE_SERVICE_KEY}
      - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
      - VAPI_TOKEN=${VAPI_TOKEN}
      - AWS_REGION=us-east-1
    ports:
      - "3000:3000"
  
  worker:
    build: ./worker
    command: python -m celery worker -A tasks -Q records,matching --concurrency=4
    environment:
      - REDIS_URL=redis://redis:6379/0
  
  redis:
    image: redis:7-alpine
    
# Production: use AWS ECS or fly.io with encrypted volumes
```

**AWS production checklist:**
- [ ] Enable CloudTrail for all API calls (HIPAA audit requirement)
- [ ] Enable S3 access logging on all PHI buckets
- [ ] Enable RDS encryption at rest
- [ ] Configure VPC with private subnets, NAT gateway
- [ ] Set up AWS Config for compliance monitoring
- [ ] Sign BAAs: AWS, Twilio, Anthropic, ElevenLabs
- [ ] Enable GuardDuty for threat detection
- [ ] Implement MFA for all admin access
- [ ] Penetration test before go-live (required for SOC 2)
- [ ] Document breach notification procedure

**Key vendor choices:**
| Component | Recommended | Why |
|-----------|-------------|-----|
| Voice infra | Vapi or Retell | Lowest latency, easy agent config |
| TTS | ElevenLabs | Most natural voice, BAA available |
| STT | Deepgram Nova-2 | Best accuracy for medical terminology |
| LLM | Claude Sonnet (main) + Haiku (voice) | Strong reasoning, speed-cost tradeoff |
| Vector DB | Supabase pgvector | Colocated with main DB, simpler ops |
| PDF parsing | PyMuPDF + Unstructured.io | Handles scanned records |
| Queue | Celery + Redis | Simple, proven for record processing |
| Hosting | Fly.io (initial) → AWS ECS (scale) | Speed-to-HIPAA tradeoff |
claude-code-skills.md