Skip to main content

Use Case: Credit Approval

This use case demonstrates evaluating a credit decision directly via the API, with no process orchestration involved. The decision model is versioned independently, and every evaluation is persisted with a verifiable trail of which rule matched and why.

Scenario​

When an application needs to approve or deny credit, it:

  1. Sends the applicant's credit score and monthly income to a decision model
  2. Receives an immediate approval outcome and credit limit
  3. Can later prove, for any past evaluation, exactly which rule fired and against which inputs

No BPMN process is required — the decision model is a standalone API surface.

Step 1: Create the Decision Model​

Define a decision that maps credit score and income to an approval outcome:

curl -X POST https://api.stateway.io/v1/decisions \
-H "X-API-Key: sw_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"key": "credit-approval-rules",
"name": "Credit Approval Rules",
"source_type": "json",
"source": {
"key": "credit-approval-rules",
"name": "Credit Approval Rules",
"hitPolicy": "FIRST",
"inputs": [
{ "name": "credit_score", "type": "number" },
{ "name": "monthly_income", "type": "number" }
],
"outputs": [
{ "name": "approved", "type": "boolean", "defaultValue": false },
{ "name": "credit_limit", "type": "number", "defaultValue": 0 }
],
"rules": [
{
"id": "high-score",
"conditions": {
"credit_score": { "operator": ">=", "value": 750 }
},
"outputs": { "approved": true, "credit_limit": 50000 }
},
{
"id": "mid-score-income",
"conditions": {
"credit_score": { "operator": ">=", "value": 600 },
"monthly_income": { "operator": ">=", "value": 5000 }
},
"outputs": { "approved": true, "credit_limit": 15000 }
},
{
"id": "denied",
"conditions": {
"credit_score": { "operator": "<", "value": 600 }
},
"outputs": { "approved": false, "credit_limit": 0 }
}
]
}
}'
{
"data": {
"id": "dmd_01j...",
"key": "credit-approval-rules",
"version": 1,
"is_active": true
}
}

Step 2: Evaluate the Decision​

Call the decision directly — no instance, no process, no state to manage:

curl -X POST https://api.stateway.io/v1/decisions/credit-approval-rules/evaluate \
-H "X-API-Key: sw_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"variables": {
"credit_score": 680,
"monthly_income": 6200
}
}'
{
"data": {
"evaluation_id": "eval_01j...",
"decision_key": "credit-approval-rules",
"decision_version": 1,
"decision_hash": "a3f1c2d4e5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2",
"matched": true,
"rules_matched": ["mid-score-income"],
"outputs": {
"approved": true,
"credit_limit": 15000
},
"evaluated_at": "2026-05-19T12:00:00Z"
}
}

rules_matched shows exactly which row fired — here, mid-score-income, because the score (680) fell below the high-score threshold (750) but the income requirement was met.

Step 3: Retrieve Evaluation History​

Every evaluation is persisted. Query the full history for the decision, or filter by a specific evaluation:

curl "https://api.stateway.io/v1/decisions/credit-approval-rules/evaluations" \
-H "X-API-Key: sw_live_your_key"
{
"data": [
{
"id": "eval_01j...",
"decision_key": "credit-approval-rules",
"decision_version": 1,
"decision_hash": "a3f1c2d4e5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2",
"rules_matched": ["mid-score-income"],
"outputs": { "approved": true, "credit_limit": 15000 },
"evaluated_at": "2026-05-19T12:00:00Z"
}
]
}

Step 4: Change the Rule Without Redeploying Anything​

Publish a new version of the decision — for example, raising the income threshold:

curl -X PUT https://api.stateway.io/v1/decisions/credit-approval-rules \
-H "X-API-Key: sw_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"source_type": "json",
"source": {
"key": "credit-approval-rules",
"name": "Credit Approval Rules",
"hitPolicy": "FIRST",
"inputs": [
{ "name": "credit_score", "type": "number" },
{ "name": "monthly_income", "type": "number" }
],
"outputs": [
{ "name": "approved", "type": "boolean", "defaultValue": false },
{ "name": "credit_limit", "type": "number", "defaultValue": 0 }
],
"rules": [
{ "id": "high-score", "conditions": { "credit_score": { "operator": ">=", "value": 750 } }, "outputs": { "approved": true, "credit_limit": 50000 } },
{ "id": "mid-score-income","conditions": { "credit_score": { "operator": ">=", "value": 600 }, "monthly_income": { "operator": ">=", "value": 7000 } }, "outputs": { "approved": true, "credit_limit": 15000 } },
{ "id": "denied", "conditions": { "credit_score": { "operator": "<", "value": 600 } }, "outputs": { "approved": false, "credit_limit": 0 } }
]
}
}'
{
"data": {
"id": "dmd_02j...",
"key": "credit-approval-rules",
"version": 2,
"is_active": true
}
}

The evaluation from Step 2 keeps its original decision_version: 1 and decision_hash — history is never rewritten. Every evaluation made from now on runs against version 2.

Key Takeaways​

  • Decisions are a standalone API surface — POST /v1/decisions/:key/evaluate needs no process instance, no BPMN diagram, no state.
  • decision_hash proves which exact source produced a given outcome; combine with Hash Verification to compare it against a file you hold.
  • rules_matched shows which row of the table fired — no guessing which condition applied.
  • Publishing a new version never rewrites history — past evaluations keep the version and hash they ran against.
  • Hit policies (FIRST used here; ALL, UNIQUE, COLLECT also available) control what happens when more than one rule matches — see Decision Models.