{"openapi":"3.1.0","info":{"title":"MAAL AI Decisioning API","summary":"Credit, affordability, and fraud decisioning for Maal Wallet.","description":"MAAL AI is the internal decisioning service for BNPL credit scoring, affordability checks, fraud screening, admin-triggered rescoring, and event-driven rescoring.\n\nRecommended reading order:\n1. Open `/` for the human overview page.\n2. Open `/docs` for Swagger UI.\n3. Open `/openapi.json` for machine-readable integration contracts.\n\nDecision meanings:\n- `APPROVE`: financing can continue.\n- `MANUAL_REVIEW`: do not auto-approve; hold for review or a stricter UX state.\n- `REJECT`: financing should stop.\n- `ALLOW`, `ADVISORY`, `WARNING`, `CRITICAL`: fraud/risk severity levels.","contact":{"name":"Maal AI Engineering","url":"https://github.com/Maal-Wallet/maal-ai"},"license":{"name":"Proprietary / Internal Use"},"version":"0.1.0"},"paths":{"/health":{"get":{"tags":["health"],"summary":"Check service and model health","description":"Returns database readiness, config status, and whether the live credit model artifact is healthy enough for production scoring.","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponse"}}}}}}},"/v1/scoring/credit":{"post":{"tags":["scoring"],"summary":"Score BNPL credit eligibility","description":"Scores a user for BNPL-style financing eligibility. This is the main endpoint the product should use before continuing a financing flow. The response combines the trained credit model output with Maal policy thresholds and financing cap rules.","operationId":"score_credit_v1_scoring_credit_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreditScoringRequest"}}},"required":true},"responses":{"200":{"description":"Persisted credit decision with score, confidence, and financing cap.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreditScoringResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/scoring/affordability":{"post":{"tags":["affordability"],"summary":"Assess affordability","description":"Assesses whether the user's current liquidity and repayment load support a proposed financing obligation. This should complement, not override, credit or fraud policy.","operationId":"assess_affordability_v1_scoring_affordability_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AffordabilityRequest"}}},"required":true},"responses":{"200":{"description":"Persisted affordability assessment.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AffordabilityResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/scoring/fraud":{"post":{"tags":["fraud"],"summary":"Score transaction fraud risk","description":"Evaluates transaction-level fraud or operational risk. Use this before sensitive financial actions. `CRITICAL` should be treated as stronger than a credit approval.","operationId":"score_fraud_v1_scoring_fraud_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FraudScoringRequest"}}},"required":true},"responses":{"200":{"description":"Persisted fraud decision with severity and recommended action.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FraudScoringResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/rescore":{"post":{"tags":["admin"],"summary":"Force an admin-triggered credit rescore","description":"Triggers an auditable admin-driven rescore for one user. Use this when an operator needs a fresh decision after new information, a dispute, or a model release.","operationId":"rescore_user_v1_admin_rescore_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminRescoreRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminRescoreResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/risk/flags/{flag_id}/decide":{"post":{"tags":["admin"],"summary":"Resolve, accept, or override an open risk flag","description":"Records an auditable admin decision against a risk flag. The reason field is mandatory and persisted alongside the decided_at timestamp, the prior decision, and the admin actor identifier.","operationId":"decide_risk_flag_v1_admin_risk_flags__flag_id__decide_post","parameters":[{"name":"flag_id","in":"path","required":true,"schema":{"type":"string","title":"Flag Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RiskFlagDecisionRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RiskFlagDecisionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/events/repayment/missed":{"post":{"tags":["events"],"summary":"Ingest missed repayment event","description":"Queues an asynchronous credit rescore after a missed repayment signal. Internal backend systems should use this instead of directly mutating user score state.","operationId":"missed_repayment_v1_events_repayment_missed_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RepaymentEventRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/events/repayment/completed":{"post":{"tags":["events"],"summary":"Ingest repayment completed event","description":"Queues an asynchronous credit rescore after a repayment completion event so the user's latest credit state can recover or stabilize.","operationId":"completed_repayment_v1_events_repayment_completed_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RepaymentEventRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/events/jobs":{"post":{"tags":["events"],"summary":"Enqueue custom internal job","description":"Internal operational endpoint for enqueuing supported async jobs. This is mainly for admin or service-to-service automation rather than end-user flows.","operationId":"trigger_job_v1_events_jobs_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobTriggerRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JobResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/users/{user_id}/scores/latest":{"get":{"tags":["users"],"summary":"Get latest persisted user decisions","description":"Returns the newest credit, affordability, and fraud state currently stored for the user. Use this for admin tools, case review, or operational dashboards.","operationId":"latest_scores_v1_users__user_id__scores_latest_get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LatestScoreResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/users/{user_id}/decisions":{"get":{"tags":["users"],"summary":"Get recent decision history","description":"Returns a newest-first mixed timeline of persisted credit, affordability, and fraud decisions for one user.","operationId":"decision_history_v1_users__user_id__decisions_get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DecisionHistoryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/users/{user_id}/risk-flags/open":{"get":{"tags":["users"],"summary":"Get open user risk flags","description":"Returns currently open fraud or operational risk flags tied to the user.","operationId":"open_flags_v1_users__user_id__risk_flags_open_get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RiskFlagListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/transactions/{transaction_id}/risk-flags":{"get":{"tags":["users"],"summary":"Get transaction risk flags","description":"Returns risk flags associated with one transaction, typically for fraud review screens or support tooling.","operationId":"transaction_flags_v1_transactions__transaction_id__risk_flags_get","parameters":[{"name":"transaction_id","in":"path","required":true,"schema":{"type":"string","title":"Transaction Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RiskFlagListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"AdminRescoreRequest":{"properties":{"metadata":{"$ref":"#/components/schemas/RequestMetadata"},"user_id":{"type":"string","title":"User Id","description":"Maal user identifier to rescore."},"admin_actor_id":{"type":"string","minLength":2,"title":"Admin Actor Id","description":"Admin or operator ID performing the rescore.","examples":["admin-1"]},"reason":{"type":"string","minLength":3,"title":"Reason","description":"Human justification for the admin-triggered rescore.","examples":["post-deploy validation"]}},"type":"object","required":["metadata","user_id","admin_actor_id","reason"],"title":"AdminRescoreRequest"},"AdminRescoreResponse":{"properties":{"job":{"$ref":"#/components/schemas/JobResponse"},"result":{"$ref":"#/components/schemas/CreditScoringResponse"}},"type":"object","required":["job","result"],"title":"AdminRescoreResponse"},"AffordabilityRequest":{"properties":{"metadata":{"$ref":"#/components/schemas/RequestMetadata"},"user_id":{"type":"string","title":"User Id","description":"Maal user identifier being assessed.","examples":["user-123"]},"wallet_balance":{"type":"number","minimum":0.0,"title":"Wallet Balance","description":"Current wallet balance in ETB.","examples":[8200]},"monthly_repayment_obligation":{"type":"number","minimum":0.0,"title":"Monthly Repayment Obligation","description":"Current or proposed monthly repayment burden in ETB.","examples":[2400]},"monthly_income_estimate":{"type":"number","minimum":0.0,"title":"Monthly Income Estimate","description":"Estimated monthly income or inflow in ETB.","examples":[12000]},"feature_payload":{"additionalProperties":{"anyOf":[{"type":"number"},{"type":"integer"},{"type":"string"},{"type":"boolean"}]},"type":"object","title":"Feature Payload","description":"Optional extension fields for future affordability features."}},"type":"object","required":["metadata","user_id","wallet_balance","monthly_repayment_obligation","monthly_income_estimate"],"title":"AffordabilityRequest"},"AffordabilityResponse":{"properties":{"metadata":{"$ref":"#/components/schemas/ServiceMetadata"},"user_id":{"type":"string","title":"User Id"},"decision":{"type":"string","title":"Decision","description":"Affordability decision, typically APPROVE or MANUAL_REVIEW."},"affordability_pass":{"type":"boolean","title":"Affordability Pass","description":"Whether the affordability rule set considers the user affordable."},"confidence":{"type":"number","title":"Confidence"},"factors":{"items":{"type":"string"},"type":"array","title":"Factors"},"model_version":{"type":"string","title":"Model Version"}},"type":"object","required":["metadata","user_id","decision","affordability_pass","confidence","factors","model_version"],"title":"AffordabilityResponse"},"CreditScoringRequest":{"properties":{"metadata":{"$ref":"#/components/schemas/RequestMetadata"},"user_id":{"type":"string","title":"User Id","description":"Maal user identifier being scored.","examples":["user-123"]},"wallet_balance":{"type":"number","minimum":0.0,"title":"Wallet Balance","description":"Current wallet balance snapshot in ETB.","examples":[17500]},"outstanding_financing_count":{"type":"integer","minimum":0.0,"title":"Outstanding Financing Count","description":"Number of currently outstanding financing obligations.","examples":[1]},"has_overdue_repayment":{"type":"boolean","title":"Has Overdue Repayment","description":"Whether the user currently has an overdue repayment that should block approval.","default":false},"repayment_ratio":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Repayment Ratio","description":"Recent repayment quality ratio, where 1 means strong repayment behavior.","default":0.5,"examples":[0.92]},"transaction_consistency":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Transaction Consistency","description":"Behavioral consistency score used for credit feature fallback mapping.","default":0.5,"examples":[0.88]},"feature_payload":{"additionalProperties":{"anyOf":[{"type":"number"},{"type":"integer"},{"type":"string"},{"type":"boolean"}]},"type":"object","title":"Feature Payload","description":"Extended credit feature payload. Preferred keys include account_age_days, active_days_30d, txn_count_30d, cash flow totals, prior missed payments, KYC tier, income type, and region type."}},"type":"object","required":["metadata","user_id","wallet_balance","outstanding_financing_count"],"title":"CreditScoringRequest","example":{"feature_payload":{"account_age_days":700,"active_days_30d":23,"avg_balance_30d":15000,"cash_in_total_30d":46000,"cash_out_total_30d":23000,"existing_outstanding_amount":0,"income_band":"high","income_type":"salaried","kyc_tier":"tier_2","merchant_pay_total_30d":4100,"p2p_in_total_30d":6500,"p2p_out_total_30d":5400,"prior_delinquent_loans_12m":0,"prior_missed_payments_12m":0,"region_type":"ethiopia_urban","std_balance_30d":1700,"txn_count_30d":30},"has_overdue_repayment":false,"metadata":{"request_id":"req-credit-001","source":"backend"},"outstanding_financing_count":0,"repayment_ratio":0.95,"transaction_consistency":0.91,"user_id":"user-123","wallet_balance":17500}},"CreditScoringResponse":{"properties":{"metadata":{"$ref":"#/components/schemas/ServiceMetadata"},"user_id":{"type":"string","title":"User Id","description":"User identifier tied to the decision."},"decision":{"type":"string","title":"Decision","description":"Final financing decision: APPROVE, MANUAL_REVIEW, or REJECT."},"score_value":{"type":"integer","title":"Score Value","description":"0-100 score where higher means lower modeled delinquency risk."},"confidence":{"type":"number","title":"Confidence","description":"Model confidence proxy derived from bootstrap-CI uncertainty."},"risk_probability":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Risk Probability","description":"Calibrated mean delinquency probability used to derive the score."},"risk_probability_low":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Risk Probability Low","description":"5th-percentile bound from the bootstrap CI over calibrated sub-models."},"risk_probability_high":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Risk Probability High","description":"95th-percentile bound from the bootstrap CI over calibrated sub-models."},"limit_cap":{"type":"number","title":"Limit Cap","description":"Maximum financing cap in ETB permitted by policy for this decision."},"factors":{"items":{"type":"string"},"type":"array","title":"Factors","description":"Admin-facing explanation factors. Do not expose raw factors directly to end users."},"model_version":{"type":"string","title":"Model Version","description":"Active credit model version used for the decision."},"manual_review_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Manual Review Reason","description":"Human-readable reason when the decision falls to manual review."}},"type":"object","required":["metadata","user_id","decision","score_value","confidence","limit_cap","factors","model_version"],"title":"CreditScoringResponse","example":{"confidence":0.8798,"decision":"APPROVE","factors":["current_loan=0 contribution=-3.507","prior_missed_payments_12m=0.0 contribution=-2.418","p_delinquency=0.1202"],"limit_cap":8500.0,"metadata":{"created_at":"2026-04-27T06:48:45.561044+00:00","id":"02f918a6-123d-4146-aa67-48dafe302b05","request_id":"req-credit-001"},"model_version":"credit-score-real-logistic-regression-20260427200115","score_value":88,"user_id":"user-123"}},"DecisionHistoryResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/DecisionListItem"},"type":"array","title":"Items"}},"type":"object","title":"DecisionHistoryResponse"},"DecisionListItem":{"properties":{"id":{"type":"string","title":"Id"},"decision_type":{"type":"string","title":"Decision Type","description":"Decision domain such as CREDIT, AFFORDABILITY, or FRAUD."},"decision":{"type":"string","title":"Decision","description":"Final persisted decision value."},"created_at":{"type":"string","title":"Created At"},"source":{"type":"string","title":"Source"},"subject_id":{"type":"string","title":"Subject Id"}},"type":"object","required":["id","decision_type","decision","created_at","source","subject_id"],"title":"DecisionListItem"},"FraudScoringRequest":{"properties":{"metadata":{"$ref":"#/components/schemas/RequestMetadata"},"user_id":{"type":"string","title":"User Id","description":"Maal user identifier tied to the transaction."},"transaction_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Transaction Id","description":"Optional transaction identifier for transaction-level fraud checks."},"amount":{"type":"number","minimum":0.0,"title":"Amount","description":"Transaction amount in ETB being evaluated for fraud risk.","examples":[25000]},"velocity_score":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Velocity Score","description":"Precomputed transaction-velocity score where higher means riskier behavior.","default":0.1,"examples":[0.85]},"device_mismatch":{"type":"boolean","title":"Device Mismatch","description":"Whether the device used for the transaction mismatches the expected profile.","default":false},"geo_mismatch":{"type":"boolean","title":"Geo Mismatch","description":"Whether the transaction location mismatches the expected profile.","default":false},"feature_payload":{"additionalProperties":{"anyOf":[{"type":"number"},{"type":"integer"},{"type":"string"},{"type":"boolean"}]},"type":"object","title":"Feature Payload","description":"Optional extension fields for fraud rules and future features."}},"type":"object","required":["metadata","user_id","amount"],"title":"FraudScoringRequest"},"FraudScoringResponse":{"properties":{"metadata":{"$ref":"#/components/schemas/ServiceMetadata"},"user_id":{"type":"string","title":"User Id"},"transaction_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Transaction Id"},"decision":{"type":"string","title":"Decision","description":"Fraud decision such as ALLOW, ADVISORY, WARNING, or CRITICAL."},"severity":{"type":"string","title":"Severity","description":"Severity level used for UI and operational routing."},"risk_score":{"type":"number","title":"Risk Score","description":"0-1 fraud risk score where higher means riskier."},"factors":{"items":{"type":"string"},"type":"array","title":"Factors"},"model_version":{"type":"string","title":"Model Version"},"recommended_action":{"type":"string","title":"Recommended Action","description":"Operational action such as allow, manual_review, or block_and_flag."}},"type":"object","required":["metadata","user_id","decision","severity","risk_score","factors","model_version","recommended_action"],"title":"FraudScoringResponse"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"HealthResponse":{"properties":{"status":{"type":"string","title":"Status","description":"Overall service health, usually ok or degraded."},"service":{"type":"string","title":"Service"},"database":{"type":"string","title":"Database"},"config_loaded":{"type":"boolean","title":"Config Loaded"},"version":{"type":"string","title":"Version"},"credit_model_ready":{"type":"boolean","title":"Credit Model Ready","description":"Whether the active credit model is ready for production scoring.","default":false},"credit_model_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Credit Model Status","description":"Current model health status such as healthy, warning, or action_required."},"credit_model_version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Credit Model Version"},"credit_model_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Credit Model Reason","description":"Human-readable reason for the current model health state."}},"type":"object","required":["status","service","database","config_loaded","version"],"title":"HealthResponse"},"JobResponse":{"properties":{"id":{"type":"string","title":"Id","description":"Persisted job identifier."},"request_id":{"type":"string","title":"Request Id","description":"Caller-provided request identifier."},"job_type":{"type":"string","title":"Job Type","description":"Enqueued job type."},"status":{"type":"string","title":"Status","description":"Current job status such as PENDING or COMPLETED."},"attempts":{"type":"integer","title":"Attempts","description":"Current processing attempt count."},"created_at":{"type":"string","title":"Created At","description":"ISO-8601 UTC creation timestamp."},"completed_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Completed At","description":"ISO-8601 completion timestamp when the job is finished."}},"type":"object","required":["id","request_id","job_type","status","attempts","created_at"],"title":"JobResponse"},"JobTriggerRequest":{"properties":{"metadata":{"$ref":"#/components/schemas/RequestMetadata"},"job_type":{"type":"string","title":"Job Type","description":"Internal job type to enqueue, such as PERIODIC_RESCORE or BATCH_BACKFILL."},"payload":{"additionalProperties":{"anyOf":[{"type":"number"},{"type":"integer"},{"type":"string"},{"type":"boolean"}]},"type":"object","title":"Payload","description":"Opaque internal job payload consumed by the worker."}},"type":"object","required":["metadata","job_type"],"title":"JobTriggerRequest"},"LatestScoreResponse":{"properties":{"credit":{"anyOf":[{"$ref":"#/components/schemas/CreditScoringResponse"},{"type":"null"}]},"affordability":{"anyOf":[{"$ref":"#/components/schemas/AffordabilityResponse"},{"type":"null"}]},"latest_fraud":{"anyOf":[{"$ref":"#/components/schemas/FraudScoringResponse"},{"type":"null"}]}},"type":"object","title":"LatestScoreResponse"},"RepaymentEventRequest":{"properties":{"metadata":{"$ref":"#/components/schemas/RequestMetadata"},"user_id":{"type":"string","title":"User Id","description":"Maal user identifier impacted by the repayment event."},"wallet_balance":{"type":"number","minimum":0.0,"title":"Wallet Balance","description":"Wallet balance snapshot in ETB."},"outstanding_financing_count":{"type":"integer","minimum":0.0,"title":"Outstanding Financing Count","description":"Outstanding financing count at event time."},"repayment_ratio":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Repayment Ratio","description":"Repayment quality ratio at event time.","default":0.5},"transaction_consistency":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Transaction Consistency","description":"Behavioral consistency score at event time.","default":0.5},"feature_payload":{"additionalProperties":{"anyOf":[{"type":"number"},{"type":"integer"},{"type":"string"},{"type":"boolean"}]},"type":"object","title":"Feature Payload","description":"Optional richer credit features captured at event time."}},"type":"object","required":["metadata","user_id","wallet_balance","outstanding_financing_count"],"title":"RepaymentEventRequest"},"RequestMetadata":{"properties":{"request_id":{"type":"string","minLength":3,"title":"Request Id","description":"Stable caller-provided request identifier for tracing and audit logs.","examples":["req-credit-001"]},"source":{"type":"string","minLength":2,"title":"Source","description":"Calling system or channel, such as backend, admin, worker, or frontend.","examples":["backend"]},"actor_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Actor Id","description":"Optional actor identifier for admin or service-to-service audit trails.","examples":["admin-42"]}},"type":"object","required":["request_id","source"],"title":"RequestMetadata"},"RiskFlagDecisionRequest":{"properties":{"metadata":{"$ref":"#/components/schemas/RequestMetadata"},"decision":{"type":"string","enum":["ACCEPT","OVERRIDE","RESOLVE"],"title":"Decision","description":"The admin decision applied to the flag. ACCEPT confirms the AI signal and closes the flag, OVERRIDE rejects it (typically clears the user-side block), RESOLVE marks remediation completed without disputing the AI signal."},"reason":{"type":"string","minLength":3,"title":"Reason","description":"Human justification captured for governance and audit.","examples":["Customer disputed; bank statement reconciled."]},"admin_actor_id":{"type":"string","minLength":2,"title":"Admin Actor Id","description":"Identifier of the admin or operator performing the decision.","examples":["admin-1"]}},"type":"object","required":["metadata","decision","reason","admin_actor_id"],"title":"RiskFlagDecisionRequest"},"RiskFlagDecisionResponse":{"properties":{"metadata":{"$ref":"#/components/schemas/ServiceMetadata"},"flag":{"$ref":"#/components/schemas/RiskFlagResponse"},"prior_decision":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Prior Decision","description":"Status of the flag before the admin decision was applied."},"decided_at":{"type":"string","title":"Decided At","description":"ISO-8601 timestamp when the admin decision was recorded."},"overridden_by":{"type":"string","title":"Overridden By","description":"Admin actor id that decided the flag."},"override_reason":{"type":"string","title":"Override Reason","description":"Admin-supplied justification."}},"type":"object","required":["metadata","flag","decided_at","overridden_by","override_reason"],"title":"RiskFlagDecisionResponse"},"RiskFlagListResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/RiskFlagResponse"},"type":"array","title":"Items"}},"type":"object","title":"RiskFlagListResponse"},"RiskFlagResponse":{"properties":{"id":{"type":"string","title":"Id"},"user_id":{"type":"string","title":"User Id"},"transaction_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Transaction Id"},"event_type":{"type":"string","title":"Event Type"},"severity":{"type":"string","title":"Severity","description":"Flag severity such as WARNING or CRITICAL."},"status":{"type":"string","title":"Status","description":"Open or resolved flag status."},"recommended_action":{"type":"string","title":"Recommended Action"},"factors":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"additionalProperties":{"type":"string"},"type":"object"},{"additionalProperties":{"type":"number"},"type":"object"}],"title":"Factors"},"first_detected_at":{"type":"string","title":"First Detected At"},"last_detected_at":{"type":"string","title":"Last Detected At"}},"type":"object","required":["id","user_id","event_type","severity","status","recommended_action","factors","first_detected_at","last_detected_at"],"title":"RiskFlagResponse"},"ServiceMetadata":{"properties":{"id":{"type":"string","title":"Id","description":"Persisted record identifier."},"request_id":{"type":"string","title":"Request Id","description":"Caller-provided request identifier."},"created_at":{"type":"string","title":"Created At","description":"ISO-8601 UTC timestamp for the persisted record."}},"type":"object","required":["id","request_id","created_at"],"title":"ServiceMetadata"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}},"tags":[{"name":"scoring","description":"Primary credit decisioning endpoint used before financing flows continue."},{"name":"affordability","description":"Affordability assessment endpoints used to validate repayment burden."},{"name":"fraud","description":"Fraud and operational risk endpoints used before sensitive financial actions."},{"name":"health","description":"Operational readiness and model health checks."},{"name":"admin","description":"Internal admin-only operational actions such as forcing a user rescore."},{"name":"events","description":"Internal event ingestion endpoints that enqueue async rescoring and back-office jobs."},{"name":"users","description":"Read endpoints for latest persisted decisions, history, and open flags."}]}