Rules Reference

This page documents all individual rules available in the Underwriting Engine (located in pkg/rules). Each rule implements specific logic to evaluate a user’s eligibility for Floats or Installment Loans.

Rule Categories

Rules are organized by the dimension they evaluate:

Category Purpose Rule Count

Account Health

Verify account status and standing

3 rules

Transaction Activity

Analyze account usage patterns

6 rules

Payment History

Evaluate loan repayment behavior

4 rules

Income Stability

Verify regular income and deposits

3 rules

Risk Assessment

Identify fraud and suspicious behavior

4 rules

Balance & Limits

Verify available funds and limits

3 rules

Behavior Patterns

Analyze spending and transfer patterns

4 rules

Subscription & Loyalty

Verify subscription status and renewals

2 rules

External Scoring

Use ML models and external services

3 rules

Rules Summary Table

Rule Type Key Properties Min Float Rank

AgeOfAccount

Account

min_age (days)

-

AverageBalance

Balance

available_threshold

-

BalanceBetweenBounds

Balance

min_balance, max_balance, max_float_rank

0-N

BalanceRequirement

Balance

min_available, min_current

Optional

CashAdvanceScore

Scoring

min_cash_advance_score, loan_amount_window

Optional

CollectionsErrors

Payment

max_error_ratio

-

FloatAchProcessingStatus

Payment

None

-

CompetitorEwa

Activity

min_advances, min_repayments, number_of_days

Optional

EssentialSpend

Activity

required_float_rank, required_number_of_transactions

Y

EWADollarAmount

Activity

required_min_borrow_amount, required_min_repayment_amount

-

FloatRank

Account

min_float_rank, max_float_rank

-

GoodStanding

Account

None

-

HighTransfer

Behavior

max_transfer_ratio, min_income

-

InstitutionCheck

Risk

institution_list, min_balance

-

LowTransactions

Activity

average_transactions, float_rank

-

MLPaybackPrediction

Scoring

min_prediction_score, max_float_count

Optional

MLPaybackPredictionVariableThreshold

Scoring

low_float_threshold, high_float_threshold, min_float_count_for_high_threshold

-

MultipleAccounts

Risk

max_accounts

-

OnTimeFloatPayback

Payment

days_after_float_on_time, required_last_floats_on_time

Y

PayrollSpend

Behavior

N/A

-

RecentFloat

Payment

max_days

Y

RecurringDeposits

Income

min_income

-

RecurringDepositsAndHighTransfer

Income

min_income, transfer_ratio

-

SpendVelocity

Behavior

spend_percentage, min_income, days_after_income

-

ConsecutiveSubscriptions

Loyalty

min_consecutive_subscriptions

-

SubscriptionRank

Loyalty

min_rank, paid_within_days

-

SuspiciousHighBalance

Risk

high_account_balance, min_age_of_account

-

TransferRatio

Behavior

transfer_categories, max_transfer_percentage

-

ValidDebitCard

Account

None

-

Rule Definitions

Account Health

RuleAgeOfAccount

Establishes that the user’s account has been active for a minimum duration by checking the age of the oldest transaction.

  • Properties:

    • min_age (Integer): The minimum required age in days

  • Input Data: User transaction history

  • Pass Criteria: Oldest transaction date is older than min_age days ago

  • Fail Criteria: Account is younger than min_age days

  • Use Case: Prevent fraud from newly-created accounts

  • Common Values: min_age = 30, 60, 90 days

Example:

Configuration: min_age = 30 days
User account created: 45 days ago
Result: PASS (45 > 30)

RuleBalanceRequirement

Verifies the user maintains minimum funds in either Available or Current balance, optionally requiring a minimum float history.

  • Properties:

    • min_available (Integer/int64): Minimum required available balance (cents)

    • min_current (Integer/int64): Minimum required current balance (cents)

    • min_num_of_floats (Integer, Optional): Minimum completed floats required

  • Input Data: Account balances, float history

  • Pass Criteria:

    • (If min_num_of_floats set) User must have completed that many floats

    • AND (Available Balance >= min_available OR Current Balance >= min_current)

  • Fail Criteria: Both balances below thresholds OR float count insufficient

  • Use Case: Ensure user has capacity to repay

  • Common Values: min_available = $50-500, min_current = $100-1000

Example:

Configuration:
  min_available: 5000 (cents = $50)
  min_current: 10000 (cents = $100)

User State:
  available_balance: 15000 ($150)
  current_balance: 8000 ($80)

Result: PASS
  (available_balance $150 >= $50)

RuleGoodStanding

Fundamental account health check ensuring the user is an active customer without outstanding debts.

  • Properties: None

  • Input Data: User status, floats, loans, and the rule runner’s early-advance candidate

  • Pass Criteria:

    • User status is "ACTIVE" or "PROCESSING"

    • No outstanding (non-COMPLETED, non-HOLD) floats

    • No open loans

  • Fail Criteria: User inactive OR has an unpaid float/loan outstanding

  • Use Case: Gating rule - prevents approving inactive or over-indebted users

  • Criticality: Often configured as required gate

Early-advance exception

The rule runner identifies an early-advance candidate during data gathering: the user’s most recent float is ACHSENT, no other float is outstanding, and that float’s most recent collection attempt was a T-1 day-before-ACH run (TOMORROW_FIRST_FLOAT, or TOMORROW_RETURNING_FLOAT when the floats.collections.day_before.returning_float_ach GrowthBook flag is on). It then runs the full transaction-level evaluation (float_evaluation.EvaluateEarlyAdvance) over the transactions already fetched for the run — fetching that one collection payment by confirmation ID and applying the day-bucket checks — and only treats the user as a candidate when that evaluation clears them. So RuleGoodStanding ignores that one ACHSENT float only when the user is genuinely eligible; any other non-completed float still fails the rule.

On its passing branch, when it ignores the candidate float, the rule emits early_advance_float_id, early_advance_payment_id, and early_advance_day (the day bucket the evaluation cleared) features. The result runner lifts all three onto the stored evaluation.

The live early-advance check re-uses the recorded decision efficiently: it recomputes the current day bucket from the float’s due date and, while that bucket still matches the recorded early_advance_day, trusts the rule run’s decision (the float still being ACHSENT confirms the collection has not settled or returned) — no payment or transaction re-fetch. This is safe because any new transaction batch triggers a full rule re-run that re-evaluates and re-records (or clears) the decision. Only when the day bucket has advanced does the live check re-fetch the payment and transactions and re-run EvaluateEarlyAdvance, since the applicable day-bucket checks differ.

Example:

User State:
  status: "ACTIVE" ✓
  active_floats: [] (empty) ✓
  open_loans: [] (empty) ✓

Result: PASS (all conditions met)

RuleValidDebitCard

Verifies the user has a valid debit card on file for repayment processing.

  • Properties: None

  • Input Data: Debit card information from payment service

  • Pass Criteria: DebitCard.IsValid is true

  • Fail Criteria: No valid debit card

  • Use Case: Ensure repayment mechanism exists

  • Criticality: Gateway rule - cannot approve without payment method

Example:

User State:
  debit_card:
    card_ending_in: 1234
    is_valid: true
    expiration: 2026-12-31

Result: PASS

Transaction Activity

RuleLowTransactions

Ensures the user actively uses their account (minimum transaction velocity).

  • Properties:

    • days_to_consider (Integer): Lookback period in days

    • average_transactions (Float): Minimum average transactions per day

    • float_rank (Integer): Float count threshold for exemption

    • run_chime_varo_check (Boolean): Enable special logic for these institutions

  • Input Data: Transaction history, bank institution, float history

  • Pass Criteria:

    • Average txns/day >= average_transactions

    • OR User has > float_rank completed floats AND no active float

    • OR (If enabled) Chime/Varo special rules pass

  • Fail Criteria: Too few transactions AND no exemption triggered

  • Use Case: Identify dormant accounts (higher risk)

  • Common Values: average_transactions = 2.0, float_rank = 5

Example:

Configuration:
  days_to_consider: 30
  average_transactions: 1.5  (1.5 transactions/day)
  float_rank: 3

User State:
  transactions_last_30_days: 60
  average: 2.0 txns/day
  float_history: 2 floats

Result: PASS (2.0 >= 1.5)

RuleEssentialSpend

Rewards users who spend on essential categories (groceries, gas, utilities), indicating responsible budgeting.

  • Properties:

    • required_float_rank (Integer): Minimum floats to qualify

    • required_dollar_amount (Integer): Minimum transaction amount

    • required_number_of_transactions (Integer): Count threshold

    • essential_categories (List of Strings): Plaid category list

    • days_to_consider (Integer): Lookback period

  • Input Data: Categorized transactions (via Plaid)

  • Pass Criteria:

    • User has >= required_float_rank floats

    • AND >= required_number_of_transactions in essential categories

    • AND each >= required_dollar_amount

  • Fail Criteria: Thresholds not met

  • Use Case: Approve users with demonstrated financial responsibility

  • Common Values: required_float_rank = 1, required_number_of_transactions = 3

RuleCompetitorEwa

Analyzes whether user actively uses competitor Early Wage Access (EWA) services, indicating financial need but also experience with loans.

  • Properties:

    • number_of_days (Integer): Lookback window in days

    • min_advance_amount (Integer): Threshold to count as cash advance

    • min_inflows (Integer): Minimum competitor advances required

    • min_repayments (Integer): Minimum competitor repayments required

    • min_floats (Integer): Minimum FloatMe floats to apply rule

  • Input Data: Transaction history, merchant classification

  • Pass Criteria:

    • User has >= min_floats FloatMe floats (or N/A if not set)

    • AND >= min_inflows competitor advances

    • AND >= min_repayments competitor repayments

  • Fail Criteria: Counts insufficient

  • Use Case: Target users experienced with short-term lending

  • Common Values: min_inflows = 2, min_repayments = 2, min_floats = 0

Example:

Configuration:
  days_to_consider: 90
  min_advance_amount: 2500 ($25)
  min_inflows: 2
  min_repayments: 2

User Transactions (90 days):
  Competitor A: +$25 (inflow 1)
  Competitor A: +$20 (inflow 2)
  Competitor A: -$25 (repayment 1)
  Competitor B: -$20 (repayment 2)

Result: PASS (meets all counts)

RuleTransferRatio

Identifies users who primarily transfer money (potentially money laundering or fraudulent activity).

  • Properties:

    • days_to_consider (Integer): Lookback window

    • transfer_categories (List of Strings): Merchant categories counting as transfers

    • required_number_of_transactions (Integer): Minimum txns to evaluate (avoids false positives)

    • max_transfer_percentage (Float): Maximum allowed % transfers

  • Input Data: Categorized transactions

  • Pass Criteria:

    • (Transfer Txns / Total Txns) * 100 ⇐ max_transfer_percentage

    • OR Too few transactions to evaluate (below required_number_of_transactions)

  • Fail Criteria: Transfer percentage exceeds threshold

  • Use Case: Fraud prevention

  • Common Values: max_transfer_percentage = 30.0

Example:

Configuration:
  max_transfer_percentage: 30%
  required_number_of_transactions: 10

User Transactions (30 days): 20 total
  Transfer txns: 4
  Other txns: 16

Calculation: (4/20) * 100 = 20%

Result: PASS (20% <= 30%)

RuleEWADollarAmount

Similar to CompetitorEwa but evaluates total dollar amounts borrowed and repaid (vs. transaction counts).

  • Properties:

    • days_to_consider (Integer): Lookback window

    • required_min_borrow_amount (Integer): Minimum total borrowed

    • required_min_repayment_amount (Integer): Minimum total repaid

    • min_advance_amount (Integer): Threshold per transaction

  • Input Data: Transaction history, merchant classification

  • Pass Criteria: Total Borrowed >= threshold AND Total Repaid >= threshold

  • Fail Criteria: Amounts insufficient

  • Use Case: Identify active, substantial users of competitor services

  • Common Values: required_min_borrow_amount = 10000 ($100), required_min_repayment_amount = 10000

Payment History

RuleOnTimeFloatPayback

Validates that the user has a recent pattern of paying floats back on time.

  • Properties:

    • days_after_float_on_time (Integer): Grace period (definition of on-time)

    • required_last_floats_on_time (Integer): How many recent floats must be on time

    • required_float_rank (Integer): Minimum total floats required

  • Input Data: Float history, repayment dates

  • Pass Criteria:

    • User has >= required_float_rank completed floats

    • AND last required_last_floats_on_time consecutive floats paid within grace period

  • Fail Criteria: Float rank too low OR recent floats have late payments

  • Use Case: Reward reliable borrowers

  • Criticality: Often required for repeat users

  • Common Values: days_after_float_on_time = 3, required_last_floats_on_time = 3

RuleRecentFloat

Checks that the user actively borrows (paid back a float recently).

  • Properties:

    • max_days (Integer): Maximum days since last payback

  • Input Data: Float history, repayment dates

  • Pass Criteria: Most recent float payback is within max_days ago

  • Fail Criteria: Last float was paid back too long ago

  • Use Case: Identify dormant but otherwise qualified users

  • Common Values: max_days = 180 (6 months)

Early advance (no rulebook)

Early advance lets a qualified user take a new float against an in-processing (ACHSENT) float before its debit settles. It is not a rulebook. Two pieces cooperate:

  1. Rule runner + RuleGoodStanding (underwriting gate). The rule runner finds the user’s most recent float when it is ACHSENT with no other outstanding float, calls float-service for that float’s collection history, and — when the most recent collection attempt is a T-1 day-before-ACH run (TOMORROW_FIRST_FLOAT, or TOMORROW_RETURNING_FLOAT behind the floats.collections.day_before.returning_float_ach GrowthBook flag for rank 1+ floaters) — runs the full transaction-level evaluation (float_evaluation.EvaluateEarlyAdvance, over the transactions already fetched for the run) and records that float’s ID, the collection attempt’s payment confirmation ID, and the cleared day bucket as an early-advance candidate only when the evaluation clears the user. RuleGoodStanding then ignores that ACHSENT float so the user passes underwriting, and — only on its passing branch — emits early_advance_float_id, early_advance_payment_id, and early_advance_day. The result runner persists all three on the evaluation.

  2. float_evaluation.EvaluateEarlyAdvance (live check). At request time the float-check and eligibility endpoints read early_advance_float_id, early_advance_payment_id, and early_advance_day off the stored evaluation and resolve that float from the user’s floats. While the current day bucket still matches the recorded early_advance_day (and the float is still ACHSENT), the check trusts the rule run’s decision without re-fetching the payment or transactions — any new transaction batch would have triggered a full re-run that re-records or clears the decision. When the day bucket has advanced, the check re-fetches just that collection payment by confirmation ID and re-runs the day-bucket checks over the user’s transactions since the payment’s submit date. The result is surfaced to clients as early_advance_eligible on the float-check response and as float_stacking_eligible on the eligibility-check response (the latter name retained for app compatibility). When eligible, both responses also include early_advance_float_id and early_advance_payment_id — the same IDs the live check just verified — so the client knows which float and collection payment the advance applies to; they are omitted when the user is not currently eligible, even if the evaluation has stale IDs from a candidate that no longer applies. The live check requires:

    • the payment is still an ACHSENT debit (re-checked live, since it may have returned or completed since the evaluation ran);

    • the current day bucket — the number of business days (weekends and federal bank holidays skipped) between the float’s due date (debit_date) and now, where 0 = the due date — has checks defined and they all pass. Days 0 and 1 are defined and share the same checks: a floatme transaction whose amount equals the float amount + fee, no return/reversal transaction, and no floatme credit reversing that debit (the negative of the expected amount). A day with no entry denies.

Descriptors are normalized (lowercased, non-alphanumeric stripped) before matching and amounts are compared in cents, mirroring how payments-service clears ACH float debits against bank transactions. The stored early_advance_day never needs to expire: the live check recomputes the current bucket from the float’s due date and re-evaluates as soon as it diverges, so a stale recorded decision is self-correcting and there is no cleanup job. The user is also notified via an Iterable early_advance_eligible event — carrying an end_date (the business day the bucket rolls over). The rule runner computes the nudge (email, float, day, rollover, payment) when it clears the user and carries it on the rule-run-completed event; the result runner sends it only once it determines the user was approved by underwriting (float_results.approved), so the nudge fires only for users who both cleared early advance and passed all the rules — never someone who cleared early advance but was denied by another rule.

Decision metric

Every early-advance evaluation emits the underwriting.early_advance.decision Datadog counter so we can understand, on an institutional basis, how the checks clear or deny users. Tags:

  • approvalapprove or deny.

  • check — the check (or structural gate) that denied: a day-check name (floatme_present, no_returns_or_reversals, no_floatme_return) or a structural reason (payment_not_achsent, unparseable_due_date, no_checks_for_day, unparseable_submit_date); empty when approved.

  • business_days — the day bucket (business days since the float’s due date).

  • institution_name — the user’s bank display name, captured at rule-run time and persisted on the evaluation (early_advance_institution_name) so the live check tags it without an institution lookup.

  • processprocess_rule_runner (candidate identification) or process_api (live float-check re-evaluation). The live check’s day-bucket trust path is a cache hit and does not re-emit — the rule run already recorded that decision.

Example (day 0):

Collection debit submitted yesterday (T-1) → float due today; today's transactions:
  "FLOATME ADVANCE"  amount == collection debit amount   ← floatme debit posted
  (no return/reversal transactions)

Result: eligible for early advance

RuleCollectionsErrors

Analyzes payment failure rate (Usio debit collection errors) relative to float volume.

  • Properties:

    • max_error_ratio (Float): Maximum allowed ratio errors:floats

  • Input Data: Failed debit transactions, float count

  • Pass Criteria: (Error Count / Completed Floats) < max_error_ratio

  • Fail Criteria: Ratio >= threshold

  • Use Case: Prevent users with payment method issues

  • Common Values: max_error_ratio = 0.15 (max 15% failure rate)

Example:

User State:
  failed_debits: 2
  completed_floats: 20

Calculation: 2 / 20 = 0.10 (10%)

Configuration: max_error_ratio = 0.15 (15%)

Result: PASS (10% < 15%)

Income Stability

RuleRecurringDeposits

Verifies the user receives regular payroll or income deposits.

  • Properties:

    • min_income (Integer): Minimum amount to count as payroll

  • Input Data: Transaction history, income classification

  • Pass Criteria:

    • At least 2 payroll deposits in lookback period

    • AND at least one is "recent" (within typical pay schedule)

  • Fail Criteria: Insufficient income instances or no recent income

  • Use Case: Verify income stability

  • Common Values: min_income = 10000 ($100)

Example:

User Transaction History (90 days):
  Deposits >= $100:
    Day 15: $1000 (paycheck) ✓
    Day 30: $1000 (paycheck) ✓ recent
    Day 60: $500 (other income)

Result: PASS (2 payroll deposits, 1 recent)

RuleRecurringDepositsAndHighTransfer

Combines income stability check with transfer behavior check. User must have recent income AND not immediately transfer it away.

  • Properties:

    • min_income (Integer): Minimum income amount

    • transfer_ratio (Float): Max allowed transfer ratio

  • Input Data: Income deposits, transfer transactions

  • Pass Criteria:

    • (At least one recent Payroll Income AND No High Transfers found)

    • OR (At least one recent "Any Income" > min AND No High Transfers)

  • Fail Criteria: No recent income OR High Transfers detected

  • Use Case: Approve users with stable income and reasonable spending

  • Complexity: Combines two rule logics

RuleSpendVelocity

Identifies users who spend their paycheck too quickly after payday (risky spending pattern).

  • Properties:

    • spend_percentage (Float): Threshold % of income to spend

    • min_income (Integer): Minimum income to apply rule

    • days_after_income (Integer): Lookback window after payday

    • allowed_high_spend_instances (Integer): Max violations allowed

  • Input Data: Income deposits, spending transactions

  • Pass Criteria: Number of "high spend" instances ⇐ allowed_high_spend_instances

  • Fail Criteria: Too many instances where (Spend % Income) > threshold

  • Use Case: Prevent approving users with poor budget control

  • Common Values: spend_percentage = 0.8, allowed_high_spend_instances = 1

Example:

Configuration:
  spend_percentage: 0.8 (80%)
  days_after_income: 7
  allowed_high_spend_instances: 1

User Recent Paychecks:
  Paycheck 1: $1000
    Spending within 7 days: $950 (95% > 80%) ← violation 1

  Paycheck 2: $1000
    Spending within 7 days: $700 (70% < 80%) ← OK

violations = 1, allowed = 1

Result: PASS (violations == allowed)

Risk Assessment

RuleSuspiciousHighBalance

Flags new accounts with unusually high balances (potential fraud/money laundering).

  • Properties:

    • high_account_balance (Integer): Balance threshold (cents)

    • min_age_of_account (Integer): Age threshold (days)

  • Input Data: Current balance, account age, float history

  • Pass Criteria: User is NOT suspicious:

    • Account is old enough (>= min_age_of_account)

    • OR Balance is low enough (< high_account_balance)

    • OR User already has floats (grandfathered/trusted)

  • Fail Criteria: Account is young AND Balance is high AND No floats

  • Use Case: AML/fraud prevention

  • Common Values: high_account_balance = 500000 ($5000), min_age = 30 days

Example:

Configuration:
  high_account_balance: 500000 ($5000)
  min_age_of_account: 30 days

New User Account:
  age: 5 days (< 30) ❌
  balance: 1000000 ($10,000) (> $5000) ❌
  float_history: 0 floats ❌

Result: FAIL (suspicious)

RuleInstitutionCheck

Applies special handling for known problematic banking institutions (Chime, Varo, etc.) that allow overdrafts or have delayed posting.

  • Properties:

    • institution_list (List of Strings): List of Institution IDs/names

    • min_balance (Integer/int64): Stricter balance requirement

  • Input Data: Bank institution, account balances

  • Pass Criteria:

    • User is NOT in institution_list (Auto-pass for other banks)

    • OR User IS in list AND meets stricter balance check:

      • Available Balance >= min_balance while Current Balance >= 0

      • OR Current Balance >= min_balance while Available Balance >= 0

  • Fail Criteria: In problematic institution AND fails balance check

  • Use Case: Risk mitigation for unreliable bank data

  • Common Values: institution_list = ["chime", "varo"], min_balance = 50000 ($500)

RuleMultipleAccounts

Prevents fraud by detecting users with multiple linked accounts in the system.

  • Properties:

    • max_accounts (Integer): Maximum allowed linked accounts

  • Input Data: Linked accounts list (from user service)

  • Pass Criteria:

    • User already has completed floats (grandfathered/trusted)

    • OR unique linked accounts count ⇐ max_accounts

  • Fail Criteria: Too many linked accounts AND new user

  • Use Case: Fraud/abuse prevention

  • Common Values: max_accounts = 2

Example:

Configuration: max_accounts = 2

New User (no floats):
  Linked accounts: 5
  Result: FAIL (5 > 2, no grandfathering)

Existing User (3 previous floats):
  Linked accounts: 5
  Result: PASS (grandfathered despite high count)

RuleCashAdvanceScore

Uses an external Cash Advance Score (CAS) plus optional float rank constraints to evaluate creditworthiness.

  • Properties:

    • min_cash_advance_score (Integer): Minimum passing CAS

    • loan_amount_window (Integer): Loan amount to select correct score interval

    • min_float_rank (Integer, Optional): Minimum float count

    • max_float_rank (Integer, Optional): Maximum float count

    • deny_for_float_rank (Boolean): Enforce float rank range

  • Input Data: External CAS service, float history

  • Pass Criteria:

    • Float rank within acceptable bounds (or ignored if not enforced)

    • AND CAS Score >= min_cash_advance_score

  • Fail Criteria: Float rank violated (if enforced) OR CAS Score too low

  • Use Case: Incorporate external lending criteria

  • Complexity: References external scoring system

Behavior Patterns

RuleHighTransfer

Checks for excessive transfers immediately after payday (potential emergency need or financial distress).

  • Properties:

    • max_transfer_ratio (Float): Max allowed (Transfers / Income) ratio

    • min_income (Integer): Minimum income to apply

  • Input Data: Income deposits, transfer transactions

  • Pass Criteria: No instances where (Transfers on Payday + Next Day) > (Income * max_transfer_ratio)

  • Fail Criteria: One or more "high transfer" instances found

  • Use Case: Identify users in financial distress

  • Common Values: max_transfer_ratio = 0.5

Example:

Configuration: max_transfer_ratio = 0.5 (50%)

Payday: $2000 deposit
Transfers on Payday + Next Day: $1200

Check: $1200 > ($2000 * 0.5 = $1000) ✓ HIGH TRANSFER

Result: FAIL (found high transfer instance)

RulePayrollSpend

Analyzes spending behavior relative to payday patterns (variant of SpendVelocity).

  • Properties: (Varies, implementation-specific)

  • Input Data: Payroll deposits, spending transactions

  • Use Case: Assess budget control

  • Similar To: SpendVelocity but may have different thresholds

RuleBalanceBetweenBounds

Denies specific balance ranges for new users (prevents "testing" with small amounts).

  • Properties:

    • max_float_rank (Integer): Float count threshold, users below are subject to check

    • min_balance (Integer/int64): Lower bound of deny range

    • max_balance (Integer/int64): Upper bound of deny range

  • Input Data: Current available balance, float history

  • Pass Criteria:

    • User has >= max_float_rank floats (experienced users exempt)

    • OR Balance is outside deny range: ABS(AvailableBalance) < min_balance OR ABS(AvailableBalance) > max_balance

  • Fail Criteria: New user (< floats) AND balance falls in deny range

  • Use Case: Prevent "testing" accounts, fraud

  • Common Values: max_float_rank = 1, min_balance = 1000, max_balance = 5000

Example:

Configuration:
  max_float_rank: 1
  min_balance: 1000 ($10)
  max_balance: 5000 ($50)

New User (0 floats):
  available_balance: 2500 ($25)

Check: In deny range [1000, 5000]? YES ❌

Result: FAIL (new user with suspicious balance)

RuleAverageBalance

Verifies the user maintains sufficient average available balance over time (financial stability).

  • Properties:

    • available_threshold (Integer/int64): Minimum average available balance required (cents)

  • Input Data: Account balance history

  • Pass Criteria: Average Available Balance >= available_threshold

  • Fail Criteria: Average balance below threshold

  • Use Case: Ensure financial stability

  • Common Values: available_threshold = 50000 ($500)

Example:

Configuration: available_threshold = 50000 ($500)

User Balance History (30 days):
  Day 1: 60000
  Day 10: 45000
  Day 20: 55000
  Day 30: 50000

Average: (60000 + 45000 + 55000 + 50000) / 4 = 52500

Result: PASS (52500 >= 50000)

Subscription & Loyalty

RuleConsecutiveSubscriptions

Verifies that the user’s most recent subscription payments have been paid consecutively and successfully, with no gaps. Subscriptions with a future billing date are skipped and do not count toward or break the streak.

  • Properties:

    • min_consecutive_subscriptions (Integer): Number of consecutive paid subscriptions required

  • Input Data: Subscription payment history

  • Pass Criteria: The most recent min_consecutive_subscriptions non-future subscriptions all have COMPLETED status

  • Fail Criteria: A non-COMPLETED subscription is encountered before reaching the required count

  • Use Case: Ensure a user has a recent, unbroken streak of successful subscription payments before extending credit

  • Common Values: min_consecutive_subscriptions = 3

Notes:

  • Subscriptions are evaluated most-recent-first (sorted by subscription_date descending)

  • Subscriptions with a subscription_date in the future are skipped entirely — they neither contribute to nor break the streak

  • Subscriptions with an unparseable subscription_date are also skipped

Example:

Configuration: min_consecutive_subscriptions = 3

Subscription History (most recent first):
  2026-03-01: PENDING  ← future, skipped
  2026-02-01: COMPLETED  ← count 1
  2026-01-01: COMPLETED  ← count 2
  2025-12-01: COMPLETED  ← count 3 → PASS

Result: PASS
Configuration: min_consecutive_subscriptions = 3

Subscription History (most recent first):
  2026-02-01: COMPLETED  ← count 1
  2026-01-01: FAILED     ← streak broken at 1
  2025-12-01: COMPLETED  ← never reached

Result: FAIL (consecutive paid streak broken after 1 subscriptions, minimum required is 3)

RuleSubscriptionRank

Uses subscription history and renewals as a proxy for user stability and loyalty to FloatMe.

  • Properties:

    • min_rank (Integer): Minimum subscription renewals required

    • paid_within_days (Integer): Subscription must be paid within this many days

  • Input Data: Subscription payment history

  • Pass Criteria:

    • Current subscription rank >= min_rank

    • AND (if applicable) subscription payment occurred within paid_within_days

  • Fail Criteria: Rank too low or no recent payment

  • Use Case: Reward loyal subscribers

  • Common Values: min_rank = 2, paid_within_days = 365

External Scoring

RuleMLPaybackPrediction

Uses a Machine Learning model’s "Probability to Default" prediction score.

This is unrelated to Payback Predictor V2, which runs outside the rulebook after the rule engine has already decided, and can override that decision.
  • Properties:

    • min_prediction_score (Float): Maximum allowed probability to default (threshold)

    • max_float_count (Integer, Optional): Max float count to apply rule

    • deny_non_applicable (Boolean, Optional): Behavior if float count exceeded

  • Input Data: ML model inference from Sagemaker

  • Pass Criteria:

    • (If max_float_count set) Float count ⇐ threshold (or ignored if deny_non_applicable = false)

    • AND Probability to default ⇐ min_prediction_score

  • Fail Criteria: Probability to default > threshold OR float count exceeded with deny flag

  • Use Case: Leverage ML for credit decisions

  • Common Values: min_prediction_score = 0.3 (30% max default probability)

Note: The field is named min_prediction_score but actually represents the maximum allowed bad probability (confusing naming).

RuleMLPaybackPredictionVariableThreshold

Uses a Machine Learning model’s "Probability to Default" prediction score with variable thresholds based on the user’s completed float history.

  • Properties:

    • low_float_threshold (Float): Maximum allowed probability to default for users with fewer completed floats

    • high_float_threshold (Float): Maximum allowed probability to default for users with more completed floats

    • min_float_count_for_high_threshold (Integer): Minimum completed floats to use the higher (more lenient) threshold

  • Input Data: ML model inference from Sagemaker, user’s float history

  • Pass Criteria:

    • If completed floats < min_float_count_for_high_threshold: Probability to default ⇐ low_float_threshold

    • If completed floats >= min_float_count_for_high_threshold: Probability to default ⇐ high_float_threshold

  • Fail Criteria: Probability to default exceeds the applicable threshold

  • Use Case: Apply stricter ML thresholds to newer users while being more lenient with users who have a proven repayment history

  • Common Values: low_float_threshold = 0.8, high_float_threshold = 0.85, min_float_count_for_high_threshold = 3

Note: Only floats with COMPLETED debit status are counted when determining which threshold to apply.

Example:

Configuration: low_float_threshold = 0.8, high_float_threshold = 0.85, min_float_count_for_high_threshold = 3
User A: 2 completed floats, score = 0.82 → FAIL (0.82 > 0.8)
User B: 3 completed floats, score = 0.82 → PASS (0.82 <= 0.85)

Rule Execution Order

Within a rulebook, rules are typically executed in order defined in the YAML configuration. There’s no automatic re-ordering based on rule type or dependency. If rule B depends on data from rule A, they should be ordered accordingly in the rulebook definition.

Error Handling

  • If a rule errors (exception): Error is recorded at rule outcome level

  • The rulebook continues to next rule

  • Error is surfaced to rulebook level

  • Rulebook results in ERROR status

  • Final evaluation may return EVALERR status

Custom Properties

Each rule accepts a properties object in YAML that defines its behavior. Properties are rule-specific and type-safe (validated during rulebook loading).