Try it
Copy the code ↓- Data source
- Decision schema
- Model inference
- Structured result
2 Decision schema 3 typed questions
payment_actionchoicepayverify_vendorhuman_reviewrejectfraud_riskscorelowmediumhighneeds_humannoulprobability 0–1
3 Model inference typesafe/jev-1.13
Waiting for a run.
4 Structured result
Raw JSON
Run the demo to see the JSON answers, the highlighted pick and the final action.
FLATKEY_API_KEY and run.curl -sS https://router.flatkey.ai/api/alpha/decisions \
-H "Authorization: Bearer $FLATKEY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "typesafe/jev-1.13",
"state": "Invoice INV-20931 from Northwind Supplies — $48,600.00, due in 3 days\nPO-7781 match: amount OK, qty OK, goods receipt posted\nDuplicate check: no exact match; INV-2093 for $48,600.00 paid 41 days ago\nVendor master: bank account changed 2 days ago via emailed request (new bank, new country)\nEmail note: \"Please pay to the new account urgently, our old bank is closing.\"\nPolicy: bank-detail change in last 30 days requires call-back verification; possible duplicates go to AP review.\nTask: typed payment decision — pay / verify_vendor / human_review / reject (+ fraud_risk, needs_human).",
"questions": {
"payment_action": {
"type": "choice",
"instructions": "Decide what accounts payable should do with this invoice before any payment is released. Use only the invoice, matching results, payment history and vendor master facts provided.",
"criteria": {
"pay": "Clean match to PO/receipt, no duplicate signal, vendor bank details unchanged; safe to include in the payment run",
"verify_vendor": "Payment looks legitimate but vendor bank details or remit-to changed recently; confirm by call-back to a known contact before paying",
"human_review": "Possible duplicate, PO/amount mismatch, or policy exception (no PO, over threshold); an AP analyst must review",
"reject": "Clear duplicate of a paid invoice, unknown vendor, or strong fraud signals; do not pay"
}
},
"fraud_risk": {
"type": "score",
"instructions": "How likely is this invoice or payment request to be fraudulent or a duplicate, based on bank-detail changes, duplicate signals, urgency pressure and matching gaps.",
"criteria": [
"low",
"medium",
"high"
]
},
"needs_human": {
"type": "noul",
"instructions": "A human in AP or finance must approve before this payment is released."
}
}
}'// Node.js 18+ (built-in fetch). Run: FLATKEY_API_KEY=... node jev.mjs
const body = {
"model": "typesafe/jev-1.13",
"state": "Invoice INV-20931 from Northwind Supplies — $48,600.00, due in 3 days\nPO-7781 match: amount OK, qty OK, goods receipt posted\nDuplicate check: no exact match; INV-2093 for $48,600.00 paid 41 days ago\nVendor master: bank account changed 2 days ago via emailed request (new bank, new country)\nEmail note: \"Please pay to the new account urgently, our old bank is closing.\"\nPolicy: bank-detail change in last 30 days requires call-back verification; possible duplicates go to AP review.\nTask: typed payment decision — pay / verify_vendor / human_review / reject (+ fraud_risk, needs_human).",
"questions": {
"payment_action": {
"type": "choice",
"instructions": "Decide what accounts payable should do with this invoice before any payment is released. Use only the invoice, matching results, payment history and vendor master facts provided.",
"criteria": {
"pay": "Clean match to PO/receipt, no duplicate signal, vendor bank details unchanged; safe to include in the payment run",
"verify_vendor": "Payment looks legitimate but vendor bank details or remit-to changed recently; confirm by call-back to a known contact before paying",
"human_review": "Possible duplicate, PO/amount mismatch, or policy exception (no PO, over threshold); an AP analyst must review",
"reject": "Clear duplicate of a paid invoice, unknown vendor, or strong fraud signals; do not pay"
}
},
"fraud_risk": {
"type": "score",
"instructions": "How likely is this invoice or payment request to be fraudulent or a duplicate, based on bank-detail changes, duplicate signals, urgency pressure and matching gaps.",
"criteria": [
"low",
"medium",
"high"
]
},
"needs_human": {
"type": "noul",
"instructions": "A human in AP or finance must approve before this payment is released."
}
}
};
const res = await fetch("https://router.flatkey.ai/api/alpha/decisions", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.FLATKEY_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
if (!res.ok) throw new Error(`Jev request failed: ${res.status} ${await res.text()}`);
const { answers, usage } = await res.json();
console.log(JSON.stringify(answers, null, 2), usage);# Python 3.8+ (pip install requests). Run: FLATKEY_API_KEY=... python jev.py
import json, os, requests
body = json.loads(r"""{
"model": "typesafe/jev-1.13",
"state": "Invoice INV-20931 from Northwind Supplies — $48,600.00, due in 3 days\nPO-7781 match: amount OK, qty OK, goods receipt posted\nDuplicate check: no exact match; INV-2093 for $48,600.00 paid 41 days ago\nVendor master: bank account changed 2 days ago via emailed request (new bank, new country)\nEmail note: \"Please pay to the new account urgently, our old bank is closing.\"\nPolicy: bank-detail change in last 30 days requires call-back verification; possible duplicates go to AP review.\nTask: typed payment decision — pay / verify_vendor / human_review / reject (+ fraud_risk, needs_human).",
"questions": {
"payment_action": {
"type": "choice",
"instructions": "Decide what accounts payable should do with this invoice before any payment is released. Use only the invoice, matching results, payment history and vendor master facts provided.",
"criteria": {
"pay": "Clean match to PO/receipt, no duplicate signal, vendor bank details unchanged; safe to include in the payment run",
"verify_vendor": "Payment looks legitimate but vendor bank details or remit-to changed recently; confirm by call-back to a known contact before paying",
"human_review": "Possible duplicate, PO/amount mismatch, or policy exception (no PO, over threshold); an AP analyst must review",
"reject": "Clear duplicate of a paid invoice, unknown vendor, or strong fraud signals; do not pay"
}
},
"fraud_risk": {
"type": "score",
"instructions": "How likely is this invoice or payment request to be fraudulent or a duplicate, based on bank-detail changes, duplicate signals, urgency pressure and matching gaps.",
"criteria": [
"low",
"medium",
"high"
]
},
"needs_human": {
"type": "noul",
"instructions": "A human in AP or finance must approve before this payment is released."
}
}
}""")
res = requests.post(
"https://router.flatkey.ai/api/alpha/decisions",
headers={"Authorization": f"Bearer {os.environ['FLATKEY_API_KEY']}"},
json=body,
timeout=60,
)
res.raise_for_status()
data = res.json()
print(json.dumps(data["answers"], indent=2), data.get("usage"))
No Flatkey key yet? Get one for Jev 1.13 →
About this use case

Who / when
Audience
- finance and AP teams automating invoice approval without letting a chatbot move money
- controllers and CFOs who want confidence thresholds that route uncertain payments to humans
- builders wiring ERP, OCR or email intake into a payment run with a typed fraud gate
Triggers
- an invoice looks like a duplicate of one already paid (same vendor, amount or number variant)
- payment-to-invoice or PO matching is off on amount, quantity or terms
- a vendor's bank details changed recently, especially via an email request
- a policy exception (no PO, over threshold, new vendor) needs a typed route instead of prose
The workflow
- 01Invoice + contextPO, history, vendor masterInput
- 02Score fraud risklow / medium / highJev
- 03Payment decisionpay / verify / review / rejectJev
- 04AP gatePay, call back, hold, or rejectYour code
Question shape
{
"payment_action": {
"type": "choice",
"instructions": "Decide what accounts payable should do with this invoice before any payment is released. Use only the invoice, matching results, payment history and vendor master facts provided.",
"criteria": {
"pay": "Clean match to PO/receipt, no duplicate signal, vendor bank details unchanged; safe to include in the payment run",
"verify_vendor": "Payment looks legitimate but vendor bank details or remit-to changed recently; confirm by call-back to a known contact before paying",
"human_review": "Possible duplicate, PO/amount mismatch, or policy exception (no PO, over threshold); an AP analyst must review",
"reject": "Clear duplicate of a paid invoice, unknown vendor, or strong fraud signals; do not pay"
}
},
"fraud_risk": {
"type": "score",
"instructions": "How likely is this invoice or payment request to be fraudulent or a duplicate, based on bank-detail changes, duplicate signals, urgency pressure and matching gaps.",
"criteria": [
"low",
"medium",
"high"
]
},
"needs_human": {
"type": "noul",
"instructions": "A human in AP or finance must approve before this payment is released."
}
}Gate policy
≥ 0.90
Auto-pay only when payment_action is pay, fraud_risk is low and needs_human is false. Auto-reject a high-confidence duplicate and log it.
0.65–0.90
Hold the payment: verify_vendor triggers a call-back to a known contact; anything else goes to the AP review queue. No auto-pay.
< 0.65
Block release and escalate to a finance approver, especially when fraud_risk is high or bank details changed.
Failure modes
Pre-launch checks before you trust auto-routing:
- Asking a chatbot "should we pay this?" and parsing a paragraph inside the payment run;
- Auto-paying because the PO match is clean while the vendor bank account changed by email two days ago;
- Missing near-duplicates (INV-2093 vs INV-20931) because only exact invoice numbers are compared;
- Letting urgency language ("pay today", "old bank is closing") lower the bar instead of raising risk;
- Using one global threshold instead of holding mid-confidence payments for review;
FAQ
Why use a typed decision model for AP instead of an LLM chat?
Payments are high-stakes actions. A typed Choice (pay / verify_vendor / human_review / reject) with calibrated probabilities lets your AP code auto-pay only clean invoices and route everything uncertain to a person, instead of trusting free-form prose that can sound confident while being wrong.
How should the confidence bands map to AP actions?
Auto-pay only on high-confidence pay with low fraud_risk and needs_human false. Mid confidence holds the payment: call back the vendor on a known number for bank changes, otherwise queue for AP review. Low confidence or high fraud_risk blocks release and escalates to a finance approver.
Does Jev replace three-way matching or duplicate checks?
No. Keep exact PO / receipt / invoice matching and exact duplicate keys in code or your ERP. Jev adds judgment on the fuzzy cases those rules miss: near-duplicate invoice numbers, suspicious bank-detail change requests, urgency pressure and policy exceptions.
Can I bring my own API key?
Yes. Try the free demo above without signing in. To run it live on your own text, sign in with Google and connect a Flatkey or OpenRouter key (a quick test call runs when you save it). Manage or delete it anytime under Dashboard → BYOK. Keys are stored encrypted on the server, used only for runs you trigger, and never sent back to the browser. Need a Flatkey key? Get one for Jev 1.13 on Flatkey.
Is this the official TypeSafe Jev product page?
No. jev-llm.com is an independent research and practice site. Verify pricing, models and terms at the original TypeSafe sources.
Sources
duplicate invoice/policy-exception detection, payment-to-invoice matching, vendor bank-detail fraud flags… confidence thresholds routing uncertain cases to humans.
— @roh1 View the post on X →
Wire this gate with your own key
Bring your own key through Flatkey / OpenRouter. Keep exact checks in code.