Decisions as a Service
Sign Up Now →Sign Up Now

Eligibility and qualification

An application must be tested against stated criteria. The same facts must always get the same answer, a declined application must be explained by the criterion it failed, and the cases a person should look at must reach a person. This example decides who qualifies for a water utility's hardship payment plan.

AvailableWorked exampleRecorded run

How this runs

Every result on this page is the recorded run of the rulebook below through the Helixor rule engine, case by case. In production a rulebook like this runs in a digital worker Available that you build and deploy in the console or with the agent command-line tools; console access is granted on request (email hello@helixor.ai). The rule language itself is described in Rules.

The policy#

  1. Criteria every household must meet.

    An active service account, at least one month in arrears, income no more than 10 percent over the program limit, and no default on a payment plan in the last 12 months. Failing any one declines the application, and the household is told which.

  2. Cases a person decides.

    Income up to 10 percent over the limit is borderline: a case worker reviews it. Income that is not yet verified holds the application until the documents arrive.

  3. What follows.

    A scheduled disconnection is paused while an application that is not declined is open.

  4. Missing facts.

    Nothing is decided until the five facts the criteria read are all known.

What the rules decided#

Each row is one case run through the rulebook. The outcome is one of four; the deciding rule is the one whose recommendation won; derived facts show every criterion the rules established; and the last column lists the obligations and prohibitions that follow.

CaseOutcomeDeciding ruleDerived factsWhat follows
S1 Three months behind, income well under the limit, documents verified.APPROVERDEC04: A complete application that meets every criterion and needs no review qualifies.QUALIFIESnone
S2 Income 25 percent over the program limit.DECLINERDEC01: A household that fails any criterion is declined.FAILS_INCOME INELIGIBLEOBLIGATION_SEND_ELIGIBILITY_NOTICE
S3 Income 6 percent over the limit: inside the review band.HOLDRDEC02: Borderline income, with no failed criterion, is held for a case worker.BORDERLINE_INCOME REVIEW_RECOMMENDEDOBLIGATION_ASSIGN_CASE_WORKER
S4 Qualifies on every criterion, but income is not yet verified.HOLDRDEC03: Unverified income, with no failed criterion, is held until the documents arrive.INCOME_UNVERIFIED DOCUMENTS_REQUESTEDOBLIGATION_REQUEST_INCOME_DOCUMENTS
S5 Defaulted on a payment plan in the last 12 months.DECLINERDEC01: A household that fails any criterion is declined.FAILS_PLAN_HISTORY INELIGIBLEOBLIGATION_SEND_ELIGIBILITY_NOTICE
S6 The account is up to date.DECLINERDEC01: A household that fails any criterion is declined.FAILS_ARREARS INELIGIBLEOBLIGATION_SEND_ELIGIBILITY_NOTICE
S7 Qualifies, and a disconnection is already scheduled.APPROVERDEC04: A complete application that meets every criterion and needs no review qualifies.QUALIFIESOBLIGATION_PAUSE_DISCONNECTION
S8 Income against the limit has not been provided.NEED_DATANo rule ran: missing income_pct_of_limitnonenone
OutcomeMeans here
APPROVEThe household qualifies for the plan.
DECLINEA criterion failed; the derived facts name which one (FAILS_INCOME, FAILS_ARREARS and so on).
HOLDA person decides: a case worker for borderline income, or the application waits for income documents.
NEED_DATAA fact the criteria read is missing; the result names it.

Two things to notice. A declined case carries the failed criterion as a derived fact, so the notice can name it without anyone rereading the file. And the borderline band is a rule of its own: moving it is a one-line change, reviewed like any other.

The rulebook#

Every rule names its owner, its authority, how hard it is, and what it means in plain English, and binds to a typed vocabulary so a misspelled fact fails at load instead of silently never matching.

# Example hardship payment plan eligibility rulebook in the Helixor rule language.
# Fictional domain for helixor.dev: a water utility decides who qualifies for a hardship payment plan.
# Classes and predicates: ontology.json. Each case fact <name> reaches the rules as HAS_<NAME>,
# carrying its value, and KNOWN_<NAME>, true when it has one.
# Recommendations are resolved by @authority, then @priority, then decline > hold > approve.

# ---------------------------------------------------------------- required data

@domain utility_assistance
@ontology example_hardship_plans
@ontology_ver 1
@authority policy
@owner assistance-programs
@severity hard
@category data
@priority 100
@required_fact_mode explicit
@required_facts [HAS_ACCOUNT_ACTIVE, HAS_MONTHS_IN_ARREARS, HAS_INCOME_PCT_OF_LIMIT, HAS_INCOME_VERIFIED, HAS_PRIOR_PLAN_DEFAULT]
@english The application is complete when account status, arrears, income against the limit, income verification and plan history are all known.
@uses_predicates [hasAccountActive, hasMonthsInArrears, hasIncomePctOfLimit, hasIncomeVerified, hasPriorPlanDefault]
@uses_classes [ServiceAccount, Household, PlanApplication]
RDAT01: IF KNOWN_ACCOUNT_ACTIVE AND KNOWN_MONTHS_IN_ARREARS AND KNOWN_INCOME_PCT_OF_LIMIT AND KNOWN_INCOME_VERIFIED AND KNOWN_PRIOR_PLAN_DEFAULT THEN FILE_COMPLETE

# ---------------------------------------------------------------- criteria that fail

@domain utility_assistance
@ontology example_hardship_plans
@authority policy
@owner assistance-programs
@severity hard
@category criterion
@priority 100
@required_fact_mode explicit
@required_facts [HAS_ACCOUNT_ACTIVE]
@english Only an active service account can be placed on a plan.
@uses_predicates [hasAccountActive]
@uses_classes [ServiceAccount]
RCR01: IF FILE_COMPLETE AND HAS_ACCOUNT_ACTIVE == 0 THEN FAILS_ACTIVE_ACCOUNT

@domain utility_assistance
@ontology example_hardship_plans
@authority policy
@owner assistance-programs
@severity hard
@category criterion
@priority 100
@required_fact_mode explicit
@required_facts [HAS_MONTHS_IN_ARREARS]
@english A plan is for an account at least one month in arrears.
@uses_predicates [hasMonthsInArrears]
@uses_classes [ServiceAccount]
RCR02: IF FILE_COMPLETE AND HAS_MONTHS_IN_ARREARS < 1 THEN FAILS_ARREARS

@domain utility_assistance
@ontology example_hardship_plans
@authority policy
@owner assistance-programs
@severity hard
@category criterion
@priority 100
@required_fact_mode explicit
@required_facts [HAS_INCOME_PCT_OF_LIMIT]
@english Household income more than 10 percent over the program limit does not qualify.
@uses_predicates [hasIncomePctOfLimit]
@uses_classes [Household]
RCR03: IF FILE_COMPLETE AND HAS_INCOME_PCT_OF_LIMIT > 1.10 THEN FAILS_INCOME

@domain utility_assistance
@ontology example_hardship_plans
@authority policy
@owner assistance-programs
@severity hard
@category criterion
@priority 100
@required_fact_mode explicit
@required_facts [HAS_PRIOR_PLAN_DEFAULT]
@english A household that defaulted on a payment plan in the last 12 months does not qualify.
@uses_predicates [hasPriorPlanDefault]
@uses_classes [Household]
RCR04: IF FILE_COMPLETE AND HAS_PRIOR_PLAN_DEFAULT == 1 THEN FAILS_PLAN_HISTORY

@domain utility_assistance
@authority policy
@owner assistance-programs
@severity hard
@category criterion
@priority 100
@english An inactive account makes the household ineligible.
RCR05: IF FAILS_ACTIVE_ACCOUNT THEN INELIGIBLE

@domain utility_assistance
@authority policy
@owner assistance-programs
@severity hard
@category criterion
@priority 100
@english No arrears makes the household ineligible.
RCR06: IF FAILS_ARREARS THEN INELIGIBLE

@domain utility_assistance
@authority policy
@owner assistance-programs
@severity hard
@category criterion
@priority 100
@english Income over the limit makes the household ineligible.
RCR07: IF FAILS_INCOME THEN INELIGIBLE

@domain utility_assistance
@authority policy
@owner assistance-programs
@severity hard
@category criterion
@priority 100
@english A recent plan default makes the household ineligible.
RCR08: IF FAILS_PLAN_HISTORY THEN INELIGIBLE

# ---------------------------------------------------------------- cases a person decides

@domain utility_assistance
@ontology example_hardship_plans
@authority policy
@owner assistance-programs
@severity soft
@category borderline
@priority 100
@required_fact_mode explicit
@required_facts [HAS_INCOME_PCT_OF_LIMIT]
@english Income up to 10 percent over the limit is borderline: a case worker reviews it.
@uses_predicates [hasIncomePctOfLimit]
@uses_classes [Household]
RBL01: IF FILE_COMPLETE AND HAS_INCOME_PCT_OF_LIMIT > 1.0 AND HAS_INCOME_PCT_OF_LIMIT <= 1.10 THEN BORDERLINE_INCOME

@domain utility_assistance
@ontology example_hardship_plans
@authority policy
@owner assistance-programs
@severity soft
@category verification
@priority 100
@required_fact_mode explicit
@required_facts [HAS_INCOME_VERIFIED]
@english Income that is not yet verified must be documented before a plan starts.
@uses_predicates [hasIncomeVerified]
@uses_classes [PlanApplication]
RBL02: IF FILE_COMPLETE AND HAS_INCOME_VERIFIED == 0 THEN INCOME_UNVERIFIED

# ---------------------------------------------------------------- recommendations

@domain utility_assistance
@authority policy
@owner assistance-programs
@severity hard
@category decision
@priority 100
@english A household that fails any criterion is declined.
RDEC01: IF INELIGIBLE THEN RECOMMEND_DECLINE

@domain utility_assistance
@authority policy
@owner assistance-programs
@severity hard
@category decision
@priority 80
@english Borderline income, with no failed criterion, is held for a case worker.
RDEC02: IF BORDERLINE_INCOME AND NOT INELIGIBLE THEN RECOMMEND_HOLD AND REVIEW_RECOMMENDED

@domain utility_assistance
@authority policy
@owner assistance-programs
@severity hard
@category decision
@priority 80
@english Unverified income, with no failed criterion, is held until the documents arrive.
RDEC03: IF INCOME_UNVERIFIED AND NOT INELIGIBLE THEN RECOMMEND_HOLD AND DOCUMENTS_REQUESTED

@domain utility_assistance
@authority policy
@owner assistance-programs
@severity hard
@category decision
@priority 100
@english A complete application that meets every criterion and needs no review qualifies.
RDEC04: IF FILE_COMPLETE AND NOT INELIGIBLE AND NOT BORDERLINE_INCOME AND NOT INCOME_UNVERIFIED THEN RECOMMEND_APPROVE AND QUALIFIES

# ---------------------------------------------------------------- what follows from the decision

@domain utility_assistance
@authority policy
@owner assistance-programs
@severity hard
@category obligation
@priority 100
@english A declined household is told which criterion it did not meet.
ROBL01: IF RECOMMEND_DECLINE THEN OBLIGATION(send_eligibility_notice)

@domain utility_assistance
@authority policy
@owner assistance-programs
@severity hard
@category obligation
@priority 100
@english A held application for unverified income triggers a request for income documents.
ROBL02: IF DOCUMENTS_REQUESTED THEN OBLIGATION(request_income_documents)

@domain utility_assistance
@authority policy
@owner assistance-programs
@severity hard
@category obligation
@priority 100
@english A borderline case is assigned to a case worker.
ROBL03: IF REVIEW_RECOMMENDED THEN OBLIGATION(assign_case_worker)

@domain utility_assistance
@ontology example_hardship_plans
@authority policy
@owner assistance-programs
@severity hard
@category obligation
@priority 100
@required_fact_mode explicit
@required_facts [HAS_DISCONNECTION_SCHEDULED]
@english A scheduled disconnection is paused while an application that is not declined is open.
@uses_predicates [hasDisconnectionScheduled]
@uses_classes [ServiceAccount]
ROBL04: IF HAS_DISCONNECTION_SCHEDULED == 1 AND NOT INELIGIBLE THEN OBLIGATION(pause_disconnection)
The full recorded run
ontology example_hardship_plans v1: 3 classes, 6 properties
rulebook hardship-plan@v1: 19 rules, bound to ontology example_hardship_plans: yes

== S1-qualifies: Three months behind, income well under the limit, documents verified.
facts: account_active=1, months_in_arrears=3, income_pct_of_limit=0.82, income_verified=1, prior_plan_default=0
outcome: APPROVE
report: fired; fired 2 of 19 rules
fired: RDAT01 RDEC04
derived facts: FILE_COMPLETE QUALIFIES
recommendations: RECOMMEND_APPROVE
obligations: none
deciding rule RDEC04 (policy, hard, priority 100): A complete application that meets every criterion and needs no review qualifies.
resolution: only_candidate: RDEC04 recommends APPROVE

== S2-over-income: Income 25 percent over the program limit.
facts: account_active=1, months_in_arrears=3, income_pct_of_limit=1.25, income_verified=1, prior_plan_default=0
outcome: DECLINE
report: fired; fired 5 of 19 rules
fired: RDAT01 RCR03 RCR07 RDEC01 ROBL01
derived facts: FILE_COMPLETE FAILS_INCOME INELIGIBLE
recommendations: RECOMMEND_DECLINE
obligations: OBLIGATION_SEND_ELIGIBILITY_NOTICE
deciding rule RDEC01 (policy, hard, priority 100): A household that fails any criterion is declined.
resolution: only_candidate: RDEC01 recommends DECLINE

== S3-borderline-income: Income 6 percent over the limit: inside the review band.
facts: account_active=1, months_in_arrears=3, income_pct_of_limit=1.06, income_verified=1, prior_plan_default=0
outcome: HOLD
report: fired; fired 4 of 19 rules
fired: RDAT01 RBL01 RDEC02 ROBL03
derived facts: FILE_COMPLETE BORDERLINE_INCOME REVIEW_RECOMMENDED
recommendations: RECOMMEND_HOLD
obligations: OBLIGATION_ASSIGN_CASE_WORKER
deciding rule RDEC02 (policy, hard, priority 80): Borderline income, with no failed criterion, is held for a case worker.
resolution: only_candidate: RDEC02 recommends HOLD

== S4-documents-pending: Qualifies on every criterion, but income is not yet verified.
facts: account_active=1, months_in_arrears=3, income_pct_of_limit=0.82, income_verified=0, prior_plan_default=0
outcome: HOLD
report: fired; fired 4 of 19 rules
fired: RDAT01 RBL02 RDEC03 ROBL02
derived facts: FILE_COMPLETE INCOME_UNVERIFIED DOCUMENTS_REQUESTED
recommendations: RECOMMEND_HOLD
obligations: OBLIGATION_REQUEST_INCOME_DOCUMENTS
deciding rule RDEC03 (policy, hard, priority 80): Unverified income, with no failed criterion, is held until the documents arrive.
resolution: only_candidate: RDEC03 recommends HOLD

== S5-recent-default: Defaulted on a payment plan in the last 12 months.
facts: account_active=1, months_in_arrears=3, income_pct_of_limit=0.82, income_verified=1, prior_plan_default=1
outcome: DECLINE
report: fired; fired 5 of 19 rules
fired: RDAT01 RCR04 RCR08 RDEC01 ROBL01
derived facts: FILE_COMPLETE FAILS_PLAN_HISTORY INELIGIBLE
recommendations: RECOMMEND_DECLINE
obligations: OBLIGATION_SEND_ELIGIBILITY_NOTICE
deciding rule RDEC01 (policy, hard, priority 100): A household that fails any criterion is declined.
resolution: only_candidate: RDEC01 recommends DECLINE

== S6-no-arrears: The account is up to date.
facts: account_active=1, months_in_arrears=0, income_pct_of_limit=0.82, income_verified=1, prior_plan_default=0
outcome: DECLINE
report: fired; fired 5 of 19 rules
fired: RDAT01 RCR02 RCR06 RDEC01 ROBL01
derived facts: FILE_COMPLETE FAILS_ARREARS INELIGIBLE
recommendations: RECOMMEND_DECLINE
obligations: OBLIGATION_SEND_ELIGIBILITY_NOTICE
deciding rule RDEC01 (policy, hard, priority 100): A household that fails any criterion is declined.
resolution: only_candidate: RDEC01 recommends DECLINE

== S7-disconnection-scheduled: Qualifies, and a disconnection is already scheduled.
facts: account_active=1, months_in_arrears=3, income_pct_of_limit=0.82, income_verified=1, prior_plan_default=0, disconnection_scheduled=1
outcome: APPROVE
report: fired; fired 3 of 19 rules
fired: RDAT01 RDEC04 ROBL04
derived facts: FILE_COMPLETE QUALIFIES
recommendations: RECOMMEND_APPROVE
obligations: OBLIGATION_PAUSE_DISCONNECTION
deciding rule RDEC04 (policy, hard, priority 100): A complete application that meets every criterion and needs no review qualifies.
resolution: only_candidate: RDEC04 recommends APPROVE

== S8-missing-income: Income against the limit has not been provided.
facts: account_active=1, months_in_arrears=3, income_verified=1, prior_plan_default=0
outcome: NEED_DATA
missing facts: income_pct_of_limit
rulebook not run: the decision type's required facts are missing

Adapt it to your policy#

  • Replace the criteria with yours, one rule each, so every decline names exactly one criterion.
  • Keep borderline bands as separate rules that hold rather than decline: they are where a person adds the most.
  • Give the decision type the facts every criterion reads as required facts, so a missing fact asks for data instead of deciding.