Decision Rules — Policy ↔ Engine Cross-Reference¶
Kaptl → TurnKey Lender configuration brief. Prepared 15 July 2026.
This document cross-references Kaptl's contractual credit policy (Decision
Tree / Approval Logic attached to the signed Order Form, 30 June 2026 — see the
Offer & Contract Summary) against the decision rules
actually evaluating on the Kaptl UAT tenant (kaptl-uat.turnkey-lender.com).
Evidence base. On 15 July 2026 all 19 loans present on UAT were read via
the Public API (GET /Management/Loans/{loanId}) and their
RiskScoreResults.MatchedDecisionRules unioned: the engine currently evaluates
26 distinct rules. Rule identifiers below are quoted exactly as the API
returns them (including the RegistraionDistance spelling).
Per the support terms, new or modified credit rules within existing scope carry 48-business-hour delivery to UAT — the request lists at the end of this page are scoped to that.
1. Policy ↔ engine mapping¶
Tier 1 — Fraud & lists checks (policy: reject on hit)¶
| # | Policy item | Engine rule(s) on UAT | Status |
|---|---|---|---|
| 1.1 | Multiple applications in a short period | — | Missing — no velocity rule observed |
| 1.2 | Fake ID / photo of ID / copy of ID / stolen identity | — | Handled in origination (ID + liveness), not the decision engine — confirm |
| 1.3 | Cross-application matching (email + phone) | MainPhoneUniqueness, EqualPhones, SsnUniqueness, DriverIdUniqueness |
Configured — email uniqueness not evidenced, confirm |
| 1.4 | Internal blacklist | BlackListCheck |
Configured |
| 1.5 | External blacklists | — | Missing — no external-list rule observed |
| 1.6 | Sanctioned persons | OpenSanctionsDatabaseNameDoB, OpenSanctionsDatabaseNationalId |
Configured |
Tier 2 — Hard rules (policy: fail → reject)¶
| # | Policy item | Engine rule(s) on UAT | Status |
|---|---|---|---|
| 2.1 | Age 20+ | SuspiciousAge |
Mismatch — the engine rule flags high age (a 90-year-old refers on UAT); the policy requires a minimum age of 20. Both bounds needed. |
| 2.2 | Bulgarian citizenship | Residence |
Configured — confirm it checks citizenship, not only residency |
| 2.3 | Valid ID (photo / copy) + liveness test | — | Origination flow, not the decision engine — confirm |
| 2.4 | Credit history: 0 current DPD; MaxDPD < 15 in last 12 months; no write-off / legal / other bad status per AnaCredit (ex-CCR) | PastDueActiveLoans, PastDueClosedLoans, DelinquencyAmountCheckActiveAndClosedLoans, DelinquencyAmountCheckPastDueLoans, ChargeOffCheck |
Partial — rules exist but thresholds and the AnaCredit/BNB data source must be confirmed (AnaCredit integration is in the contracted scope) |
| 2.5 | Up to 5 active loans (max 1 from an NBFI) | LoansNumber |
Partial — active-loan count exists; the NBFI sub-limit is not evidenced |
| 2.6 | At least 1 successfully closed loan (repeat borrowers) | — | Missing |
| 2.7 | Minimum €1,200 official net monthly income | NetIncome |
Threshold mismatch — UAT refers below €100; policy requires €1,200 |
| 2.8 | More than 6 months with current employer | — | Missing — expected via the NSSI Bulgaria check (contracted scope) |
| 2.9 | More than 12 months total employment in last 24 months | — | Missing — expected via NSSI |
| 2.10 | Permanent or long-term fixed employment contract | Unemployed |
Partial — Unemployed catches no employment; contract-type check not evidenced |
| 2.11 | Self-employed / civil contract → approval only after additional verification | — | Missing — needs a refer-not-reject branch on income type |
Tier 3 — Soft rules (policy: fail → review)¶
| # | Policy item | Engine rule(s) on UAT | Status |
|---|---|---|---|
| 3.1 | Debt-to-income < 50% | PTI |
Configured — confirm the threshold is 50% |
| 3.2 | Free cash flow ≥ €400 / month after existing obligations (per register) | — | Missing |
| 3.3 | Located outside Bulgaria → flagged | RegistraionDistance, IpAddressLookupRule |
Configured — both currently refer with "Address details are not provided in the application form", so they fire on missing data rather than geography (see §2, item D) |
| 3.4 | Data mismatch → flagged | — | Missing — no cross-field consistency rule observed |
Engine rules with no policy counterpart¶
Evaluating on UAT but not part of the signed decision tree — confirm whether they should stay active, and at what verdict:
CreditBureauScore (refers below 660) · CreditBureauInquiries (refers above
15 in 6 months) · BankruptcyCheck (refers above 0) · SuspiciousPhoneNumber
(currently refers because the suspicious-phones list is empty — see §2,
item C) · ResidedAtAddressOwner / ResidedAtAddressTenant.
2. Observed configuration issues on UAT¶
- A.
NetIncomethreshold. Live referral comment: "Net monthly income is less than 100". Policy minimum is €1,200. Please raise the threshold. - B.
SuspiciousAgedirection. Live comment: "Borrower's age is suspicious: 90" — a maximum-age check. The policy's hard rule is a minimum age of 20. Please add/confirm the lower bound (and confirm the intended upper bound, if any). - C.
SuspiciousPhoneNumberfires on empty list. Live comment: "The rule can not be checked correctly because Suspicious phones' list is empty." Every application refers until the list is seeded or the rule is set to pass on an empty list. - D. Geolocation rules fire on missing data.
RegistraionDistanceandIpAddressLookupRuleboth refer with "Address details are not provided in the application form" — as doesDriverIdUniquenessfor the licence field. Please confirm whether missing-data referrals are intended ahead of the application-form field mapping, or whether these should pass until the field exists. - E. Verdict severity. All rule failures observed on UAT produce Refer (manual review). The policy's Tier 1 and Tier 2 items are reject rules. Please align verdicts per tier: fraud/lists and hard rules → Reject, soft rules → Refer.
3. Requested changes (48-business-hour scope)¶
- Set
NetIncomethreshold to €1,200 (item A). - Add a minimum-age (20) bound; confirm
SuspiciousAgeupper bound (item B). - Seed or disable the suspicious-phones list so
SuspiciousPhoneNumberstops referring every application (item C). - Align verdicts per tier — hard/fraud rules Reject, soft rules Refer (item E).
- Confirm
PTIthreshold = 50% andLoansNumberlimit = 5 active. - Confirm whether the credit-bureau rules (
CreditBureauScore,CreditBureauInquiries,BankruptcyCheck) should remain active, and their place in the decision tree.
4. Requested confirmations / new scope¶
- Velocity rule for multiple applications in a short period (1.1).
- External blacklists rule and its data source (1.5).
- Email uniqueness in cross-application matching (1.3).
- AnaCredit/BNB-backed credit-history thresholds: 0 current DPD, MaxDPD < 15 in 12 months, bad-status exclusions (2.4) — contracted integration.
- NBFI sub-limit (max 1 of the 5 active loans) (2.5).
- Repeat-borrower rule — at least 1 successfully closed loan (2.6).
- Employment tenure rules via NSSI — > 6 months current employer,
12 of last 24 months total (2.8, 2.9) — contracted integration.
- Contract-type / self-employed branch — permanent contract passes, self-employed refers for additional verification (2.10, 2.11).
- Free-cash-flow rule — ≥ €400/month after obligations (3.2).
- Data-mismatch flag (3.4).
- Confirm ID + liveness and fake/stolen-ID detection are enforced in the origination flow, since they are not decision-engine rules (1.2, 2.3).
Method note: engine evidence is read-only Public API output from the UAT tenant; no tenant configuration was changed in producing this document. The same cross-reference is mirrored in the Kaptl LOS prototype under Settings → Decision rules.