Integration
How eligibility rules flow from policy to code, and how to integrate structured rulesets into your systems.
How Eligibility Determination Works
User Attributes
A person's circumstances expressed as key/value pairs: income, age, household size, residency, etc.
{
"household_income": 2400,
"household_size": 3,
"age": 34,
"residency": "CA"
}
Determination
A clear, consistent, calculable result: eligible or not, with detailed validation messages.
{
"eligible": true,
"passed": ["income", "age"],
"failed": [],
"messages": [...]
}
The rules engine is a pure function: given a person's attributes and a program's ruleset, it returns a determination. The same inputs always produce the same outputs. This makes eligibility calculable, testable, and auditable.
Each rule in a ruleset maps to a specific attribute key. The engine evaluates each condition and returns which rules passed, which failed, and which were skipped (due to missing data or conditional logic).
From Policy to Code
Eligibility rules originate in legislation, regulation, and policy documents. The path to implementation often looks like this:
Legislation & Policy
Laws and regulations define who qualifies for a program. These documents contain the authoritative eligibility criteria, often in legal language.
Agency Interpretation
Agencies publish guidance, handbooks, and FAQs that clarify how rules apply. This is where edge cases and exceptions get documented.
Structured Encoding
Rules are distilled into machine-readable YAML with clear operators, values, and source citations. Each condition becomes testable.
operator: less_than_or_equal
value: 130
unit: percent_fpl
Software Integration
Applications consume the ruleset via API or direct import. The rules engine evaluates user data against the current ruleset version.
Version & Update
When policy changes, the ruleset is updated via pull request. Git history provides full audit trail. Downstream systems pull the new version.
For Agency Staff & Policy Teams
Encoding Existing Programs
Your program's eligibility rules likely already exist in handbooks, policy documents, or legacy systems. These can be reliably distilled into structured rulesets.
Start with your program's eligibility page or handbook. Identify each discrete condition (income limits, age ranges, residency requirements). Map each to a rule with a clear operator and value.
Adding New Rules
When policy changes, add or update conditions in the ruleset. Each change is a clear, versioned edit that can be reviewed, tested, and deployed.
New rules should include: a unique ID, the attribute key being checked, the operator and value, a human-readable description, and a source URL citing the authoritative policy.
Distillation Prompt
Use this prompt with an LLM to extract eligibility conditions from a program's documentation into a structured ruleset:
You are an eligibility rules encoder. Given a program's documentation, extract the eligibility conditions into a structured YAML ruleset. For each eligibility condition, create a rule with: - id: A unique snake_case identifier (e.g., "income_gross_limit") - type: The category (income_fpl, age, residency, citizenship, household, employment, disability, enrollment, assets) - key: The attribute key to check (e.g., "household_income_fpl_percent") - operator: One of: equals, not_equals, in, not_in, less_than, less_than_or_equal, greater_than, greater_than_or_equal, between, exists, matches - value: The threshold or allowed values - unit: If applicable (percent, dollars, months, years) - description: A clear, human-readable explanation - source_url: The URL where this rule is documented Use these conventions: - Income as FPL percentage: key ends in "_fpl_percent", value is the percentage (e.g., 130 for 130% FPL) - Age ranges: use "between" operator with [min, max] array - Categorical requirements: use "in" operator with array of valid values - Boolean conditions: use "exists" operator Output only valid YAML. Include all conditions you can identify, even if some details require assumptions (note assumptions in the description). --- Program URL: [URL] Program Documentation: [PASTE CONTENT HERE]
Technical Integration
Direct Import
Clone the repository and import YAML files directly into your application. Parse with any YAML library.
data/jurisdictions/federal/services/snap.yaml
Raw GitHub URLs
Fetch rulesets directly from GitHub's raw content URLs for always-current data.
raw.githubusercontent.com/civicstudio/eligibility-rules/main/data/...
Rules Engine
Implement the validation function in your language. The engine evaluates user attributes against ruleset conditions.
validate(ruleset, attributes) → result
Code Examples
import yaml from 'js-yaml';
// Fetch and parse ruleset
const response = await fetch(
'https://raw.githubusercontent.com/civicstudio/eligibility-rules/main/data/jurisdictions/federal/services/snap.yaml'
);
const ruleset = yaml.load(await response.text());
// User attributes to validate
const attributes = {
household_income_fpl_percent: 125,
household_size: 3,
residency_state: 'CA',
citizenship_status: 'us_citizen'
};
// Simple validation function
function validate(ruleset, attributes) {
const results = { passed: [], failed: [], skipped: [] };
for (const rule of ruleset.ruleset.rules) {
const value = attributes[rule.key];
if (value === undefined) {
results.skipped.push(rule.id);
continue;
}
const passed = evaluateRule(rule, value);
(passed ? results.passed : results.failed).push(rule.id);
}
results.eligible = results.failed.length === 0;
return results;
}
const result = validate(ruleset, attributes);
console.log(result); // { eligible: true, passed: [...], failed: [], skipped: [...] }
import yaml
import requests
# Fetch and parse ruleset
url = "https://raw.githubusercontent.com/civicstudio/eligibility-rules/main/data/jurisdictions/federal/services/snap.yaml"
response = requests.get(url)
ruleset = yaml.safe_load(response.text)
# User attributes to validate
attributes = {
"household_income_fpl_percent": 125,
"household_size": 3,
"residency_state": "CA",
"citizenship_status": "us_citizen"
}
def validate(ruleset: dict, attributes: dict) -> dict:
results = {"passed": [], "failed": [], "skipped": []}
for rule in ruleset["ruleset"]["rules"]:
value = attributes.get(rule["key"])
if value is None:
results["skipped"].append(rule["id"])
continue
passed = evaluate_rule(rule, value)
(results["passed"] if passed else results["failed"]).append(rule["id"])
results["eligible"] = len(results["failed"]) == 0
return results
result = validate(ruleset, attributes)
print(result) # {'eligible': True, 'passed': [...], 'failed': [], 'skipped': [...]}
require 'yaml'
require 'net/http'
# Fetch and parse ruleset
uri = URI("https://raw.githubusercontent.com/civicstudio/eligibility-rules/main/data/jurisdictions/federal/services/snap.yaml")
response = Net::HTTP.get(uri)
ruleset = YAML.safe_load(response)
# User attributes to validate
attributes = {
"household_income_fpl_percent" => 125,
"household_size" => 3,
"residency_state" => "CA",
"citizenship_status" => "us_citizen"
}
def validate(ruleset, attributes)
results = { passed: [], failed: [], skipped: [] }
ruleset["ruleset"]["rules"].each do |rule|
value = attributes[rule["key"]]
if value.nil?
results[:skipped] << rule["id"]
next
end
passed = evaluate_rule(rule, value)
(passed ? results[:passed] : results[:failed]) << rule["id"]
end
results[:eligible] = results[:failed].empty?
results
end
result = validate(ruleset, attributes)
puts result # {:eligible=>true, :passed=>[...], :failed=>[], :skipped=>[...]}
Ready to integrate?
Browse existing rulesets, request an encoding for your program, or contribute directly via GitHub.