Credit Office API Workflow Skill
Overview
Tasks require querying a remote credit-office public API and producing a committee-ready JSON answer matching a strict template. The API base URL is provided in environment_access.md. Do not run local env/setup.sh.
API Endpoints (always use remote public API)
GET /api/manifest– lists all available endpoints and benchmark versionsGET /api/branches– all branchesGET /api/branches/{branch_id}– branch details (capacity, limits, state)GET /api/branches/{branch_id}/metrics– quarterly metrics (use Q1 2025)GET /api/branches/{branch_id}/loans– loan portfolioGET /api/branches/{branch_id}/sector-exposures– sector concentrationsGET /api/branches/{branch_id}/applications– pending applicationsGET /api/policies– rating thresholds, CDFI score tables, stress formulasGET /api/benchmarks/fdic/q4-2024– FDIC benchmarksGET /api/benchmarks/ncua/q1-2025– NCUA benchmarks (rows by state_code)GET /api/credit-union-segments/{segment_id}– segment data
Common Business Rules
Risk Rating Re-derivation (from policies)
- DSCR: ≥1.5→3, ≥1.25→4, ≥1.05→5, ≥1.0→6, <1.0→7
- LTV: ≤0.65→3, ≤0.75→4, ≤0.85→5, ≤1.0→6, >1.0→7
- Delinquency: Current→null, 30 DPD→4, 60 DPD→5, 90+ DPD→7, Nonaccrual→8
- Final rating = worst of available DSCR, LTV, and delinquency factors.
- If a factor is missing (None), skip it.
CDFI Factor Score (from policies)
- debt_to_asset: <0.40→0, 0.40-0.60→2, 0.60-0.80→4, >0.80→6
- fico: >720→0, 680-720→1, 580-679→3, <580→5
- liquidity_months: >12→0, 6-12→1, 3-6→3, <3→5
- ltv: <0.40→0, 0.40-0.60→2, 0.60-0.80→4, >0.80→6
- Sum all available factor scores.
- Risk class: ≤5 Prime, ≤9 Desirable, ≤13 Satisfactory, ≤18 Watch, ≥19 & ltv>1.0 Projected Loss, ≥19 Doubtful.
Stress Formulas (from policies)
- Watch-list +200bp:
stressed_dscr = dscr / (1 + 0.18) - CRE dual-stress:
stressed_dscr = dscr * 0.85 / (1 + 0.18) - Breach threshold = 1.0
Material Downgrade
- Downgrade notches = final_rating − current_rating
- Material = ≥2 notches (policy:
material_downgrade_notches: 2)
NPA Benchmark
branch_npa_exposure=nonperforming_loansfrom branch metrics (Q1 2025)branch_total_loans=total_loans_outstandingfrom branch metricsbranch_npa_ratio= branch_npa_exposure / branch_total_loans (4 decimals)- Use
total_loans_noncurrent_pctas the default FDIC benchmark metric unless the task specifically calls for real-estate or construction.
Output Conventions
Ordering
- Lists of loans/applications: ascending by
loan_idorapplication_id - Action buckets: ascending alphabetically by action name
- Sectors: ascending alphabetically
- Watch-list workout queue: descending by exposure, then ascending loan_id
- Severe bucket counts: ascending by current_rating, then by payment_status order (Current, 30 DPD, 60 DPD, 90+ DPD, Nonaccrual)
Precision
- Currency: round to 2 decimals
- Percentages/ratios: round to 4 decimals
- Basis points: round to 2 decimals
- DSCR: round to 2 decimals
Enum Values
- Always use exact enum strings from the template. Never invent values.
- Payment status:
Current,30 Days Past Due,60 Days Past Due,90+ Days Past Due,Nonaccrual - Actions:
monitor,watchlist,special_assets,workout,partial_chargeoff_review,legal_referral - Decisions:
approve,conditional_approve,decline,defer,participation_required - Conditions:
participation_required,reduced_amount,board_exception,sba_guaranty_required,startup_monitoring,none
Task-Specific Patterns
Rating Migration Review (e.g., train_001)
- Fetch branch loans, filter by
current_rating >= target_min - Re-derive final ratings using DSCR/LTV/delinquency rules
- Build
final_rating_exposure_totalsandmigration_from_current_rating_3 watch_list_action_coveragemaps final ratings to actions (3→monitor, 4→watchlist, 5→special_assets, 6→workout, 7→partial_chargeoff_review, 8→legal_referral)top_problem_credit= worst final rating, tie-break by highest exposure
Allocation Package (e.g., train_002)
- Fetch applications, sector exposures, branch capacity
- Compute post-approval sector % = (current_exposure + approved_amount) / total_loans_outstanding
- Decision logic:
- Clean + within limits →
approve - Minor issue (weak DSCR, high LTV) →
conditional_approvewith relevant condition - Sector breach →
conditional_approvewithparticipation_requiredorreduced_amount - Multiple severe issues →
decline
- Clean + within limits →
bank_capacity_used= approved_amount for approve/conditional; for participation_required use 50% of approved amountpriority_ranking= all approved/conditional apps sorted by amount descendingdecline_reasons= sorted list of reason codes per declined apppost_approval_concentrations= every sector with exposure_after_approval
Credit-Union Posture (e.g., train_003)
- Fetch segment and NCUA benchmarks
- State metrics = exact integer values from NCUA row for the segment's state
- Peer comparison: compute median of peer_states for each metric; direction = higher/lower/equal vs US and vs peer median
required_checklist_gates= segment'sminimum_checklist(sorted)added_operating_controls= choose controls that address segment's noted issues (e.g., insurance binder verification for insurance gaps)- Posture: if delinquency above peers but capacity available →
continue_with_tighter_conditions interpretationfields must be consistent with posture and metrics
Watch-List Stress (e.g., train_004)
- Fetch loans with
current_rating >= adverse_min - Compute CDFI factor scores and risk classes
monitoring_cadence: if any Watch/Doubtful/Projected Loss →monthly, elsequarterly- Stress all loans with DSCR available;
breaches_thresholdif stressed_dscr < 1.0 - Workout queue: map risk class to action;
projected_loss= true for Nonaccrual or 90+ DPD
Competing CRE Decision (e.g., train_005)
- Fetch both applications, branch loans, metrics, FDIC benchmark
- Compute weighted CDFI score using policy weights (capacity 0.45, capital 0.03, character 0.05, collateral_exposure 0.36, conditions 0.11)
- Score class: ≤2.0 approve_quality, ≤3.0 conditional, >3.0 weak
- CRE dual-stress both applications; select the one that does NOT breach
- Unselected gets
declinewith reason codes sorted alphabetically - Concentration: existing_cre_exposure = sum of CRE loans; post-approval = existing + selected amount
selected_policy_variance_bps= (post_conc − cre_policy_limit_pct) × 10000fdic_benchmark_metric=total_real_estate_30_89_pct
Common Pitfalls
- Do not use localhost or env/setup.sh; always use the remote API URL from
environment_access.md - Round precisely: currency to 2 decimals, ratios to 4 decimals, bps to 2 decimals
- Sort correctly: ascending loan_id, alphabetical action, descending exposure for workout queue
- Skip missing factors: if DSCR or LTV is None, do not include it in rating derivation
- Use Q1 2025 metrics: metrics endpoint returns two quarters; pick the one with quarter="2025Q1"
- Enum exactness: any value not in the template's allowed enum will fail validation
- Do not include narrative outside the JSON object
- Total loans denominator: for concentrations, use
total_loans_outstandingfrom metrics, not total_assets