{
  "info": {
    "_postman_id": "bre-credit-engine-v1",
    "name": "Credit Engine API (v1)",
    "description": "Deterministic Credit Engine + BRE. Set the `base_url` and `api_key` collection variables, then run any request. Every /v1 call authenticates with `Authorization: Bearer {{api_key}}`. Get a key from POST /api/signup or the console.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "auth": {
    "type": "bearer",
    "bearer": [
      {
        "key": "token",
        "value": "{{api_key}}",
        "type": "string"
      }
    ]
  },
  "variable": [
    {
      "key": "base_url",
      "value": "http://localhost:3000",
      "type": "string"
    },
    {
      "key": "api_key",
      "value": "bre_live_replace_me",
      "type": "string"
    },
    {
      "key": "decision_id",
      "value": "",
      "type": "string"
    },
    {
      "key": "rule_set_id",
      "value": "",
      "type": "string"
    },
    {
      "key": "alert_id",
      "value": "",
      "type": "string"
    }
  ],
  "item": [
    {
      "name": "Onboarding",
      "item": [
        {
          "name": "Sign up (public)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/api/signup",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "api",
                "signup"
              ]
            },
            "description": "Create a tenant + admin user + first API key. The api_key is returned once — copy it into the collection variable.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"you@lender.com\",\n  \"org_name\": \"Acme Lending\",\n  \"password\": \"change-me-8+\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "auth": {
              "type": "noauth"
            }
          },
          "response": []
        },
        {
          "name": "Health",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/health",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "health"
              ]
            },
            "description": "Liveness/readiness. Add ?db=1 for a DB readiness probe."
          },
          "response": []
        },
        {
          "name": "Usage & quota",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/usage",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "usage"
              ]
            },
            "description": "Current-period metered decisions vs plan quota."
          },
          "response": []
        }
      ]
    },
    {
      "name": "Decisions",
      "item": [
        {
          "name": "Evaluate (bureau report → priced offer)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/evaluate",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "evaluate"
              ]
            },
            "description": "Credit Engine: raw bureau report → normalize → score → grade → priced offer. Meters one decision.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"bureau\": \"crif\",\n  \"report\": {\n    \"crif\": {\n      \"reportId\": \"DEV-EVAL-1\",\n      \"pan\": \"ABCDE1234F\",\n      \"scoreData\": {\n        \"score\": 770\n      },\n      \"demographics\": {\n        \"age\": 34,\n        \"employmentType\": \"salaried\"\n      },\n      \"accountSummary\": {\n        \"oldestAccountMonths\": 60,\n        \"enquiriesLast90\": 1,\n        \"worstDpd\": 0,\n        \"delinquencies\": {\n          \"dpd30\": 0,\n          \"dpd60\": 0,\n          \"dpd90\": 0\n        },\n        \"accounts\": {\n          \"total\": 6,\n          \"secured\": 3,\n          \"unsecured\": 3,\n          \"active\": 1\n        },\n        \"utilizationPercent\": 22\n      },\n      \"incomeAssessment\": {\n        \"monthlyIncome\": 60000,\n        \"foir\": 28\n      },\n      \"requestedAmount\": 30000\n    }\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Evaluate + intelligence blocks (F1/F2/F4 + B5)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/evaluate",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "evaluate"
              ]
            },
            "description": "Same endpoint, with every OPTIONAL intelligence block attached. All four are additive: omit any of them and the decision is byte-for-byte what it would have been without it.\n\n• \"features\": true  — F1 feature factory. Generates ~3,500 named features (metric × window × product × math fn) and persists the vector against the decision. Only the features your ACTIVE POLICY names are merged into the decision input; the rest are analytics. Read them back at GET /v1/decisions/{id}/features.\n• \"device\"   — F2. Derives device_is_emulator / device_is_rooted / device_sim_new / device_loan_apps_excess. Emulator HARD-BLOCKS by default (KO_DEVICE_EMULATOR); tune in /v1/fraud/config.\n• \"location\" — F4. Derives location_gps_pincode_match / location_ip_country / location_pincode_risk / location_permission_denied. Pincode risk is tenant-editable at /v1/fraud/pincode-risk.\n• \"fraud\"    — B5 ring clustering (pincode spike, IP cluster, beneficiary reuse, agent/geo anomaly).\n\nDevice and location signals add points to fraud_score. Every derived field is referencable from a condition_ast knockout; a field that was not supplied reads as absent, which is default-deny.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"bureau\": \"crif\",\n  \"report\": {\n    \"crif\": {\n      \"reportId\": \"DEV-EVAL-1\",\n      \"pan\": \"ABCDE1234F\",\n      \"scoreData\": {\n        \"score\": 770\n      },\n      \"demographics\": {\n        \"age\": 34,\n        \"employmentType\": \"salaried\"\n      },\n      \"accountSummary\": {\n        \"oldestAccountMonths\": 60,\n        \"enquiriesLast90\": 1,\n        \"worstDpd\": 0,\n        \"delinquencies\": {\n          \"dpd30\": 0,\n          \"dpd60\": 0,\n          \"dpd90\": 0\n        },\n        \"accounts\": {\n          \"total\": 6,\n          \"secured\": 3,\n          \"unsecured\": 3,\n          \"active\": 1\n        },\n        \"utilizationPercent\": 22\n      },\n      \"incomeAssessment\": {\n        \"monthlyIncome\": 60000,\n        \"foir\": 28\n      },\n      \"requestedAmount\": 30000\n    }\n  },\n  \"bank_summary\": {\n    \"monthly_income\": 60000,\n    \"existing_emi\": 18000,\n    \"avg_balance\": 42000,\n    \"bounces_3m\": 0,\n    \"declared_monthly_income\": 72000,\n    \"declared_emi\": 9000,\n    \"transactions\": [\n      {\n        \"days_ago\": 3,\n        \"kind\": \"salary\",\n        \"amount\": 60000,\n        \"balance\": 78000\n      },\n      {\n        \"days_ago\": 5,\n        \"kind\": \"emi\",\n        \"amount\": 9000,\n        \"balance\": 69000\n      },\n      {\n        \"days_ago\": 12,\n        \"kind\": \"spend\",\n        \"amount\": 4200,\n        \"balance\": 64800\n      },\n      {\n        \"days_ago\": 34,\n        \"kind\": \"salary\",\n        \"amount\": 60000,\n        \"balance\": 91000\n      },\n      {\n        \"days_ago\": 41,\n        \"kind\": \"bounce\",\n        \"amount\": 9000,\n        \"balance\": 2100\n      }\n    ]\n  },\n  \"features\": true,\n  \"device\": {\n    \"model\": \"Pixel 7\",\n    \"os\": \"android\",\n    \"os_version\": \"14.0\",\n    \"is_rooted\": false,\n    \"is_emulator\": false,\n    \"sim_age_days\": 820,\n    \"loan_apps_installed_count\": 2,\n    \"app_install_age_days\": 140\n  },\n  \"location\": {\n    \"lat\": 12.977,\n    \"lng\": 77.6,\n    \"ip\": \"203.0.113.9\",\n    \"declared_pincode\": \"560001\",\n    \"permission_denied\": false\n  },\n  \"fraud\": {\n    \"applicant_ref\": \"BORROWER-9f2c\",\n    \"pincode\": \"560001\",\n    \"ip\": \"203.0.113.9\",\n    \"beneficiary_account\": \"50100123456789\",\n    \"agent_id\": \"AG-17\"\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Decision feature vector (F1)",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/decisions/{{decision_id}}/features",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "decisions",
                "{{decision_id}}",
                "features"
              ]
            },
            "description": "The full generated vector, its hash, the ranked contributors, and which features the policy read. 404 unless the decision was evaluated with \"features\": true."
          },
          "response": []
        },
        {
          "name": "Decision (flat/structured input)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/decisions",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "decisions"
              ]
            },
            "description": "Core deterministic decision from applicant/bureau/bank fields. Supports the Idempotency-Key header.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"applicant\": {\n    \"age\": 34,\n    \"employment_type\": \"salaried\",\n    \"requested_amount\": 25000\n  },\n  \"bureau\": {\n    \"score\": 765,\n    \"max_dpd_ever\": 0\n  },\n  \"monthly_income\": 55000,\n  \"foir_pct\": 30\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Preview (what-if, no persist)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/decisions/preview",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "decisions",
                "preview"
              ]
            },
            "body": {
              "mode": "raw",
              "raw": "{\n  \"bureau\": {\n    \"score\": 745\n  },\n  \"monthly_income\": 36000,\n  \"foir_pct\": 39,\n  \"age\": 30\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Get decision + re-explain",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/decisions/{{decision_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "decisions",
                "{{decision_id}}"
              ]
            },
            "description": "Fetch a stored decision and its deterministic re-explanation."
          },
          "response": []
        }
      ]
    },
    {
      "name": "Rule sets & scorecards",
      "item": [
        {
          "name": "List rule sets",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/rule-sets",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "rule-sets"
              ]
            }
          },
          "response": []
        },
        {
          "name": "Create rule set (draft)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/rule-sets",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "rule-sets"
              ]
            },
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"My policy\",\n  \"grades\": [],\n  \"reject_rules\": []\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Activate rule set",
          "request": {
            "method": "POST",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/rule-sets/{{rule_set_id}}/activate",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "rule-sets",
                "{{rule_set_id}}",
                "activate"
              ]
            }
          },
          "response": []
        },
        {
          "name": "List scorecards",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/scorecards",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "scorecards"
              ]
            }
          },
          "response": []
        },
        {
          "name": "Score against active scorecard",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/scorecards",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "scorecards"
              ]
            },
            "body": {
              "mode": "raw",
              "raw": "{\n  \"score_input\": {\n    \"bureau_score\": 760,\n    \"foir_pct\": 30,\n    \"monthly_income\": 55000,\n    \"age\": 35\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Backtest",
      "item": [
        {
          "name": "Backtest a policy (CSV/rows)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/backtest",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "backtest"
              ]
            },
            "body": {
              "mode": "raw",
              "raw": "{\n  \"rows\": [\n    {\n      \"bureau_score\": 760,\n      \"foir_pct\": 30,\n      \"monthly_income\": 55000,\n      \"age\": 35\n    }\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Credit Engine backtest (synthetic book)",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/credit/backtest",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "credit",
                "backtest"
              ]
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Fraud & intelligence",
      "item": [
        {
          "name": "Fraud config (thresholds)",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/fraud/config",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "fraud",
                "config"
              ]
            },
            "description": "Effective thresholds for this tenant: B5 ring signals, F2 device rules, F4 location rules, plus the shipped defaults and your override."
          },
          "response": []
        },
        {
          "name": "Update fraud config",
          "request": {
            "method": "PUT",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/fraud/config",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "fraud",
                "config"
              ]
            },
            "description": "Partial override, deep-merged onto the defaults. Everything is per-tenant.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"signals\": {\n    \"PINCODE_SPIKE\": {\n      \"max_apps_per_window\": 4\n    },\n    \"IP_CLUSTER\": {\n      \"max_distinct_applicants\": 2\n    }\n  },\n  \"device\": {\n    \"rules\": {\n      \"emulator\": {\n        \"hard_block\": true\n      },\n      \"loan_apps\": {\n        \"max_installed\": 4\n      }\n    }\n  },\n  \"location\": {\n    \"rules\": {\n      \"risky_pincode\": {\n        \"min_risk\": 65\n      }\n    }\n  },\n  \"score_knockout\": {\n    \"enabled\": false,\n    \"threshold\": 80\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Ring alerts",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/fraud/alerts?status=open",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "fraud",
                "alerts"
              ],
              "query": [
                {
                  "key": "status",
                  "value": "open"
                }
              ]
            },
            "description": "Fraud alerts, newest first. Alert content is immutable; only triage fields move."
          },
          "response": []
        },
        {
          "name": "Triage an alert",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/fraud/alerts/{{alert_id}}",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "fraud",
                "alerts",
                "{{alert_id}}"
              ]
            },
            "body": {
              "mode": "raw",
              "raw": "{\n  \"status\": \"acknowledged\",\n  \"note\": \"analyst reviewing\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Pincode risk (F4)",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/v1/fraud/pincode-risk",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "fraud",
                "pincode-risk"
              ]
            },
            "description": "This tenant's pincode risk overrides. Anything not overridden falls back to the shipped seed."
          },
          "response": []
        },
        {
          "name": "Set pincode risk",
          "request": {
            "method": "PUT",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/fraud/pincode-risk",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "fraud",
                "pincode-risk"
              ]
            },
            "body": {
              "mode": "raw",
              "raw": "{\n  \"entries\": [\n    {\n      \"pincode\": \"854105\",\n      \"risk\": 90,\n      \"note\": \"known mule corridor\"\n    }\n  ]\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        }
      ]
    },
    {
      "name": "Agents",
      "item": [
        {
          "name": "Underwriting agent",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/agents/underwrite",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "agents",
                "underwrite"
              ]
            },
            "description": "Agent A orchestrates the engine (kill-switch respected). Never decides credit itself.",
            "body": {
              "mode": "raw",
              "raw": "{\n  \"applicant\": {\n    \"age\": 34,\n    \"employment_type\": \"salaried\",\n    \"requested_amount\": 25000\n  },\n  \"bureau\": {\n    \"score\": 765,\n    \"max_dpd_ever\": 0\n  },\n  \"monthly_income\": 55000,\n  \"foir_pct\": 30\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        },
        {
          "name": "Collections agent",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "url": {
              "raw": "{{base_url}}/v1/agents/collections",
              "host": [
                "{{base_url}}"
              ],
              "path": [
                "v1",
                "agents",
                "collections"
              ]
            },
            "body": {
              "mode": "raw",
              "raw": "{\n  \"loan_id\": \"L-1002\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            }
          },
          "response": []
        }
      ]
    }
  ]
}