Back to Guides
Claims Processing

Claims Adjudication Guide

Complete guide to healthcare claims adjudication engine design. Learn payment calculation, denial codes, coordination of benefits, and automated processing rules.

Claims Adjudication Process

1

Receipt & Validation

Claim received via EDI 837, validated for format and required fields

2

Eligibility Check

Verify patient coverage active on service date

3

Duplicate Detection

Check for duplicate submissions using service date + provider + patient

4

Prior Authorization

Verify PA exists if required for service

5

Medical Necessity

Validate diagnosis codes support procedure codes

6

Pricing

Apply contracted rates, fee schedule, or usual & customary

7

COB

Coordinate benefits if patient has multiple policies

8

Patient Responsibility

Calculate deductible, copay, coinsurance

9

Payment Decision

Approve, deny, or pend for manual review

10

Remittance

Generate ERA/EOB (EDI 835) with payment details

Adjudication Database Schema

-- Claim Adjudication
CREATE TABLE claim_adjudication (
  id                    UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  claim_id              UUID NOT NULL REFERENCES claim(id),
  claim_line_id         UUID REFERENCES claim_line(id),
  
  -- Adjudication Status
  adjudication_status   VARCHAR(50),              -- approved, denied, pended
  adjudication_date     TIMESTAMP NOT NULL DEFAULT NOW(),
  
  -- Amounts
  billed_amount         DECIMAL(10,2) NOT NULL,
  allowed_amount        DECIMAL(10,2),
  deductible_amount     DECIMAL(10,2) DEFAULT 0,
  copay_amount          DECIMAL(10,2) DEFAULT 0,
  coinsurance_amount    DECIMAL(10,2) DEFAULT 0,
  cob_amount            DECIMAL(10,2) DEFAULT 0,   -- Paid by other insurance
  paid_amount           DECIMAL(10,2),
  patient_responsibility DECIMAL(10,2),
  
  -- Adjustments
  adjustment_amount     DECIMAL(10,2) DEFAULT 0,
  adjustment_reason     VARCHAR(10),               -- CARC code
  
  -- Denial
  denial_code           VARCHAR(10),               -- CARC code
  denial_reason         TEXT,
  remark_code           VARCHAR(10),               -- RARC code
  
  -- Pricing
  contract_rate         DECIMAL(10,2),
  fee_schedule_rate     DECIMAL(10,2),
  pricing_method        VARCHAR(50),               -- contracted, fee_schedule, UCR
  
  -- Processing
  auto_adjudicated      BOOLEAN DEFAULT FALSE,
  reviewer_id           UUID,
  review_notes          TEXT,
  
  created_at            TIMESTAMP DEFAULT NOW()
);

CREATE INDEX idx_adj_claim ON claim_adjudication(claim_id);
CREATE INDEX idx_adj_status ON claim_adjudication(adjudication_status);

-- Payment Calculation Rules
CREATE TABLE adjudication_rule (
  id                    UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  
  rule_name             VARCHAR(255) NOT NULL,
  rule_type             VARCHAR(50),               -- eligibility, pricing, medical_necessity
  priority              INTEGER,
  
  -- Conditions (JSON for flexibility)
  conditions            JSONB,                     -- e.g., {"service_type": "surgery", "place_of_service": "11"}
  
  -- Actions
  action_type           VARCHAR(50),               -- approve, deny, pend, adjust
  action_config         JSONB,                     -- Action parameters
  
  -- Status
  active                BOOLEAN DEFAULT TRUE,
  effective_date        DATE,
  termination_date      DATE,
  
  created_at            TIMESTAMP DEFAULT NOW()
);

-- Denial Codes Reference
CREATE TABLE denial_code (
  code                  VARCHAR(10) PRIMARY KEY,
  code_type             VARCHAR(10),               -- CARC, RARC
  description           TEXT,
  category              VARCHAR(100),              -- CO, OA, PR, PI
  patient_responsibility BOOLEAN,
  
  created_at            TIMESTAMP DEFAULT NOW()
);

-- Coordination of Benefits
CREATE TABLE cob_calculation (
  id                    UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  claim_id              UUID NOT NULL REFERENCES claim(id),
  
  -- Primary Insurance
  primary_payer_id      UUID,
  primary_allowed       DECIMAL(10,2),
  primary_paid          DECIMAL(10,2),
  
  -- Secondary Insurance
  secondary_payer_id    UUID,
  secondary_allowed     DECIMAL(10,2),
  secondary_paid        DECIMAL(10,2),
  
  -- Tertiary (if applicable)
  tertiary_payer_id     UUID,
  tertiary_paid         DECIMAL(10,2),
  
  -- Total
  total_paid            DECIMAL(10,2),
  patient_balance       DECIMAL(10,2),
  
  calculated_date       TIMESTAMP DEFAULT NOW()
);

Payment Calculation Logic

Step-by-Step Calculation

Example Claim:
Billed Amount: $500.00
Contracted Rate: $350.00
Deductible Remaining: $100.00
Coinsurance: 20%
Copay: $20.00
1. Allowed Amount$350.00
2. Apply Deductible-$100.00
Remaining After Deductible$250.00
3. Apply Coinsurance (20% × $250)-$50.00
4. Apply Copay-$20.00
Insurance Pays$180.00
Patient Owes$170.00

Common Denial Codes

CodeCategoryDescription
CO-16ContractualClaim lacks information needed for adjudication
CO-50ContractualNon-covered charges
PR-1PatientDeductible amount
PR-2PatientCoinsurance amount
OA-23OtherImpact of prior payer adjudication
CO-97ContractualBenefit for this service included in another service
Category Codes: CO = Contractual Obligation, PR = Patient Responsibility, OA = Other Adjustment, PI = Payer Initiated

SQL Query Examples

Calculate Payment for Claim

WITH claim_totals AS (
  SELECT 
    c.id as claim_id,
    SUM(cl.charge_amount) as billed_amount,
    SUM(ca.allowed_amount) as allowed_amount,
    SUM(ca.deductible_amount) as deductible,
    SUM(ca.copay_amount) as copay,
    SUM(ca.coinsurance_amount) as coinsurance,
    SUM(ca.paid_amount) as insurance_paid
  FROM claim c
  JOIN claim_line cl ON c.id = cl.claim_id
  LEFT JOIN claim_adjudication ca ON cl.id = ca.claim_line_id
  WHERE c.claim_number = 'CLM123456'
  GROUP BY c.id
)
SELECT 
  billed_amount,
  allowed_amount,
  allowed_amount - billed_amount as adjustment,
  deductible + copay + coinsurance as patient_responsibility,
  insurance_paid
FROM claim_totals;

Top Denial Reasons

SELECT 
  dc.code,
  dc.description,
  COUNT(*) as denial_count,
  SUM(ca.billed_amount) as total_denied_amount
FROM claim_adjudication ca
JOIN denial_code dc ON ca.denial_code = dc.code
WHERE ca.adjudication_date >= CURRENT_DATE - INTERVAL '30 days'
  AND ca.adjudication_status = 'denied'
GROUP BY dc.code, dc.description
ORDER BY denial_count DESC
LIMIT 10;

Related Healthcare Guides