CREATE TABLE programs (
-- Identity
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
slug TEXT UNIQUE NOT NULL, -- URL-friendly identifier
name TEXT NOT NULL, -- "Supplemental Nutrition Assistance Program"
short_name TEXT, -- "SNAP"
-- Classification
category TEXT NOT NULL, -- See categories below
subcategory TEXT,
tags TEXT[] DEFAULT '{}', -- ["food", "nutrition", "ebt"]
-- Administering agency
agency_name TEXT NOT NULL,
agency_level TEXT NOT NULL, -- federal, state, county, city
agency_website TEXT,
agency_phone TEXT,
-- Geographic coverage
coverage_type TEXT NOT NULL, -- federal, state, county, city
coverage_states TEXT[] DEFAULT '{}', -- ["TX", "CA"] or ["*"] for all
coverage_fips TEXT[] DEFAULT '{}', -- County FIPS codes
-- Eligibility
eligibility_rules JSONB DEFAULT '[]',
eligibility_summary TEXT, -- Human-readable summary
income_limit_fpl_percent INTEGER, -- e.g., 130 for 130% FPL
income_limit_type TEXT, -- gross, net, modified_gross
asset_limit_cents INTEGER,
categorical_eligibility BOOLEAN DEFAULT false,
-- Benefit details
benefit_type TEXT NOT NULL, -- cash, voucher, service, tax_credit
benefit_value_cents INTEGER,
benefit_value_max_cents INTEGER,
benefit_frequency TEXT, -- monthly, annual, one_time
benefit_description TEXT,
-- Application process
application_url TEXT,
application_methods TEXT[] DEFAULT '{}', -- ["online", "in_person", "phone", "mail"]
processing_time_days INTEGER,
required_documents JSONB DEFAULT '[]',
how_to_apply JSONB DEFAULT '[]', -- Step-by-step instructions
-- Content
description TEXT NOT NULL,
short_description TEXT,
what_it_covers TEXT,
who_qualifies TEXT,
tips TEXT, -- Insider tips for applicants
-- Search
search_vector TSVECTOR, -- Full-text search
-- Metadata
source_url TEXT, -- Where we got this data
last_verified_at TIMESTAMPTZ,
is_active BOOLEAN DEFAULT true,
-- Timestamps
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- Indexes for performance
CREATE INDEX idx_programs_category ON programs(category);
CREATE INDEX idx_programs_coverage ON programs(coverage_type, coverage_states);
CREATE INDEX idx_programs_fips ON programs USING GIN(coverage_fips);
CREATE INDEX idx_programs_search ON programs USING GIN(search_vector);
CREATE INDEX idx_programs_active ON programs(is_active) WHERE is_active = true;