Appeals — Workflow & State Machine

← Back to Platform Architecture

Appeals — Workflow & State Machine

Overview

The appeals table tracks insurance denial appeals from creation through submission and resolution. Appeals leverage precedents as evidence, follow a strict state machine, and are versioned immutably (like precedents).

Core Concept

An appeal is a structured request to overturn a denial using clinical evidence and precedent-based argument. Lifecycle:
  • User drafts appeal (state: Draft) — selects denial, finds relevant precedents
  • User saves drafts (versions auto-snapshots)
  • System validates appeal (checks evidence, runs quality gates)
  • User submits (state: Submitted)
  • Payer reviews and responds (state: Under Review → Approved/Denied)
  • Each state transition is immutable: previous versions are preserved in appeal_versions.

    Schema Definition

    CREATE TABLE appeals (
      id UUID PRIMARY KEY,
      
      -- Claim/Denial Reference
      denial_id TEXT NOT NULL,       -- External claim/denial ID (e.g., "CLM-2024-001")
      payer_id TEXT NOT NULL,        -- Payer identifier (e.g., "cigna-v2024")
      denial_code TEXT,              -- X.12 denial code (UR-001, MN-007, etc.)
      
      -- Appeal Narrative
      narrative TEXT,                -- Free-text appeal argument (markdown)
      
      -- Evidence Selection (JSONB for flexibility)
      evidence_elements JSONB,
      -- Shape: {
      --   "clinical_notes": ["date-range-1", "date-range-2"],
      --   "gaf_score": "35",
      --   "functional_impact": "description",
      --   "treatment_plan": "url or summary"
      -- }
      
      -- Precedent Evidence (UUID references)
      selected_precedent_ids UUID[] DEFAULT '{}',
      
      -- State Machine
      status TEXT NOT NULL DEFAULT 'Draft',
      -- Valid: Draft, Submitted, Under Review, Approved, Denied, Withdrawn, Archived
      
      -- Validation
      validation_status TEXT,        -- "clean", "warnings", "errors"
      validation_result JSONB,       -- { "checks_passed": [...], "warnings": [...], "errors": [...] }
      
      -- Workflow
      created_by UUID NOT NULL REFERENCES users(id),
      submitted_by UUID REFERENCES users(id),
      submitted_at TIMESTAMPTZ,
      
      -- Audit
      created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
      updated_at TIMESTAMPTZ DEFAULT NOW(),
      notes TEXT,
      
      -- Tenant Isolation
      tenant_id UUID NOT NULL REFERENCES tenants(id),
      
      -- Versioning
      version INTEGER NOT NULL DEFAULT 1,
      
      -- Indexes
      UNIQUE(tenant_id, denial_id),  -- One appeal per denial per tenant
      INDEX idx_appeal_tenant (tenant_id, status),
      INDEX idx_appeal_status (status),
      INDEX idx_appeal_created_at (created_at DESC),
      INDEX idx_appeal_submitted_at (submitted_at DESC),
      INDEX idx_appeal_precedent (selected_precedent_ids)
    );

    State Machine

    ┌────────┐
    │ Draft  │  ← User is building appeal, saving drafts
    └───┬────┘
        │ (user clicks Submit)
        ↓
    ┌──────────┐
    │Submitted │  ← Appeal sent to payer or internal queue
    └───┬──────┘
        │ (payer reviews)
        ↓
    ┌──────────────┐
    │ Under Review │  ← Payer is evaluating
    └─┬──────────┬─┘
      │          │
      │(approved)│(denied)
      ↓          ↓
    ┌────────┐  ┌────────┐
    │Approved│  │ Denied │
    └────────┘  └────────┘
    Valid Transitions: