Jev Case Study: Loan Application Risk Tiering (Full Code)
TL;DR: This Jev case study shows a risk-tiering pipeline for a small lending platform: a choice question sorts each application into
low,medium, orhighrisk, and a judgment question decides whether the application needs a human reviewer. A confidence threshold routes the flow — auto-decide at 0.8 and above, human review in the middle band, and a full manual pass below 0.5. Full request bodies, example-fixture responses, and a runnable Python script are below.
Scenario
We work with a small consumer-lending platform that processes about 9,000 applications a month. Their existing rules engine only knows hard cutoffs: FICO below 620 goes to decline, FICO above 720 and debt-to-income under 36% goes to auto-approve, everything in between lands on a human reviewer’s desk. That works fine for the numbers — and misses everything the numbers don’t say.
The gap showed up in post-mortems. Applications where the employment description contradicts itself (“five years at a company founded in 2023”), where a stated salary doesn’t match the stated industry, or where the reason for the loan shifts between fields — those sailed through if the credit score happened to clear the bar. The rules engine can’t weigh soft signals, and human reviewers can’t scale to 9,000 applications.
So we added two Jev calls per application, sitting between the rules engine and the reviewer queue:
- A choice question — assign the application to exactly one risk tier:
low,medium, orhigh. - A judgment question — does this application need manual review, regardless of tier? Answer
yes,no, orunclear.
Jev is a System One judgment model from TypeSafe AI, released in September 2026. It fits this slot because the output is typed JSON with a confidence value, not free-form prose — which is what a routing gate needs. The example application below is deliberately mixed: a decent FICO (678) that would normally trigger a middle-tier review, plus two soft contradictions the rules engine never sees.
Request
Call 1 — risk tier (choice question):
{
"model": "typesafe/jev-1.13",
"messages": [
{
"role": "system",
"content": "Choice question. Assign this loan application to exactly one risk tier: low, medium, or high. Consider stated income vs. industry norms, internal consistency of employment history, and coherence of the stated loan purpose. Reply with JSON only: {\"answer\": <low|medium|high>, \"confidence\": <0-1>, \"rationale\": <one sentence>}"
},
{
"role": "user",
"content": "Loan application #A-48213: FICO 678. Stated monthly income: $9,200, occupation: warehouse associate. Employment: \"shift lead at a regional logistics company for 5 years.\" Requested amount: $12,000 over 36 months. Purpose field 1: \"consolidate credit cards.\" Purpose field 2 (free text): \"help pay for my nursing school tuition starting this fall.\" DTI: 31%."
}
]
}
Call 2 — manual-review check (judgment question):
{
"model": "typesafe/jev-1.13",
"messages": [
{
"role": "system",
"content": "Judgment question. Answer yes, no, or unclear. Reply with JSON only: {\"answer\": <yes|no|unclear>, \"confidence\": <0-1>, \"rationale\": <one sentence>}"
},
{
"role": "user",
"content": "Loan application #A-48213: FICO 678, DTI 31%. Claims $9,200/month as a warehouse associate and 5 years at a logistics company. Loan purpose is stated as credit card consolidation in one field and nursing school tuition in another. Does this application need manual review?"
}
]
}
Response
Both responses below are example fixtures — illustrative output, not a live capture.
Call 1:
{
"answer": "medium",
"confidence": 0.71,
"rationale": "Credit metrics are acceptable, but the stated income is high for the occupation, and the loan purpose is inconsistent between fields."
}
Call 2:
{
"answer": "yes",
"confidence": 0.88,
"rationale": "The mismatched loan purposes and income-to-occupation gap are exactly the soft signals the rules engine cannot evaluate."
}
Reproduce
Save as risk_tier.py, fill in your own BASE_URL and API key, and run python3 risk_tier.py. It uses the OpenAI-compatible SDK, so any OpenAI-compatible endpoint works:
import json, os
from openai import OpenAI
BASE_URL = "https://YOUR-ENDPOINT.example.com/v1" # replace with your provider
client = OpenAI(base_url=BASE_URL, api_key=os.environ["JEV_API_KEY"])
MODEL = "typesafe/jev-1.13" # confirm exact slug with your provider
def ask(system, user):
res = client.chat.completions.create(
model=MODEL,
messages=[{"role": "system", "content": system},
{"role": "user", "content": user}])
return json.loads(res.choices[0].message.content)
CHOICE = ('Choice question. Assign this loan application to exactly one risk '
'tier: low, medium, or high. Consider stated income vs. industry '
'norms, internal consistency of employment history, and coherence '
'of the stated loan purpose. Reply with JSON only: '
'{"answer": <low|medium|high>, "confidence": <0-1>, '
'"rationale": <one sentence>}')
JUDGE = ('Judgment question. Answer yes, no, or unclear. Reply with JSON '
'only: {"answer": <yes|no|unclear>, "confidence": <0-1>, '
'"rationale": <one sentence>}')
app = ("Loan application #A-48213: FICO 678. Stated monthly income: $9,200, "
"occupation: warehouse associate. Employment: 'shift lead at a "
"regional logistics company for 5 years.' Requested amount: $12,000 "
"over 36 months. Purpose field 1: 'consolidate credit cards.' "
"Purpose field 2 (free text): 'help pay for my nursing school "
"tuition starting this fall.' DTI: 31%.")
tier = ask(CHOICE, app)
review = ask(JUDGE, "Loan application:\n" + app + "\n\nDoes this application need manual review?")
def route(tier, review):
conf = tier["confidence"]
if conf >= 0.8:
return "auto:" + tier["answer"]
if conf >= 0.5:
return "human-review"
return "human-review-priority"
print(tier)
print(review)
print(route(tier, review))
Confidence routing
The tier alone is not the decision — confidence is. Our routing rule:
tier confidence >= 0.8 ----> auto-apply tier
tier confidence 0.5-0.8 --> human review queue
tier confidence < 0.5 ----> priority manual review
review answer = yes ----> force human review (overrides tier)
review answer = unclear --> treat as yes
The middle band is where most applications actually land, and that is fine — the point of the gate is not to automate everything, it is to make sure only well-understood cases skip a human.
Key parameters
| Field | Value | Why it matters |
|---|---|---|
model | typesafe/jev-1.13 | Pin the version so a newer release cannot silently change risk decisions. Confirm the exact slug with your provider. |
messages[0] (system) | Question type + option list + JSON shape + the soft signals to weigh | Naming the soft signals (income vs. industry, consistency, purpose coherence) keeps scoring stable across applications. |
messages[1] (user) | Application fields, verbatim | Send the raw field values, not a summary — the contradictions live in the details. |
answer (response) | medium / yes (example fixture) | The tier maps to the routing table; the review flag overrides it. |
confidence (response) | 0.71 / 0.88 (example fixture) | The main routing input: ≥0.8 auto, 0.5–0.8 review, <0.5 priority review. |
rationale (response) | One sentence | Goes straight into the reviewer’s case notes. |
Notes
- The responses above are example fixtures; verify field names against official docs before relying on them. Official API details live at typesafe.ai — start with the System One announcement post.
- The raw HTTP response is an OpenAI-compatible chat completion; the JSON shown here is the model’s typed output parsed from the message content.
- This pipeline supplements the rules engine, it does not replace it. Hard cutoffs (fraud flags, legal restrictions) still fire first; Jev handles the soft-signal band the rules cannot reach.
- Log every
rationaleand confidence value. When a reviewer overrides the model, that log is how you tune the thresholds later. - Applies to Jev 1.13 (released September 2026) via the OpenRouter demonstration channel.