{"components":{"schemas":{"Address":{"properties":{"address_line1":{"description":"","type":"string"},"address_line2":{"description":"","type":"string"},"address_type":{"description":"","type":"string"},"address_uuid":{"description":"","type":"string"},"bad":{"description":"","type":"boolean"},"city":{"description":"","type":"string"},"country":{"description":"","type":"string"},"do_not_mail":{"description":"","type":"boolean"},"dt_created":{"format":"date-time","type":"string"},"end_date":{"format":"date-time","type":"string"},"is_current":{"description":"","type":"boolean"},"is_primary":{"description":"","type":"boolean"},"landlord_name":{"description":"","type":"string"},"landlord_phone":{"description":"","type":"string"},"monthly_payment_cents":{"description":"","type":"integer"},"ownership":{"description":"","type":"string"},"source":{"description":"","type":"string"},"start_date":{"format":"date-time","type":"string"},"state":{"description":"","type":"string"},"zip_code":{"description":"","type":"string"}},"type":"object"},"Application":{"properties":{"application_json":{"description":"","type":"string"},"application_uuid":{"description":"","type":"string"},"borrower_uuid":{"description":"","type":"string"},"company_uuid":{"description":"","type":"string"},"decision_json":{"description":"","type":"string"},"decision_reason":{"description":"","type":"string"},"dt_created":{"format":"date-time","type":"string"},"dt_decided":{"format":"date-time","type":"string"},"dt_submitted":{"format":"date-time","type":"string"},"loan_type_uuid":{"description":"","type":"string"},"requested_cents":{"description":"","type":"integer"},"score":{"description":"","type":"integer"},"status":{"enum":["pending","approved","declined","converted"],"type":"string"}},"type":"object"},"Assignment":{"properties":{"assignment_uuid":{"description":"","type":"string"},"dt_assigned":{"format":"date-time","type":"string"},"lender_uuid":{"description":"The chosen lender","type":"string"},"loan_uuid":{"description":"","type":"string"},"pool_uuid":{"description":"","type":"string"},"status":{"description":"","type":"string"},"strategy_uuid":{"description":"","type":"string"}},"type":"object"},"Borrower":{"description":"SSN is write-only and never returned.","properties":{"address_line1":{"description":"","type":"string"},"address_line2":{"description":"","type":"string"},"borrower_uuid":{"description":"","type":"string"},"city":{"description":"","type":"string"},"company_uuid":{"description":"","type":"string"},"country":{"description":"","type":"string"},"date_of_birth":{"description":"","type":"string"},"email":{"description":"","type":"string"},"email_verified":{"description":"","type":"boolean"},"external_id":{"description":"","type":"string"},"first_name":{"description":"","type":"string"},"home_phone":{"description":"","type":"string"},"last_name":{"description":"","type":"string"},"mobile_phone":{"description":"","type":"string"},"pay_anchor_date":{"description":"","type":"string"},"pay_day_1":{"description":"","type":"integer"},"pay_day_2":{"description":"","type":"integer"},"pay_frequency":{"description":"","type":"string"},"phone_verified":{"description":"","type":"boolean"},"state":{"description":"","type":"string"},"work_phone":{"description":"","type":"string"},"zip_code":{"description":"","type":"string"}},"type":"object"},"CashDisbursement":{"description":"A cash payout the loan platform asked the store register to make. Status: pending (unclaimed; claimable 4h), claimed (a register is paying it out; payable 24h), paying (confirmation in flight), paid (draw posted), needs_reconcile (cash paid, draw refused / closed under the register / claimed and expired — settle with POST .../resolve), canceled, expired, voided (the posted draw was voided on the ledger; cash_disbursement.voided was emitted and the register books the cash back).","properties":{"amount_cents":{"description":"","type":"integer"},"borrower_name":{"description":"The borrower's display name","type":"string"},"borrower_uuid":{"description":"","type":"string"},"business_date":{"description":"YYYY-MM-DD: the register's business date when sent, else the ET business date of the claim / paid call","type":"string"},"cash_disbursement_uuid":{"description":"","type":"string"},"claimed_at":{"format":"date-time","type":"string"},"closed_at":{"format":"date-time","type":"string"},"created_at":{"format":"date-time","type":"string"},"description":{"description":"","type":"string"},"drawer_session_uuid":{"description":"","type":"string"},"error_text":{"description":"Set on needs_reconcile","type":"string"},"expires_at":{"format":"date-time","type":"string"},"kind":{"enum":["draw","opening_advance"],"type":"string"},"loan_number":{"description":"The loan's number, for the teller's payout list","type":"string"},"loan_transaction_uuid":{"description":"Set once paid (and kept on voided)","type":"string"},"loan_uuid":{"description":"","type":"string"},"network":{"description":"money_transfer only","enum":["moneygram","western_union"],"type":"string"},"network_fee_cents":{"description":"Reported on paid (money_transfer)","type":"integer"},"network_ref":{"description":"Reported on paid (money_transfer)","type":"string"},"paid_at":{"format":"date-time","type":"string"},"product_name":{"description":"The loan type's name","type":"string"},"rail":{"description":"cash = pay out of the drawer; money_transfer = send through the named network for the borrower to pick up","enum":["cash","money_transfer"],"type":"string"},"reason":{"description":"Close reason of a canceled / voided payout","type":"string"},"receipt_number":{"description":"","type":"string"},"recipient":{"description":"money_transfer only: the borrower snapshot to key into the network","properties":{"address_state":{"description":"","type":"string"},"name":{"description":"","type":"string"},"phone":{"description":"","type":"string"}},"type":"object"},"register_txn_ref":{"description":"","type":"string"},"register_uuid":{"description":"","type":"string"},"requested_by":{"description":"","type":"string"},"resolution_reason":{"description":"How a needs_reconcile payout was settled","type":"string"},"resolved_at":{"format":"date-time","type":"string"},"status":{"enum":["pending","claimed","paying","paid","needs_reconcile","canceled","expired","voided"],"type":"string"},"store_uuid":{"description":"","type":"string"},"teller":{"description":"Register teller on file (claim, overridden by paid)","properties":{"external_id":{"description":"","type":"string"},"name":{"description":"","type":"string"},"subject":{"description":"","type":"string"}},"type":"object"},"updated_at":{"format":"date-time","type":"string"}},"type":"object"},"CounterReceipt":{"description":"The retained receipt a register-bridge posting produced (also GET /loans/{loan_uuid}/receipts/{receipt_uuid} with receipt_lines).","properties":{"amount_cents":{"description":"","type":"integer"},"balance_after_cents":{"description":"Remaining balance right after the posting","type":"integer"},"channel":{"description":"counter | servicing | portal | api","type":"string"},"document_instance_uuid":{"description":"A generated statement attached to this receipt (renewal statement)","type":"string"},"dt_issued":{"format":"date-time","type":"string"},"issued_by":{"description":"lms | store_operations","type":"string"},"kind":{"enum":["payment","payoff","draw","void","refund","title_fee","held_check","renewal","other"],"type":"string"},"maturity_date":{"description":"YYYY-MM-DD (single-pay / pawn)","type":"string"},"next_amount_due_cents":{"description":"","type":"integer"},"next_due_date":{"description":"YYYY-MM-DD","type":"string"},"paid_in_full":{"description":"","type":"boolean"},"payoff_at_maturity_cents":{"description":"","type":"integer"},"receipt_lines":{"items":{"$ref":"#/components/schemas/ReceiptLine"},"type":"array"},"receipt_number":{"description":"LMS number, or the register's number for a counter reference","type":"string"},"receipt_uuid":{"description":"","type":"string"},"register_receipt_number":{"description":"","type":"string"},"view":{"description":"ReceiptView JSON (allocation[], balances after, store block, renewal / held_check blocks)","type":"object"},"voided":{"description":"","type":"boolean"}},"type":"object"},"DenyListEntry":{"description":"A deny-list row. The blind-index hash and plaintext identity are never returned.","properties":{"action":{"description":"decline | refer","type":"string"},"active":{"description":"","type":"boolean"},"company_uuid":{"description":"Absent on the global tier","type":"string"},"deny_entry_uuid":{"description":"","type":"string"},"dt_created":{"format":"date-time","type":"string"},"dt_updated":{"format":"date-time","type":"string"},"match_last4":{"description":"Last four of the matched identity","type":"string"},"match_type":{"description":"ssn_hash | name_dob | email | phone | device","type":"string"},"reason":{"description":"","type":"string"},"scope":{"description":"company | global","type":"string"},"source":{"description":"api | admin | ...","type":"string"}},"type":"object"},"Disbursement":{"description":"One gateway draw: the public reference to poll and quote to support, its status, the money, and the processor's processed time. Internal identifiers are deliberately absent.","properties":{"amount_cents":{"description":"Gross: the loan is charged this","type":"integer"},"check_document_url":{"description":"check rail: the rendered check document","type":"string"},"check_number":{"description":"check rail: the printed check number","type":"string"},"check_voided":{"description":"check rail: the instrument was voided and the principal reversed","type":"boolean"},"created_at":{"format":"date-time","type":"string"},"currency":{"description":"","type":"string"},"disbursement_uuid":{"description":"Public draw reference","type":"string"},"failure_code":{"description":"Set on failed","type":"string"},"fee_cents":{"description":"Processor fee, deducted from the amount delivered","type":"integer"},"loan_transaction_uuid":{"description":"Set once posted","type":"string"},"loan_uuid":{"description":"","type":"string"},"net_cents":{"description":"Delivered to the destination","type":"integer"},"payment_method_uuid":{"description":"","type":"string"},"processed_at":{"format":"date-time","type":"string"},"rail":{"enum":["card","ach_credit","check"],"type":"string"},"replayed":{"type":"boolean"},"status":{"enum":["pending","succeeded","failed","needs_reconcile"],"type":"string"},"updated_at":{"format":"date-time","type":"string"}},"type":"object"},"DocumentMeta":{"properties":{"description":{"description":"","type":"string"},"doc_type":{"description":"","type":"string"},"document_uuid":{"description":"","type":"string"},"dt_created":{"format":"date-time","type":"string"},"filename":{"description":"","type":"string"},"mime_type":{"description":"","type":"string"},"size_bytes":{"description":"","type":"integer"}},"type":"object"},"EmployeeBlockEntry":{"properties":{"added_at":{"format":"date-time","type":"string"},"added_by":{"description":"","type":"string"},"employer":{"description":"","type":"string"},"entry_uuid":{"description":"","type":"string"},"first_name":{"description":"","type":"string"},"last_name":{"description":"","type":"string"},"note":{"description":"","type":"string"},"reference":{"description":"HR / payroll reference","type":"string"},"source":{"description":"","type":"string"},"ssn_last4":{"description":"","type":"string"}},"type":"object"},"Error":{"properties":{"code":{"description":"Stable machine code","type":"string"},"error":{"description":"Human-readable message","type":"string"}},"type":"object"},"FacilitySnapshot":{"properties":{"advance_rate_bps":{"description":"","type":"integer"},"as_of":{"format":"date-time","type":"string"},"available_cents":{"description":"max(0, commitment − outstanding)","type":"integer"},"borrowing_base_cents":{"description":"eligible × advance rate, capped at commitment","type":"integer"},"commitment_cents":{"description":"","type":"integer"},"concentration":{"items":{"properties":{"dimension":{"enum":["state","product","channel","borrower"],"type":"string"},"key":{"description":"","type":"string"},"label":{"description":"","type":"string"},"loan_count":{"description":"","type":"integer"},"principal_cents":{"description":"","type":"integer"}},"type":"object"},"type":"array"},"concentration_breaches":{"items":{"type":"string"},"type":"array"},"eligible_cents":{"description":"Outstanding that passes the eligibility rules as of as_of","type":"integer"},"eligible_loan_count":{"description":"","type":"integer"},"facility_code":{"description":"","type":"string"},"facility_type":{"description":"","type":"string"},"ineligible_cents":{"description":"","type":"integer"},"loan_count":{"description":"","type":"integer"},"name":{"description":"","type":"string"},"outstanding_cents":{"description":"Principal across active assignments","type":"integer"},"pool_uuid":{"description":"","type":"string"},"shortfall_cents":{"description":"max(0, outstanding − borrowing base)","type":"integer"},"utilization_bps":{"description":"outstanding / commitment","type":"integer"}},"type":"object"},"HardshipCampaign":{"properties":{"action":{"description":"","type":"string"},"approval_request_uuid":{"description":"","type":"string"},"campaign_uuid":{"description":"","type":"string"},"declaration_number":{"description":"","type":"string"},"dt_applied":{"description":"","type":"string"},"effective_from":{"description":"","type":"string"},"effective_to":{"description":"","type":"string"},"enrollment":{"description":"","type":"string"},"extend_days":{"description":"","type":"integer"},"forgive_per_payment_cents":{"description":"","type":"integer"},"name":{"description":"","type":"string"},"only_active":{"type":"boolean"},"reason":{"description":"","type":"string"},"skip_payments":{"description":"","type":"integer"},"status":{"description":"draft | pending_approval | applied | cancelled | failed","type":"string"},"waive_annual_fees":{"type":"boolean"},"waive_interest":{"type":"boolean"},"waive_late_fees":{"type":"boolean"},"waive_period_fees":{"type":"boolean"}},"type":"object"},"HardshipCampaignEnrollment":{"properties":{"amount_cents":{"description":"","type":"integer"},"borrower_uuid":{"description":"","type":"string"},"campaign_uuid":{"description":"","type":"string"},"effective_from":{"description":"","type":"string"},"effective_to":{"description":"","type":"string"},"enrollment_uuid":{"description":"","type":"string"},"loan_uuid":{"description":"","type":"string"},"note":{"description":"","type":"string"},"status":{"description":"eligible | opted_in | applied | skipped | failed | declined","type":"string"},"waive_annual_fees":{"type":"boolean"},"waive_interest":{"type":"boolean"},"waive_late_fees":{"type":"boolean"},"waive_period_fees":{"type":"boolean"}},"type":"object"},"HardshipCampaignInput":{"properties":{"action":{"description":"skip_a_pay | forgive_a_pay | '' (waiver window only)","type":"string"},"declaration_number":{"description":"FEMA DR-/EM- number","type":"string"},"effective_from":{"description":"YYYY-MM-DD","type":"string"},"effective_to":{"description":"YYYY-MM-DD","type":"string"},"enrollment":{"description":"auto | opt_in","type":"string"},"extend_days":{"description":"Explicit maturity extension instead of periods","type":"integer"},"forgive_per_payment_cents":{"description":"","type":"integer"},"loan_refs":{"items":{"description":"Loan number or UUID","type":"string"},"type":"array"},"name":{"description":"","type":"string"},"only_active":{"description":"Default true","type":"boolean"},"reason":{"description":"Stamped on every modification","type":"string"},"region":{"properties":{"counties":{"items":{"description":"Recorded only","type":"string"},"type":"array"},"states":{"items":{"description":"","type":"string"},"type":"array"},"zip_list":{"items":{"description":"5-digit zip or 3-digit prefix","type":"string"},"type":"array"}},"type":"object"},"skip_payments":{"description":"Periods to defer (skip_a_pay)","type":"integer"},"waive_annual_fees":{"description":"Waive annual fee installments in the window (default false)","type":"boolean"},"waive_interest":{"type":"boolean"},"waive_late_fees":{"description":"Default true","type":"boolean"},"waive_period_fees":{"description":"Waive the period / monthly fee in the window (default false)","type":"boolean"}},"required":["name","reason","effective_from","effective_to"],"type":"object"},"HeldCheck":{"properties":{"account_last4":{"description":"","type":"string"},"amount_cents":{"description":"","type":"integer"},"borrower_uuid":{"description":"","type":"string"},"check_number":{"description":"","type":"string"},"custody_drawer_session_uuid":{"description":"","type":"string"},"custody_notes":{"description":"","type":"string"},"custody_register_uuid":{"description":"","type":"string"},"custody_release_reason":{"enum":["","presented","returned_to_borrower","deposited","missing"],"type":"string"},"custody_source":{"description":"Who holds the paper: the store (custody_notes here) or the Store Operations register (custody fields below).","enum":["lms","register"],"type":"string"},"custody_store_uuid":{"description":"","type":"string"},"custody_teller_external_id":{"description":"","type":"string"},"custody_teller_name":{"description":"","type":"string"},"drawer_bank":{"description":"","type":"string"},"dt_created":{"format":"date-time","type":"string"},"dt_custody_recorded":{"format":"date-time","type":"string"},"dt_custody_released":{"format":"date-time","type":"string"},"dt_updated":{"format":"date-time","type":"string"},"gateway_txn_uuid":{"description":"Presentment debit","type":"string"},"held_check_uuid":{"description":"","type":"string"},"loan_uuid":{"description":"","type":"string"},"payment_method_uuid":{"description":"Vaulted bank account (ach mode)","type":"string"},"payment_txn_uuid":{"description":"","type":"string"},"presentment_date":{"description":"YYYY-MM-DD","type":"string"},"presentment_mode":{"enum":["ach","manual",""],"type":"string"},"register_instrument_ref":{"description":"Register tender_instrument holding the paper","type":"string"},"return_code":{"description":"","type":"string"},"routing_number":{"description":"","type":"string"},"status":{"enum":["held","presented","deposited","returned","returned_to_borrower","voided"],"type":"string"},"written_date":{"description":"YYYY-MM-DD","type":"string"}},"type":"object"},"HeldCheckCustody":{"properties":{"custody_state":{"enum":["awaiting","held","released"],"type":"string"},"held_check":{"$ref":"#/components/schemas/HeldCheck"},"replayed":{"description":"The request matched what was already on file","type":"boolean"}},"type":"object"},"HeldCheckListItem":{"allOf":[{"$ref":"#/components/schemas/HeldCheck"},{"properties":{"bank_name":{"description":"= drawer_bank","type":"string"},"custody":{"description":"Register paper custody state; empty for LMS custody","enum":["","awaiting","held","released"],"type":"string"},"loan_number":{"description":"","type":"string"},"store_uuid":{"description":"Custody store, else the loan's home store","type":"string"}},"type":"object"}],"description":"GET .../held-checks row: a HeldCheck plus the register's names.","type":"object"},"HeldCheckPresentment":{"properties":{"declined":{"description":"","type":"boolean"},"gateway_txn_uuid":{"description":"","type":"string"},"held_check_uuid":{"description":"","type":"string"},"payment_txn_uuid":{"description":"","type":"string"},"result_code":{"description":"","type":"string"},"result_text":{"description":"","type":"string"},"status":{"enum":["presented","deposited","returned"],"type":"string"}},"type":"object"},"LeadDecision":{"description":"The decision on a posted marketplace lead.","properties":{"decision":{"enum":["accept","decline"],"type":"string"},"lead_uuid":{"description":"","type":"string"},"loan_type_uuid":{"description":"accept","type":"string"},"reasons":{"description":"decline reason codes: affiliate_unknown | invalid_applicant:\u003cfields\u003e | consent_required | no_eligible_product | product_not_online | state_not_served | amount_out_of_range | price_too_high","items":{"type":"string"},"type":"array"},"redirect_expires_at":{"format":"date-time","type":"string"},"redirect_url":{"description":"accept: one-time prefilled application link (24h)","type":"string"},"replayed":{"type":"boolean"}},"type":"object"},"Lender":{"properties":{"company_uuid":{"description":"","type":"string"},"dt_created":{"format":"date-time","type":"string"},"email":{"description":"Contact email printed on documents","type":"string"},"last_assigned_ts":{"format":"date-time","type":"string"},"lender_uuid":{"description":"","type":"string"},"name":{"description":"","type":"string"},"phone":{"description":"Contact phone printed on documents","type":"string"},"portal_url":{"description":"Portal address printed on documents","type":"string"},"total_allocation_cents":{"description":"Hard capital cap","type":"integer"}},"type":"object"},"LienView":{"properties":{"collateral_uuid":{"description":"","type":"string"},"elt_configured":{"description":"","type":"boolean"},"funding_held":{"description":"The product's lien funding gate currently blocks disbursement","type":"boolean"},"lien_failure_reason":{"description":"","type":"string"},"lien_method":{"description":"electronic_elt | dmv_submission | paper | none","type":"string"},"lien_perfected_date":{"description":"","type":"string"},"lien_position":{"description":"1 | 2","type":"integer"},"lien_reference":{"description":"","type":"string"},"lien_release_reference":{"description":"","type":"string"},"lien_release_requested_date":{"description":"","type":"string"},"lien_released_date":{"description":"","type":"string"},"lien_status":{"description":"none_yet | pending | submitted | perfected | release_requested | released | failed","type":"string"},"lien_submitted_date":{"description":"","type":"string"},"loan_uuid":{"description":"","type":"string"},"title_custody":{"description":"with_borrower | received | held | returned | forfeited_to_pawnbroker","type":"string"},"vehicle_class":{"description":"auto | motorcycle | rv | boat | commercial | other","type":"string"}},"type":"object"},"Loan":{"properties":{"borrower_uuid":{"description":"","type":"string"},"cab_fee_outstanding_cents":{"description":"CSO/CAB products only","type":"integer"},"credit_balance_cents":{"description":"","type":"integer"},"credit_limit_cents":{"description":"","type":"integer"},"currency_code":{"description":"","type":"string"},"delinquent_since_date":{"format":"date-time","type":"string"},"dt_maturity":{"format":"date-time","type":"string"},"dt_opened":{"format":"date-time","type":"string"},"fees_outstanding_cents":{"description":"","type":"integer"},"interest_outstanding_cents":{"description":"","type":"integer"},"last_due_date":{"format":"date-time","type":"string"},"last_interest_calc_date":{"format":"date-time","type":"string"},"last_statement_cut":{"format":"date-time","type":"string"},"loan_number":{"description":"","type":"string"},"loan_type_uuid":{"description":"","type":"string"},"loan_uuid":{"description":"","type":"string"},"principal_outstanding_cents":{"description":"","type":"integer"},"status":{"description":"","type":"string"},"store_uuid":{"description":"Home store (GL store dimension, per-store filings). Empty on a loan originated before the store backfill ran; readers fall back to the company default store","type":"string"}},"type":"object"},"LoanCounterView":{"allOf":[{"$ref":"#/components/schemas/Loan"},{"properties":{"amount_due_cents":{"description":"Current obligation: scheduled installment + arrears on an installment contract, else interest + fees owed; never more than total_outstanding_cents","type":"integer"},"amount_due_unavailable":{"description":"Present (true) when the scheduled obligation could not be computed (schedule / ledger unavailable): amount_due_cents is then the full balance","type":"boolean"},"borrower_name":{"description":"","type":"string"},"collateral_uuid":{"description":"","type":"string"},"lien_status":{"description":"none_yet | pending | submitted | perfected | release_requested | released | failed (absent without a vehicle)","type":"string"},"next_due_date":{"description":"YYYY-MM-DD","type":"string"},"payable":{"description":"False exactly when POST .../payments would answer 409 loan_not_payable","type":"boolean"},"title_collateral":{"description":"The loan carries a collateral vehicle","type":"boolean"},"title_custody":{"description":"with_borrower | received | held | returned | forfeited_to_pawnbroker (absent without a vehicle)","type":"string"},"title_fee_counter_receipt":{"description":"Absent when the loan has no cash-at-origination title fee","properties":{"amount_cents":{"description":"The fee due at the counter","type":"integer"},"external_ref":{"description":"","type":"string"},"method":{"description":"","type":"string"},"receipt_number":{"description":"The register's receipt label on file","type":"string"},"receipted_at":{"format":"date-time","type":"string"},"status":{"enum":["at_origination","pending","received","voided"],"type":"string"},"store_uuid":{"description":"","type":"string"},"transaction_uuid":{"description":"The receipt row on file","type":"string"},"voided_at":{"format":"date-time","type":"string"}},"type":"object"},"total_outstanding_cents":{"description":"","type":"integer"}},"type":"object"}],"description":"GET /loans/{loan_uuid}: every Loan field plus the teller-counter view.","type":"object"},"LoanReceipt":{"description":"A retained receipt (loan_receipt row).","properties":{"amount_cents":{"description":"Receipt amount (negative on a void receipt)","type":"integer"},"channel":{"enum":["servicing","portal","counter","api"],"type":"string"},"dt_issued":{"format":"date-time","type":"string"},"dt_voided":{"format":"date-time","type":"string"},"issued_by":{"description":"Staff user uuid, register teller subject, \"borrower\", or a system name","type":"string"},"kind":{"description":"held_check = a CHECK HELD / CHECK RELEASED custody slip (no ledger row; transaction_uuids carries the slip's deterministic key); renewal = a renewal / extension statement","enum":["payment","payoff","draw","void","refund","title_fee","held_check","renewal","other"],"type":"string"},"loan_uuid":{"description":"","type":"string"},"receipt_number":{"description":"The number printed on the receipt (LMS RCPT-… / VOID-…, or the register's number for a counter reference)","type":"string"},"receipt_uuid":{"description":"","type":"string"},"register_receipt_number":{"description":"Counter receipt number for a bridged payment","type":"string"},"retain_until":{"format":"date","type":"string"},"store_uuid":{"description":"Point-of-receipt store when recorded","type":"string"},"transaction_uuids":{"description":"Ledger rows the receipt covers; the first is the group anchor","items":{"type":"string"},"type":"array"},"voided":{"description":"","type":"boolean"},"voided_by_receipt_uuid":{"description":"The void receipt that supersedes this one","type":"string"},"voids_receipt_uuid":{"description":"On a void receipt: the original it cancels","type":"string"}},"type":"object"},"LoanType":{"properties":{"annual_rate_bps":{"description":"","type":"integer"},"base_type_uuid":{"description":"Version family id","type":"string"},"company_uuid":{"description":"","type":"string"},"config_json":{"description":"","type":"string"},"description":{"description":"","type":"string"},"dt_created":{"format":"date-time","type":"string"},"interest_post_frequency":{"description":"","type":"string"},"loan_type_uuid":{"description":"","type":"string"},"name":{"description":"","type":"string"},"portal_name":{"description":"","type":"string"},"status":{"enum":["active","archived"],"type":"string"},"version":{"description":"","type":"integer"}},"type":"object"},"PawnForfeiture":{"properties":{"already_forfeited":{"description":"","type":"boolean"},"charges_forfeited_cents":{"description":"","type":"integer"},"fees_forfeited_cents":{"description":"","type":"integer"},"forfeited_at":{"description":"RFC3339","type":"string"},"principal_forfeited_cents":{"description":"","type":"integer"},"ticket_number":{"description":"","type":"string"}},"type":"object"},"PawnRenewal":{"properties":{"charge_posted_cents":{"description":"The new period's charge posted at the renewal (0 when it has not started)","type":"integer"},"new_grace_end_date":{"description":"YYYY-MM-DD","type":"string"},"new_maturity_date":{"description":"YYYY-MM-DD","type":"string"},"renewal_number":{"description":"","type":"integer"},"renewal_statement":{"$ref":"#/components/schemas/RenewalStatement"},"ticket_number":{"description":"","type":"string"}},"type":"object"},"PawnStanding":{"properties":{"can_forfeit":{"description":"","type":"boolean"},"can_redeem":{"description":"A payoff today redeems the pledge","type":"boolean"},"can_renew":{"description":"Counter semantics: true while the window and renewal bounds allow it, even with the charge outstanding (renew_rule=charge_unpaid)","type":"boolean"},"charge_due_cents":{"description":"Counter: the pawnshop charge to collect to renew today (interest + fees outstanding)","type":"integer"},"current_rate_bps_per_period":{"description":"After step-downs","type":"integer"},"forfeit_refusal":{"description":"","type":"string"},"forfeiture_date":{"description":"YYYY-MM-DD: first day the pledge may be forfeited (grace end + 1)","type":"string"},"forfeiture_mode":{"description":"manual | auto_after_grace","type":"string"},"forfeiture_status":{"description":"active | in_grace | grace_closed | forfeited | redeemed","type":"string"},"in_grace":{"description":"","type":"boolean"},"max_renewals":{"description":"0 = unlimited","type":"integer"},"next_maturity_date":{"description":"YYYY-MM-DD","type":"string"},"next_maturity_if_renewed":{"description":"YYYY-MM-DD","type":"string"},"next_period_charge_cents":{"description":"","type":"integer"},"next_period_rate_bps":{"description":"","type":"integer"},"past_maturity":{"description":"","type":"boolean"},"pawn":{"description":"The loan_pawn record: ticket_number, pledge_date, period_days, charge_accrual (per_period | daily), charge_rate_bps_per_period, step_downs_json, principal_cents, maturity_date, grace_days, grace_end_date, charges_continue_after_maturity, renewal_count, max_renewals, charges_posted_cents, status (active | redeemed | forfeited), redeemed_at, forfeited_at, principal/charges/fees_forfeited_cents","type":"object"},"pledge_age_days":{"description":"","type":"integer"},"redeem_amount_cents":{"description":"Payoff that redeems the pledge today","type":"integer"},"redeem_rule":{"description":"grace_closed | not_active","type":"string"},"redemption_cents":{"description":"Principal + charges + fees outstanding today","type":"integer"},"renew_refusal":{"description":"","type":"string"},"renew_rule":{"description":"charge_unpaid | max_renewals | pawn_max_term_days | grace_closed | not_active","type":"string"},"renewals_used":{"description":"","type":"integer"},"status":{"description":"active | redeemed | forfeited","type":"string"},"ticket_number":{"description":"","type":"string"}},"type":"object"},"PawnVehicle":{"properties":{"collateral_uuid":{"description":"","type":"string"},"color":{"description":"","type":"string"},"inventory_item_uuid":{"description":"Register inventory item once taken in","type":"string"},"lien_status":{"description":"","type":"string"},"make":{"description":"","type":"string"},"mileage":{"description":"","type":"integer"},"model":{"description":"","type":"string"},"odometer":{"description":"= mileage, always present","type":"integer"},"plate":{"description":"","type":"string"},"plate_state":{"description":"","type":"string"},"title_custody":{"description":"","type":"string"},"title_number":{"description":"","type":"string"},"title_state":{"description":"","type":"string"},"trim":{"description":"","type":"string"},"vehicle_class":{"description":"","type":"string"},"vin":{"description":"","type":"string"},"year":{"description":"","type":"integer"}},"type":"object"},"PaymentGroup":{"properties":{"amount_cents":{"description":"","type":"integer"},"external_display":{"description":"","type":"string"},"external_ref":{"description":"","type":"string"},"external_source":{"description":"","type":"string"},"idempotency_key":{"description":"","type":"string"},"loan_uuid":{"description":"","type":"string"},"payment_ref":{"description":"transaction_uuids[0]","type":"string"},"rows":{"items":{"type":"object"},"type":"array"},"status":{"enum":["posted","partially_voided","voided"],"type":"string"},"store_uuid":{"description":"","type":"string"},"transaction_uuids":{"items":{"type":"string"},"type":"array"},"void_pending_approval":{"description":"Present (true) only while a void is parked in the approval queue","type":"boolean"}},"type":"object"},"PaymentPreview":{"description":"GET /loans/{loan_uuid}/payment-preview: the projected posting.","properties":{"accrued_at_payment_cents":{"description":"Interest / pawn charge the payment path itself posts before allocating","type":"integer"},"allocation":{"items":{"properties":{"amount_cents":{"description":"","type":"integer"},"bucket":{"description":"principal | interest | fees | cab_fee | credit | refund_due","type":"string"},"label":{"description":"","type":"string"}},"type":"object"},"type":"array"},"allocation_mode":{"description":"waterfall | principal_only","type":"string"},"allocation_total_cents":{"description":"","type":"integer"},"amount_cents":{"description":"","type":"integer"},"applied_cents":{"description":"Cash into outstanding buckets","type":"integer"},"balance_after_cents":{"description":"","type":"integer"},"balance_before_cents":{"description":"The live row at preview time","type":"integer"},"credit_balance_after_cents":{"description":"","type":"integer"},"credit_consumed_cents":{"description":"Prior held credit consumed first","type":"integer"},"fees_after_cents":{"description":"","type":"integer"},"fees_before_cents":{"description":"","type":"integer"},"generated_at":{"format":"date-time","type":"string"},"held_credit_cents":{"description":"Overpayment parked as account credit","type":"integer"},"interest_after_cents":{"description":"","type":"integer"},"interest_before_cents":{"description":"","type":"integer"},"loan_number":{"description":"","type":"string"},"loan_status_after":{"description":"","type":"string"},"loan_uuid":{"description":"","type":"string"},"maturity_date":{"description":"YYYY-MM-DD","type":"string"},"method_label":{"description":"","type":"string"},"next_amount_due_cents":{"description":"","type":"integer"},"next_due_date":{"description":"YYYY-MM-DD","type":"string"},"paid_in_full":{"description":"","type":"boolean"},"payment_date_et":{"description":"YYYY-MM-DD","type":"string"},"payoff_at_maturity_cents":{"description":"","type":"integer"},"payoff_shortfall_cents":{"description":"What remains to pay the loan off after this payment (0 when paid in full)","type":"integer"},"principal_after_cents":{"description":"","type":"integer"},"principal_before_cents":{"description":"","type":"integer"},"receipt":{"description":"The projected ReceiptView (no number: nothing was posted)","type":"object"},"receipt_lines":{"items":{"$ref":"#/components/schemas/ReceiptLine"},"type":"array"},"refund_due_cents":{"description":"Overpayment booked as a refund","type":"integer"},"when":{"format":"date-time","type":"string"}},"type":"object"},"PaymentResult":{"allOf":[{"$ref":"#/components/schemas/Loan"},{"properties":{"external_ref":{"description":"","type":"string"},"external_source":{"description":"","type":"string"},"idempotency_key":{"description":"","type":"string"},"payment_ref":{"description":"transaction_uuids[0]: the stable id of this posting (stamp it on your ledger row; GET .../payments/{key} echoes it)","type":"string"},"posted_by":{"properties":{"external_id":{"description":"","type":"string"},"name":{"description":"","type":"string"},"subject":{"description":"","type":"string"}},"type":"object"},"receipt":{"$ref":"#/components/schemas/CounterReceipt"},"replayed":{"description":"true when an Idempotency-Key / external_ref replay answered with the original posting","type":"boolean"},"transaction_uuids":{"items":{"type":"string"},"type":"array"}},"type":"object"}],"description":"The updated Loan fields inline, plus posting metadata.","type":"object"},"PayoffQuote":{"description":"A counter payoff quote (GET /loans/{loan_uuid}/payoff).","properties":{"as_of":{"format":"date","type":"string"},"cab_fee_net_cents":{"description":"","type":"integer"},"cab_fee_rebate_cents":{"description":"","type":"integer"},"fees_cents":{"description":"","type":"integer"},"good_through":{"description":"Last civil day the total may be honored","format":"date","type":"string"},"interest_cents":{"description":"","type":"integer"},"issued_at":{"format":"date-time","type":"string"},"loan_uuid":{"description":"","type":"string"},"per_diem_cents":{"description":"Interest per day after as_of","type":"integer"},"precomputed_charge_cents":{"description":"","type":"integer"},"precomputed_earned_cents":{"description":"","type":"integer"},"precomputed_rebate_cents":{"description":"","type":"integer"},"principal_cents":{"description":"","type":"integer"},"projected_fees_cents":{"description":"Charges the engine will post before as_of","type":"integer"},"quote_id":{"description":"Honor with POST .../payments payoff_quote_id","type":"string"},"total_cents":{"description":"Pay exactly this","type":"integer"}},"type":"object"},"PendingApproval":{"properties":{"approval_request_uuid":{"description":"","type":"string"},"code":{"enum":["approval_pending"],"type":"string"},"required_approvals":{"description":"","type":"integer"},"status":{"enum":["pending_approval"],"type":"string"}},"type":"object"},"Phone":{"properties":{"bad":{"description":"","type":"boolean"},"do_not_call":{"description":"","type":"boolean"},"do_not_text":{"description":"","type":"boolean"},"dt_created":{"format":"date-time","type":"string"},"is_current":{"description":"","type":"boolean"},"is_verified":{"description":"","type":"boolean"},"label":{"description":"","type":"string"},"number":{"description":"","type":"string"},"phone_type":{"description":"","type":"string"},"phone_uuid":{"description":"","type":"string"},"source":{"description":"","type":"string"}},"type":"object"},"Pool":{"properties":{"advance_rate_bps":{"description":"Share of eligible principal funded (borrowing base); 0 = 100%","type":"integer"},"commitment_cents":{"description":"Hard ceiling on outstanding principal; 0 = uncapped","type":"integer"},"company_uuid":{"description":"","type":"string"},"concentration_limits_json":{"description":"JSON: max_state_share_bps / max_product_share_bps / max_channel_share_bps maps (\"*\" = default) + max_single_borrower_share_bps","type":"string"},"dt_created":{"format":"date-time","type":"string"},"eligibility_rules_json":{"description":"JSON: states_in/_not_in, product_base_types_in/_not_in, channels, min/max_principal_cents, max_apr_bps, min_credit_score (+require_credit_score), max_days_past_due, collateral_types, min/max_loan_age_days, exclude_restructured","type":"string"},"facility_code":{"description":"Short stable tag, e.g. SPARROW_SPAY; unique per company","type":"string"},"facility_type":{"enum":["warehouse","abl","forward_flow","balance_sheet","other"],"type":"string"},"kind":{"enum":["lender_pool","credit_facility"],"type":"string"},"lender_pool_uuid":{"description":"","type":"string"},"name":{"description":"","type":"string"},"reporting_period":{"enum":["monthly","weekly"],"type":"string"},"strategy_uuid":{"description":"","type":"string"}},"type":"object"},"PoolInput":{"properties":{"advance_rate_bps":{"description":"0-10000","type":"integer"},"commitment_cents":{"description":"≥ 0","type":"integer"},"concentration_limits_json":{"description":"See Pool","type":"string"},"eligibility_rules_json":{"description":"See Pool","type":"string"},"facility_code":{"description":"Required for a facility; [A-Z0-9_]{2,64}, upper-cased","type":"string"},"facility_type":{"enum":["warehouse","abl","forward_flow","balance_sheet","other"],"type":"string"},"kind":{"description":"Default lender_pool","enum":["lender_pool","credit_facility"],"type":"string"},"name":{"description":"","type":"string"},"reporting_period":{"enum":["monthly","weekly"],"type":"string"},"strategy_uuid":{"description":"Empty string unsets","type":"string"}},"required":["name"],"type":"object"},"ReceiptLine":{"description":"One print-ready disclosure line of a counter slip.","properties":{"amount_cents":{"description":"Set on money lines","type":"integer"},"kind":{"enum":["receipt_number","licensee_name","licensee_address","licensee_phone","license","loan_number","account_holder","payment_date","amount_paid","tender","total_charged","processing_fee","applied","principal_after","interest_after","fees_after","balance_after","paid_in_full","account_credit","next_due","maturity","counter","renewal","held_check","generated"],"type":"string"},"label":{"description":"","type":"string"},"value":{"description":"Print-ready","type":"string"}},"type":"object"},"RefinanceResult":{"properties":{"buydown_cents":{"description":"","type":"integer"},"kind":{"enum":["refinance","buy_up","buydown","loc_conversion"],"type":"string"},"loan":{"$ref":"#/components/schemas/Loan"},"new_advance_cents":{"description":"","type":"integer"},"new_loan_uuid":{"description":"","type":"string"},"payoff_cents":{"description":"Full balance on the prior loan before any buydown","type":"integer"},"prior_loan_uuid":{"description":"","type":"string"},"rolled_balance_cents":{"description":"Carried into the new loan (payoff minus buydown)","type":"integer"}},"type":"object"},"RenewalStatement":{"description":"The renewal / extension statement issued for a pawn renewal or a single-pay rollover (TN § 45-15-113, ID § 28-46-506, GA § 44-14-403, FL § 539.001(11)(b), TX § 371.160). Retained as a kind=renewal receipt (statement_receipt_uuid, printable through GET .../receipts/{uuid}) and, when the loan type binds a package at at_pawn_renewal / at_rollover, generated as a document instance (statement_instance_uuid) once per renewal.","properties":{"apr_percent":{"description":"","type":"string"},"charge_due_next_period_cents":{"description":"","type":"integer"},"charge_paid_cents":{"description":"","type":"integer"},"charge_posted_cents":{"description":"","type":"integer"},"event_date":{"description":"YYYY-MM-DD","type":"string"},"event_key":{"description":"Deterministic per renewal","type":"string"},"governing_state":{"description":"","type":"string"},"kind":{"description":"pawn_renewal | rollover","type":"string"},"new_grace_end_date":{"description":"YYYY-MM-DD","type":"string"},"new_loan_number":{"description":"rollover","type":"string"},"new_loan_uuid":{"description":"rollover","type":"string"},"new_maturity_date":{"description":"YYYY-MM-DD","type":"string"},"original_principal_cents":{"description":"","type":"integer"},"past_due_days":{"description":"","type":"integer"},"payoff_cents":{"description":"Cash payoff honored through payoff_honor_through_date","type":"integer"},"payoff_honor_through_date":{"description":"YYYY-MM-DD","type":"string"},"period_days":{"description":"","type":"integer"},"period_rate_percent":{"description":"","type":"string"},"principal_cents":{"description":"","type":"integer"},"prior_loan_uuid":{"description":"rollover","type":"string"},"prior_maturity_date":{"description":"YYYY-MM-DD","type":"string"},"renewal_number":{"description":"","type":"integer"},"renewals_max":{"description":"0 = unlimited","type":"integer"},"statement_instance_uuid":{"description":"","type":"string"},"statement_receipt_number":{"description":"RNWL-…","type":"string"},"statement_receipt_uuid":{"description":"","type":"string"},"statutory_text":{"description":"The governing state's clause","type":"string"},"ticket_number":{"description":"","type":"string"},"total_paid_to_date_cents":{"description":"","type":"integer"}},"type":"object"},"RepaymentPlan":{"properties":{"accept_by":{"description":"Last day the borrower may accept","type":"string"},"balance_at_offer_cents":{"description":"","type":"integer"},"borrower_uuid":{"description":"","type":"string"},"closed_at":{"description":"","type":"string"},"closed_reason":{"description":"","type":"string"},"defaulted_at":{"description":"date-time","type":"string"},"elected_at":{"description":"","type":"string"},"freeze_fees":{"type":"boolean"},"freeze_interest":{"type":"boolean"},"governing_state":{"description":"","type":"string"},"installments":{"description":"","type":"integer"},"loan_uuid":{"description":"","type":"string"},"offer_due_by":{"description":"Statutory offer deadline","type":"string"},"offered_at":{"description":"date-time","type":"string"},"plan_ends_on":{"description":"","type":"string"},"promise_plan_uuid":{"description":"Installment schedule once elected","type":"string"},"repayment_plan_uuid":{"description":"","type":"string"},"required":{"type":"boolean"},"status":{"description":"offered | active | declined | expired | completed | breached | cancelled","type":"string"},"term_days":{"description":"","type":"integer"},"trigger":{"description":"default | manual","type":"string"}},"type":"object"},"RepaymentPlanStanding":{"properties":{"eligibility":{"properties":{"accept_within_days":{"description":"","type":"integer"},"applies":{"type":"boolean"},"balance_cents":{"description":"","type":"integer"},"blocks":{"items":{"description":"","type":"string"},"type":"array"},"freeze_fees":{"type":"boolean"},"freeze_interest":{"type":"boolean"},"governing_state":{"description":"","type":"string"},"holds":{"items":{"description":"Adverse actions currently held","type":"string"},"type":"array"},"installments":{"description":"","type":"integer"},"offer_overdue":{"type":"boolean"},"offer_within_days":{"description":"","type":"integer"},"product_shape":{"description":"installment | title | single_pay | loc","type":"string"},"reason":{"description":"","type":"string"},"required":{"type":"boolean"},"term_days":{"description":"","type":"integer"}},"type":"object"},"history":{"items":{"$ref":"#/components/schemas/RepaymentPlan"},"type":"array"},"installments":{"items":{"type":"object"},"type":"array"},"open":{"$ref":"#/components/schemas/RepaymentPlan"}},"type":"object"},"RepossessionSale":{"properties":{"applied_cents":{"description":"Applied to the loan balance","type":"integer"},"deficiency_cents":{"description":"Still collectible (0 when non_recourse_settled)","type":"integer"},"net_proceeds_cents":{"description":"","type":"integer"},"non_recourse":{"description":"","type":"boolean"},"non_recourse_settled":{"description":"","type":"boolean"},"replayed":{"description":"","type":"boolean"},"repo_case_uuid":{"description":"","type":"string"},"sale_costs_cents":{"description":"","type":"integer"},"sale_date":{"description":"YYYY-MM-DD","type":"string"},"sale_proceeds_cents":{"description":"","type":"integer"},"status":{"description":"sold","type":"string"},"surplus_refund_due_cents":{"description":"Owed back to the borrower","type":"integer"},"surplus_refund_queued":{"description":"","type":"boolean"}},"type":"object"},"RepossessionStanding":{"properties":{"borrower_uuid":{"description":"","type":"string"},"can_record_sale":{"description":"","type":"boolean"},"case":{"description":"The repossession_case row: repo_case_uuid, status (cure_notice_sent | repo_ordered | repossessed | redemption_window | sale_scheduled | sold | redeemed | reinstated | cancelled), cure_notice_date, repo_ordered_at, repossessed_date, redemption_deadline_date, sale_scheduled_date, sale_date, repo_vendor, repo_cost_cents, storage_cost_cents, sale_proceeds_cents, sale_costs_cents, sale_settled_at, deficiency_cents, surplus_cents, notes","type":"object"},"deficiency_cents":{"description":"","type":"integer"},"has_open_case":{"description":"","type":"boolean"},"history":{"items":{"type":"object"},"type":"array"},"loan_number":{"description":"","type":"string"},"loan_uuid":{"description":"","type":"string"},"non_recourse":{"description":"State rule: deficiency written down at the sale","type":"boolean"},"record_sale_refusal":{"description":"","type":"string"},"redemption":{"description":"Payoff + chargeable repo/storage costs while the case is open","type":"object"},"sale_costs_cents":{"description":"","type":"integer"},"sale_proceeds_cents":{"description":"","type":"integer"},"store_uuid":{"description":"","type":"string"},"surplus_refund_due_cents":{"description":"","type":"integer"},"surplus_to_borrower":{"description":"State rule: overage queued for refund","type":"boolean"},"total_outstanding_cents":{"description":"","type":"integer"},"vehicle":{"$ref":"#/components/schemas/PawnVehicle"}},"type":"object"},"RolloverEligibility":{"properties":{"already_rolled":{"description":"The chain row when this loan was already rolled","type":"object"},"balance_carried_cents":{"description":"","type":"integer"},"chain_cost_cents":{"description":"Interest + fees charged across the chain so far","type":"integer"},"chain_origin_principal_cents":{"description":"","type":"integer"},"eligible":{"type":"boolean"},"fee_basis":{"description":"principal_plus_fees (default) | principal_only","type":"string"},"fee_cents":{"description":"Renewal term fee at the minimum paydown","type":"integer"},"governing_state":{"description":"","type":"string"},"is_final_roll":{"type":"boolean"},"lifetime_cost_cap_cents":{"description":"","type":"integer"},"lifetime_cost_cap_pct_bps":{"description":"","type":"integer"},"max_rollovers":{"description":"-1 not regulated, 0 prohibited, N cap","type":"integer"},"min_buydown_bps":{"description":"","type":"integer"},"min_buydown_cents":{"description":"Statutory principal paydown this roll must collect","type":"integer"},"min_buydown_from_roll":{"description":"Roll number the paydown rule starts at","type":"integer"},"principal_carried_cents":{"description":"","type":"integer"},"reason":{"description":"Set when eligible is false","type":"string"},"regulated":{"description":"false when no state rule set governs the loan","type":"boolean"},"remaining_rollovers":{"description":"-1 when not regulated","type":"integer"},"roll_number":{"description":"The number the next roll would carry","type":"integer"},"rolled_from":{"description":"The chain row when this loan is itself a renewal","type":"object"},"rule":{"description":"Refusing registry rule when eligible is false","type":"string"}},"type":"object"},"RolloverResult":{"properties":{"buydown_cents":{"description":"Principal paydown collected on the prior loan","type":"integer"},"cooling_off_set":{"description":"true when this was the state's final permitted roll","type":"boolean"},"fee_cents":{"description":"Term fee assessed on the renewal","type":"integer"},"loan":{"$ref":"#/components/schemas/Loan"},"new_loan_uuid":{"description":"","type":"string"},"principal_carried_cents":{"description":"","type":"integer"},"prior_loan_uuid":{"description":"","type":"string"},"roll_number":{"description":"","type":"integer"},"status":{"description":"rolled","type":"string"}},"type":"object"},"Status":{"properties":{"status":{"description":"","type":"string"}},"type":"object"},"Strategy":{"properties":{"company_uuid":{"description":"","type":"string"},"config_json":{"description":"","type":"string"},"dt_created":{"format":"date-time","type":"string"},"name":{"description":"","type":"string"},"strategy_uuid":{"description":"","type":"string"},"type":{"enum":["rule_based","round_robin","balance"],"type":"string"}},"type":"object"},"TitleFeeReceipt":{"properties":{"amount_cents":{"description":"","type":"integer"},"external_display":{"description":"","type":"string"},"external_ref":{"description":"","type":"string"},"external_source":{"description":"","type":"string"},"fees_outstanding_cents":{"description":"The loan's fees bucket after this request","type":"integer"},"loan_uuid":{"description":"","type":"string"},"method":{"description":"","type":"string"},"receipt":{"$ref":"#/components/schemas/CounterReceipt"},"receipted_at":{"format":"date-time","type":"string"},"replayed":{"description":"","type":"boolean"},"status":{"enum":["received","voided"],"type":"string"},"store_uuid":{"description":"","type":"string"},"transaction_uuid":{"description":"The receipt loan_transaction (type payment, fees bucket)","type":"string"},"voided_at":{"format":"date-time","type":"string"}},"type":"object"},"User":{"properties":{"approval_status":{"description":"","type":"string"},"default_company_uuid":{"description":"","type":"string"},"email":{"description":"","type":"string"},"enabled":{"description":"","type":"boolean"},"must_change_password":{"description":"","type":"boolean"},"name":{"description":"","type":"string"},"role":{"enum":["standard","company","super","teller"],"type":"string"},"user_uuid":{"description":"","type":"string"}},"type":"object"},"Webhook":{"properties":{"description":{"description":"","type":"string"},"dt_created":{"format":"date-time","type":"string"},"dt_updated":{"format":"date-time","type":"string"},"events":{"items":{"type":"string"},"type":"array"},"is_active":{"description":"","type":"boolean"},"url":{"description":"","type":"string"},"webhook_uuid":{"description":"","type":"string"}},"type":"object"},"WebhookDelivery":{"properties":{"attempts":{"description":"","type":"integer"},"delivery_uuid":{"description":"","type":"string"},"dt_created":{"format":"date-time","type":"string"},"event":{"description":"","type":"string"},"last_error":{"description":"","type":"string"},"next_retry_at":{"format":"date-time","type":"string"},"payload":{"description":"Full signed JSON body","type":"string"},"response_body":{"description":"","type":"string"},"response_code":{"description":"","type":"integer"},"status":{"enum":["pending","success","failed","retrying"],"type":"string"}},"type":"object"},"WebhookWithSecret":{"allOf":[{"$ref":"#/components/schemas/Webhook"},{"properties":{"secret":{"description":"HMAC-SHA256 signing secret — returned ONLY on create/rotate; store it now","type":"string"}},"type":"object"}]}},"securitySchemes":{"apiKey":{"description":"Company API key. A SECRET — server-side only. STORE SCOPING (register bridge): a key may be pinned to a set of stores (admin → API Keys → Store access; api_key.store_uuids). On staff-gated routes a scoped key may only act for a store in its set: any store_uuid the request names — the store_uuid query parameter, a top-level JSON store_uuid, or one nested one level down such as pay_and_renew.store_uuid — outside the set answers 409 store_not_permitted before the handler runs. A request that names no store passes (the loan's home store applies). Unscoped keys (the default, and every key created before scoping existed) are never checked. The acting X-User-UUID must additionally be a member of the tenant company, which is itself one of the key's companies — a user from another tenant answers 403.","in":"header","name":"X-API-Key","type":"apiKey"},"companyHeader":{"description":"Tenant scope; must be a company the key is scoped to (see /companies/allowed).","in":"header","name":"X-Company-UUID","type":"apiKey"},"userHeader":{"description":"Acting service-account user for staff-gated writes. Must belong to the tenant company and hold the route's RBAC permission; super-role users are refused on this surface.","in":"header","name":"X-User-UUID","type":"apiKey"}}},"info":{"description":"Server-to-server API at `/api/v1` for external systems: origination platforms, register bridges, loyalty engines, CRMs, and back-office automation.\n\n**Auth — three headers:**\n- `X-API-Key` (always): the company API key. A SECRET — server-side only, never in a browser or mobile app.\n- `X-Company-UUID` (all company-scoped endpoints): tenant scope; must be a company the key is scoped to.\n- `X-User-UUID` (every operation whose `security` lists `userHeader`): the acting service-account user for attribution and RBAC. That is every write — money movement, underwriting decisions and notes, origination, borrower / address / phone and lead intake, loan-type configuration, document uploads, lending infrastructure and loan assignment, simulation, webhooks, rewards, user management — plus the reads that return borrower-identifying or privileged data (payoff quotes, payment preview, cash-disbursement intents, rollover / repayment-plan / pawn / lien / repossession standing, hardship campaigns, rewards accounts, browsing history, webhook subscriptions, deny-list, staff users). Every such operation names the grant the acting user needs in `x-required-permission` (`a | b` = either). Operations whose `security` is only `apiKey` + `companyHeader` are key-scoped reads and the borrower-auth / password-reset flows. Must belong to the tenant company; `super`-role users are refused (they act only through interactive admin sessions).\n\nAll money is integer cents. Money POSTs accept an `Idempotency-Key` header so retries never double-post. Cross-tenant lookups answer 404, never 403 — existence is not confirmed across tenants. Errors use `{\"error\": message, \"code\": machine_code}`.\n\nThe borrower-facing Customer Portal API (`/api/v1/portal`, bearer tokens) is documented separately at /developers/api.\n\n**Web-only surfaces (no REST equivalent yet):** on the Store Operations register, customer create / update, Form 8300, cash-advance pay / release, vehicles, fiscal close, reason codes, check-issuer and money-transfer fee administration, store-credit grants, SSO and integration setup are operated from their web screens only. The LMS side of those counter flows IS reachable here — loans and payments, the held-check custody routes (`/loans/{loan_uuid}/held-checks`), pawn standing and renewal (`/loans/{loan_uuid}/pawn/renew`), lien and repossession standing — while the register-side records behind them (the counter's customer master, drawer and fiscal state) are not. Treat anything not listed in this document as web-only.","title":"Vector Loan — Partner / Staff API","version":"1.0.0"},"openapi":"3.0.3","paths":{"/auth/login":{"post":{"description":"Verifies staff credentials and returns the user record (no session/token is issued on this surface — use the returned user_uuid as X-User-UUID on staff-gated calls). Throttled per email.","requestBody":{"content":{"application/json":{"schema":{"properties":{"email":{"description":"","type":"string"},"password":{"description":"","type":"string"}},"required":["email","password"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"user":{"$ref":"#/components/schemas/User"}},"type":"object"}}},"description":"Authenticated"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid credentials"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Too many failed attempts"}},"security":[{"apiKey":[]}],"summary":"Staff login (email + password)","tags":["Auth"]}},"/borrower/auth/login":{"post":{"requestBody":{"content":{"application/json":{"schema":{"properties":{"email":{"description":"","type":"string"},"password":{"description":"","type":"string"}},"required":["email","password"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"2FA challenge"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid credentials"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"SSO required (code sso_required)"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Too many failed attempts"}},"security":[{"apiKey":[],"companyHeader":[]}],"summary":"Borrower login step 1 — issues a 2FA challenge","tags":["Borrower Auth"]}},"/borrower/auth/resend":{"post":{"description":"Rate-limited 5/(company,IP)/10min.","requestBody":{"content":{"application/json":{"schema":{"properties":{"challenge_uuid":{"description":"","type":"string"}},"required":["challenge_uuid"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Resent"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rate limited"}},"security":[{"apiKey":[],"companyHeader":[]}],"summary":"Resend the borrower 2FA code","tags":["Borrower Auth"]}},"/borrower/auth/verify":{"post":{"description":"Rate-limited 20/(company,IP)/10min.","requestBody":{"content":{"application/json":{"schema":{"properties":{"challenge_uuid":{"description":"","type":"string"},"code":{"description":"","type":"string"}},"required":["challenge_uuid","code"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"status":{"description":"","type":"string"}},"type":"object"}}},"description":"Verified"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Verification failed"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rate limited"}},"security":[{"apiKey":[],"companyHeader":[]}],"summary":"Borrower login step 2 — verify the 2FA code","tags":["Borrower Auth"]}},"/borrower/password/reset/cancel":{"post":{"requestBody":{"content":{"application/json":{"schema":{"properties":{"token":{"description":"","type":"string"}},"required":["token"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Canceled"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid or expired token"}},"security":[{"apiKey":[]}],"summary":"Cancel a borrower reset token","tags":["Borrower Auth"]}},"/borrower/password/reset/confirm":{"post":{"requestBody":{"content":{"application/json":{"schema":{"properties":{"new_password":{"description":"","type":"string"},"token":{"description":"","type":"string"}},"required":["token","new_password"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Password updated"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid or expired token"}},"security":[{"apiKey":[]}],"summary":"Confirm a borrower password reset","tags":["Borrower Auth"]}},"/borrower/password/reset/request":{"post":{"description":"Always 200 (no enumeration). Rate-limited 5/(company,IP)/15min.","requestBody":{"content":{"application/json":{"schema":{"properties":{"email":{"description":"","type":"string"}},"required":["email"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Accepted"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rate limited"}},"security":[{"apiKey":[],"companyHeader":[]}],"summary":"Request a borrower password reset","tags":["Borrower Auth"]}},"/borrower/password/reset/validate":{"post":{"requestBody":{"content":{"application/json":{"schema":{"properties":{"token":{"description":"","type":"string"}},"required":["token"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Token status"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid payload"}},"security":[{"apiKey":[]}],"summary":"Inspect a borrower reset token","tags":["Borrower Auth"]}},"/borrowers":{"get":{"parameters":[{"description":"Name / email / phone search","in":"query","name":"q","required":false,"schema":{"type":"string"}},{"description":"1-1000, default 50","in":"query","name":"limit","required":false,"schema":{"type":"integer"}},{"description":"","in":"query","name":"offset","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Borrower"},"type":"array"}}},"description":"Borrowers"}},"summary":"List / search borrowers","tags":["Borrowers"]},"post":{"requestBody":{"content":{"application/json":{"schema":{"properties":{"email":{"description":"Max 200","type":"string"},"external_id":{"description":"Your system's unique id for this borrower","type":"string"},"first_name":{"description":"Max 100","type":"string"},"home_phone":{"description":"Max 50","type":"string"},"last_name":{"description":"Max 100","type":"string"},"mobile_phone":{"description":"Max 50","type":"string"},"work_phone":{"description":"Max 50","type":"string"}},"required":["first_name","last_name","email"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Borrower"}}},"description":"Created"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid body or field length exceeded"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Create a borrower","tags":["Borrowers"],"x-required-permission":"borrowers:create"}},"/borrowers/{borrower_uuid}":{"get":{"parameters":[{"description":"Borrower UUID","in":"path","name":"borrower_uuid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Borrower"}}},"description":"Borrower"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found (or another tenant's)"}},"summary":"Get a borrower","tags":["Borrowers"]}},"/borrowers/{borrower_uuid}/addresses":{"get":{"parameters":[{"description":"Borrower UUID","in":"path","name":"borrower_uuid","required":true,"schema":{"type":"string"}},{"description":"\"1\" to include retired addresses","in":"query","name":"history","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"addresses":{"items":{"$ref":"#/components/schemas/Address"},"type":"array"}},"type":"object"}}},"description":"Addresses"}},"summary":"List addresses","tags":["Borrowers"]},"post":{"parameters":[{"description":"Borrower UUID","in":"path","name":"borrower_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"address_line1":{"description":"","type":"string"},"address_line2":{"description":"","type":"string"},"address_type":{"enum":["home","mailing","previous","work","other"],"type":"string"},"bad":{"description":"","type":"boolean"},"city":{"description":"","type":"string"},"country":{"description":"","type":"string"},"do_not_mail":{"description":"","type":"boolean"},"landlord_name":{"description":"","type":"string"},"landlord_phone":{"description":"","type":"string"},"make_primary":{"description":"true retires the prior primary","type":"boolean"},"monthly_payment_cents":{"description":"","type":"integer"},"ownership":{"enum":["rent","own","other"],"type":"string"},"state":{"description":"","type":"string"},"zip_code":{"description":"","type":"string"}},"required":["address_type"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Address"}}},"description":"Created"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid or empty address"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Borrower not found"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Add an address","tags":["Borrowers"],"x-required-permission":"borrowers:edit"}},"/borrowers/{borrower_uuid}/addresses/{address_uuid}":{"delete":{"parameters":[{"description":"","in":"path","name":"borrower_uuid","required":true,"schema":{"type":"string"}},{"description":"","in":"path","name":"address_uuid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Status"}}},"description":"Deleted"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Delete an address (hard delete — prefer retire)","tags":["Borrowers"],"x-required-permission":"borrowers:edit"},"put":{"parameters":[{"description":"","in":"path","name":"borrower_uuid","required":true,"schema":{"type":"string"}},{"description":"","in":"path","name":"address_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"description":"Same fields as create (make_primary ignored)","type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Status"}}},"description":"Updated"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Update an address","tags":["Borrowers"],"x-required-permission":"borrowers:edit"}},"/borrowers/{borrower_uuid}/addresses/{address_uuid}/primary":{"post":{"parameters":[{"description":"","in":"path","name":"borrower_uuid","required":true,"schema":{"type":"string"}},{"description":"","in":"path","name":"address_uuid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Status"}}},"description":"Primary set"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Make an address primary (end-dates the prior primary)","tags":["Borrowers"],"x-required-permission":"borrowers:edit"}},"/borrowers/{borrower_uuid}/addresses/{address_uuid}/retire":{"post":{"parameters":[{"description":"","in":"path","name":"borrower_uuid","required":true,"schema":{"type":"string"}},{"description":"","in":"path","name":"address_uuid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Status"}}},"description":"Retired"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Retire an address (end-date, keep history)","tags":["Borrowers"],"x-required-permission":"borrowers:edit"}},"/borrowers/{borrower_uuid}/browsing-history":{"get":{"description":"Pages the customer viewed in the borrower portal, within the retention window. Paths carry only allow-listed identifiers (uuid, year); every other path parameter is [redacted] and query strings are never stored. impersonated_by is set when a staff member was impersonating the customer. Empty with enabled=false when the feature is off.","parameters":[{"description":"","in":"path","name":"borrower_uuid","required":true,"schema":{"type":"string"}},{"description":"Default 50, max 500","in":"query","name":"limit","required":false,"schema":{"type":"integer"}},{"description":"","in":"query","name":"offset","required":false,"schema":{"type":"integer"}},{"description":"1 to include partial (HTMX) loads","in":"query","name":"include_partial","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"enabled":{"type":"boolean"},"items":{"items":{"properties":{"dt_viewed":{"format":"date-time","type":"string"},"duration_ms":{"description":"","type":"integer"},"entity_uuid":{"description":"","type":"string"},"impersonated_by":{"description":"staff user uuid or empty","type":"string"},"ip_address":{"description":"","type":"string"},"is_htmx":{"type":"boolean"},"page_key":{"description":"","type":"string"},"path":{"description":"redacted path","type":"string"},"referer_path":{"description":"","type":"string"},"route":{"description":"gin route pattern","type":"string"},"status_code":{"description":"","type":"integer"},"user_agent":{"description":"","type":"string"},"view_uuid":{"description":"","type":"string"}},"type":"object"},"type":"array"},"limit":{"description":"","type":"integer"},"offset":{"description":"","type":"integer"},"total":{"description":"","type":"integer"}},"type":"object"}}},"description":"History page"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Borrower not found (or another tenant's)"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"A customer's portal browsing history (newest first)","tags":["Borrowers"],"x-required-permission":"borrowers:view"}},"/borrowers/{borrower_uuid}/phones":{"get":{"parameters":[{"description":"","in":"path","name":"borrower_uuid","required":true,"schema":{"type":"string"}},{"description":"\"1\" to include retired phones","in":"query","name":"history","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"phones":{"items":{"$ref":"#/components/schemas/Phone"},"type":"array"}},"type":"object"}}},"description":"Phones"}},"summary":"List phones","tags":["Borrowers"]},"post":{"parameters":[{"description":"","in":"path","name":"borrower_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"bad":{"description":"","type":"boolean"},"do_not_call":{"description":"","type":"boolean"},"do_not_text":{"description":"","type":"boolean"},"label":{"description":"","type":"string"},"number":{"description":"","type":"string"},"phone_type":{"enum":["mobile","home","work","other"],"type":"string"}},"required":["phone_type","number"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Phone"}}},"description":"Created"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Number required"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Borrower not found"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Add a phone","tags":["Borrowers"],"x-required-permission":"borrowers:edit"}},"/borrowers/{borrower_uuid}/phones/{phone_uuid}":{"delete":{"parameters":[{"description":"","in":"path","name":"borrower_uuid","required":true,"schema":{"type":"string"}},{"description":"","in":"path","name":"phone_uuid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Status"}}},"description":"Deleted"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Delete a phone (hard delete — prefer retire)","tags":["Borrowers"],"x-required-permission":"borrowers:edit"},"put":{"parameters":[{"description":"","in":"path","name":"borrower_uuid","required":true,"schema":{"type":"string"}},{"description":"","in":"path","name":"phone_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"description":"Same fields as create","type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Status"}}},"description":"Updated"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Update a phone","tags":["Borrowers"],"x-required-permission":"borrowers:edit"}},"/borrowers/{borrower_uuid}/phones/{phone_uuid}/retire":{"post":{"parameters":[{"description":"","in":"path","name":"borrower_uuid","required":true,"schema":{"type":"string"}},{"description":"","in":"path","name":"phone_uuid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Status"}}},"description":"Retired"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Retire a phone","tags":["Borrowers"],"x-required-permission":"borrowers:edit"}},"/cash-disbursements":{"get":{"description":"Cash payouts the loan platform has asked the store register to make (loan draws and opening advances on companies whose cash tender source is the register). Oldest first. Nothing is posted to a loan until the register confirms a payout paid. store_uuid matches intents stamped for that store AND store-less intents on loans whose home store it is (a payout requested without a store is stamped with the loan's home store at request time). TIME CONTRACT: a pending payout is claimable for 4 HOURS after it is requested (then expired); a claimed payout stays payable for 24 HOURS after the claim — the register's /paid retry schedule must keep retrying inside that window (hold, never dead-letter), because what is still claimed when the window closes is parked needs_reconcile with an ops alert and needs a human (POST .../resolve).","parameters":[{"description":"Only intents for this store","in":"query","name":"store_uuid","required":false,"schema":{"type":"string"}},{"description":"cash | money_transfer","in":"query","name":"rail","required":false,"schema":{"type":"string"}},{"description":"moneygram | western_union (money_transfer intents)","in":"query","name":"network","required":false,"schema":{"type":"string"}},{"description":"1-500, default 100","in":"query","name":"limit","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"cash_disbursements":{"items":{"$ref":"#/components/schemas/CashDisbursement"},"type":"array"}},"type":"object"}}},"description":"Open cash disbursements"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"invalid_rail / invalid_network"},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Cash disbursements not configured"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Open cash / money-transfer payouts for the register","tags":["Cash disbursements"],"x-required-permission":"loans:view | cash_disbursements:view"}},"/cash-disbursements/{uuid}":{"get":{"parameters":[{"description":"","in":"path","name":"uuid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CashDisbursement"}}},"description":"Cash disbursement"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"One cash payout","tags":["Cash disbursements"],"x-required-permission":"loans:view | cash_disbursements:view"}},"/cash-disbursements/{uuid}/cancel":{"post":{"description":"Cancels a pending payout, or a claimed payout when register_uuid is the register that claimed it; nothing was posted so nothing is reversed. Idempotent. A paid payout cannot be canceled (409 closed). A claimed payout canceled WITHOUT register_uuid (or by another register) answers 409 claimed_by_register — permanent: send the claiming register's uuid.","parameters":[{"description":"","in":"path","name":"uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"reason":{"description":"Max 255","type":"string"},"register_uuid":{"description":"Required to cancel a claimed payout: must be the claiming register","type":"string"}},"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CashDisbursement"}}},"description":"Canceled"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"closed, or claimed_by_register (another register holds it, or register_uuid omitted)"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"internal_error: temporary failure, retry"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Abandon an unpaid payout","tags":["Cash disbursements"],"x-required-permission":"cash_disbursements:create"}},"/cash-disbursements/{uuid}/claim":{"post":{"description":"Marks the payout as being paid out by this register and re-runs the loan's draw pre-flight (a hold or limit change since the request refuses the claim and leaves it pending). Idempotent for the same register_uuid; another register gets 409 claim_conflict.","parameters":[{"description":"","in":"path","name":"uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"drawer_session_uuid":{"description":"Open drawer session","type":"string"},"register_uuid":{"description":"The register taking the payout","type":"string"},"teller":{"properties":{"external_id":{"description":"Max 64","type":"string"},"name":{"description":"Max 128","type":"string"},"subject":{"description":"Max 255: the teller's identity-provider subject; persisted (teller_subject) and resolvable to the LMS staff user","type":"string"}},"type":"object"},"when":{"description":"The register's business date (YYYY-MM-DD) or RFC3339 instant; persisted as business_date","type":"string"}},"required":["register_uuid"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CashDisbursement"}}},"description":"Claimed"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"claim_conflict (another register) or closed (terminal)"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"draw_refused: the loan cannot take this draw now"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Claim a payout for one register","tags":["Cash disbursements"],"x-required-permission":"cash_disbursements:create"}},"/cash-disbursements/{uuid}/paid":{"post":{"description":"Posts the loan draw exactly once. Safe to replay: the same payout returns the same loan_transaction_uuid. A payout must be claimed first (409 not_claimed) and confirmed within 24 HOURS of the claim (the claimed TTL; after it the sweeper parks the payout needs_reconcile — see POST .../resolve). If the draw is refused after the cash is gone the payout becomes needs_reconcile, an ops alert fires, and the response is 500 needs_reconcile: do not retry; a human (or the register, via resolve) reconciles the drawer against the loan. teller{external_id,name,subject} and when (business date) are persisted on the payout; a recent `when` (not in the future, within 72h) is also the draw's effective instant so a late-night ring posts on its business day.","parameters":[{"description":"","in":"path","name":"uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"network_fee_cents":{"description":"money_transfer rail: the network's send fee the register paid; booked as a company expense, or charged to the loan when the product's funding.money_transfer_fee_payer is borrower","type":"integer"},"network_ref":{"description":"money_transfer rail: the network's reference (MTCN), max 128 — required (400 network_ref_required)","type":"string"},"receipt_number":{"description":"The register's BARE receipt identifier: letters, digits, - _ . / (max 64, no spaces) — 422 invalid_receipt_number otherwise","type":"string"},"register_txn_ref":{"description":"The register's cash transaction id (max 128)","type":"string"},"register_uuid":{"description":"Must match the claiming register when set","type":"string"},"teller":{"properties":{"external_id":{"description":"Max 64","type":"string"},"name":{"description":"Max 128","type":"string"},"subject":{"description":"Max 255","type":"string"}},"type":"object"},"when":{"description":"The register's business date (YYYY-MM-DD) or RFC3339 instant","type":"string"}},"required":["register_txn_ref"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"cash_disbursement":{"$ref":"#/components/schemas/CashDisbursement"},"loan":{"$ref":"#/components/schemas/Loan"},"loan_transaction_uuid":{"description":"The posted draw row","type":"string"}},"type":"object"}}},"description":"Paid and posted"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"network_ref_required / invalid_network_fee"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"not_claimed, claim_conflict or closed"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"needs_reconcile (cash paid, draw refused or payout already closed under the register; ops alerted) or internal_error (temporary failure, retry)"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"persist_failed: outcome decided but not saved; retry the same call to converge | fee_persist_failed: the draw is posted but the network fee is not booked yet, retry the same request | rail_unavailable: migration 20261009200641 pending"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Confirm the cash left the drawer","tags":["Cash disbursements"],"x-required-permission":"cash_disbursements:create"}},"/cash-disbursements/{uuid}/resolve":{"post":{"description":"Settles a needs_reconcile payout (cash paid but the draw refused, a paid confirmation that arrived after the payout closed, or a claimed payout that expired unconfirmed). outcome=paid: the cash DID leave the drawer — the draw is posted exactly once under the payout's ledger id (a draw already posted is adopted) and the payout is marked paid. outcome=void: the cash never left (or came back) — the payout closes as canceled with nothing posted and an opening advance is re-armed. reason is required and persisted (resolution_reason). A payout claimed by a register may only be resolved by that register from the API (409 claim_conflict); operators resolve from the loan page. Idempotent: a replay of the same resolution answers 200.","parameters":[{"description":"","in":"path","name":"uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"outcome":{"enum":["paid","void"],"type":"string"},"reason":{"description":"Max 255; why it is settled this way","type":"string"},"register_uuid":{"description":"The register asking (must be the claiming register when the payout was claimed)","type":"string"},"teller":{"properties":{"external_id":{"description":"Max 64","type":"string"},"name":{"description":"Max 128","type":"string"},"subject":{"description":"Max 255","type":"string"}},"type":"object"}},"required":["outcome","reason"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"cash_disbursement":{"$ref":"#/components/schemas/CashDisbursement"},"loan_transaction_uuid":{"description":"The posted draw (outcome paid)","type":"string"}},"type":"object"}}},"description":"Resolved"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"invalid_resolution"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"not_reconcilable (not awaiting reconciliation) / claim_conflict"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"draw_refused: the ledger still refuses the draw; the payout stays needs_reconcile"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"persist_failed / fee_persist_failed: retry the same request"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Settle a payout parked for reconciliation","tags":["Cash disbursements"],"x-required-permission":"cash_disbursements:create"}},"/cash-disbursements/{uuid}/unclaim":{"post":{"description":"Returns a CLAIMED payout to pending so any register can claim it again — for a claim the register cannot fulfil after taking it (drawer short, manager declined, customer left). Nothing was posted, nothing is reversed. register_uuid must be the claiming register (409 claim_conflict otherwise). Idempotent: a pending payout answers 200 with its row. A payout already confirmed paid (or paying / terminal) answers 409 closed — cash cannot be un-paid; use POST .../resolve for a payout parked needs_reconcile. The pending window is re-armed (4h from the release). Same contract at POST /loans/{loan_uuid}/disburse-draw/{uuid}/unclaim.","parameters":[{"description":"","in":"path","name":"uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"posted_by":{"description":"Teller attribution (alias: teller)","properties":{"external_id":{"description":"Max 64","type":"string"},"name":{"description":"Max 128","type":"string"},"subject":{"description":"Max 255","type":"string"}},"type":"object"},"reason":{"description":"Max 255; why the claim is released (kept in the activity log)","type":"string"},"register_uuid":{"description":"The register releasing the claim: must be the claiming register","type":"string"}},"required":["register_uuid"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CashDisbursement"}}},"description":"Released (status pending)"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"register_required"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"claim_conflict (another register holds it) or closed (paid / paying / terminal)"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"internal_error: temporary failure, retry"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Release this register's claim (back to pending)","tags":["Cash disbursements"],"x-required-permission":"cash_disbursements:create"}},"/companies/allowed":{"get":{"description":"Use the returned company_uuid values as X-Company-UUID on company-scoped calls.","responses":{"200":{"content":{"application/json":{"schema":{"items":{"properties":{"company_uuid":{"description":"","type":"string"},"loan_number_prefix":{"description":"","type":"string"},"name":{"description":"","type":"string"},"slug":{"description":"","type":"string"}},"type":"object"},"type":"array"}}},"description":"Companies"},"401":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Missing or invalid API key"}},"security":[{"apiKey":[]}],"summary":"Companies this API key can access","tags":["Discovery"]}},"/deny-list":{"delete":{"description":"Soft-removes the company entry matching the supplied identity (same field rules as POST). Global-tier entries cannot be removed through the API.","requestBody":{"content":{"application/json":{"schema":{"properties":{"device_id":{"description":"","type":"string"},"dob":{"description":"YYYY-MM-DD","type":"string"},"email":{"description":"","type":"string"},"first_name":{"description":"","type":"string"},"last_name":{"description":"","type":"string"},"match_type":{"enum":["ssn_hash","name_dob","email","phone","device"],"type":"string"},"phone":{"description":"","type":"string"},"ssn":{"description":"","type":"string"},"ssn_hash":{"description":"","type":"string"}},"required":["match_type"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Status"}}},"description":"Removed"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid body / match_type / missing identity value"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Remove a deny-list entry","tags":["Deny List"],"x-required-permission":"deny_list:delete"},"get":{"description":"The company's deny-list entries plus the platform-wide (global) tier read-only, newest first, capped at 200. Entries never carry the blind-index hash or plaintext identity — only match_last4. The employee-lending block list is a separate surface (/employee-block) and is never listed here.","parameters":[{"description":"ssn_hash | name_dob | email | phone | device (employee_ssn is refused)","in":"query","name":"match_type","required":false,"schema":{"type":"string"}},{"description":"false includes removed entries (default: active only)","in":"query","name":"active","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"entries":{"items":{"$ref":"#/components/schemas/DenyListEntry"},"type":"array"},"total":{"description":"","type":"integer"}},"type":"object"}}},"description":"Entries"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid match_type"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"List deny-list entries","tags":["Deny List"],"x-required-permission":"deny_list:view"},"post":{"description":"Idempotent company-scoped push: supply exactly the raw identity field(s) for match_type (ssn for ssn_hash — or a precomputed company ssn_hash; first_name + last_name + dob for name_dob; email; phone; device_id). Values are hashed and the PII encrypted server-side; a re-push of the same identity refreshes the row. Source is recorded as api with the pushing key.","requestBody":{"content":{"application/json":{"schema":{"properties":{"action":{"description":"Default decline","enum":["decline","refer"],"type":"string"},"device_id":{"description":"match_type=device","type":"string"},"dob":{"description":"YYYY-MM-DD (name_dob)","type":"string"},"email":{"description":"match_type=email","type":"string"},"first_name":{"description":"name_dob","type":"string"},"last_name":{"description":"name_dob","type":"string"},"match_type":{"enum":["ssn_hash","name_dob","email","phone","device"],"type":"string"},"phone":{"description":"match_type=phone","type":"string"},"reason":{"description":"","type":"string"},"ssn":{"description":"Nine digits (match_type=ssn_hash)","type":"string"},"ssn_hash":{"description":"Precomputed company blind index (alternative to ssn)","type":"string"}},"required":["match_type"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DenyListEntry"}}},"description":"Entry"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid body, match_type, action, dob, or missing identity value"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Add or refresh a deny-list entry","tags":["Deny List"],"x-required-permission":"deny_list:create"}},"/employee-block/check":{"post":{"description":"A membership oracle, so it is rate-capped per tenant + IP (60 calls / 10 minutes) and every call is audited with the actor, key and outcome (never the SSN). Answers employee=true with the source that matched (none when not a hit).","requestBody":{"content":{"application/json":{"schema":{"properties":{"ssn":{"description":"Nine digits","type":"string"}},"required":["ssn"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"employee":{"description":"","type":"boolean"},"result":{"description":"hit | clear","type":"string"},"source":{"description":"Matching list, or none","type":"string"}},"type":"object"}}},"description":"Result"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"ssn must be nine digits (invalid_ssn)"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rate limited"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Employee lending block not configured or settings unavailable"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Check whether an SSN is on the employee block list","tags":["Employee Block"],"x-required-permission":"underwriting:edit"}},"/employee-block/entries":{"delete":{"description":"Removes the entry for an ssn, or every entry carrying an HR reference. One of ssn / reference is required.","requestBody":{"content":{"application/json":{"schema":{"properties":{"reference":{"description":"HR / payroll reference","type":"string"},"ssn":{"description":"Nine digits","type":"string"}},"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"removed":{"description":"","type":"integer"}},"type":"object"}}},"description":"Result"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"ssn or reference required / invalid ssn"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Employee lending block not configured"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Remove employee block entries","tags":["Employee Block"],"x-required-permission":"underwriting:edit"},"get":{"description":"The company's employee block list (SSN last four, names, employer, HR reference). Pages by limit/offset; last4 filters by SSN last four. 503 when the employee lending block is not configured for the deployment.","parameters":[{"description":"SSN last four","in":"query","name":"last4","required":false,"schema":{"type":"string"}},{"description":"Default 50","in":"query","name":"limit","required":false,"schema":{"type":"integer"}},{"description":"","in":"query","name":"offset","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"entries":{"items":{"$ref":"#/components/schemas/EmployeeBlockEntry"},"type":"array"},"next_offset":{"description":"Offset of the next page; absent on the last page","type":"integer"},"total":{"description":"","type":"integer"}},"type":"object"}}},"description":"Entries page"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Employee lending block not configured (employee_block_unavailable)"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"List the employee lending block list","tags":["Employee Block"],"x-required-permission":"underwriting:edit"},"put":{"description":"An HR system keeps the list current. mode=merge (default) adds or refreshes the supplied entries; mode=replace additionally removes every existing entry not in the payload. At most 5000 entries per call and within the 1 MiB JSON body limit every company-scoped route enforces; every ssn must be nine digits (the SSN is hashed and never stored in clear).","requestBody":{"content":{"application/json":{"schema":{"properties":{"entries":{"items":{"properties":{"employer":{"description":"","type":"string"},"first_name":{"description":"","type":"string"},"last_name":{"description":"","type":"string"},"note":{"description":"","type":"string"},"reference":{"description":"HR / payroll reference","type":"string"},"ssn":{"description":"Nine digits","type":"string"}},"required":["ssn"],"type":"object"},"type":"array"},"mode":{"description":"Default merge","enum":["merge","replace"],"type":"string"}},"required":["entries"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"added":{"description":"","type":"integer"},"removed":{"description":"replace mode only","type":"integer"},"total":{"description":"Entries after the push","type":"integer"},"updated":{"description":"","type":"integer"}},"type":"object"}}},"description":"Result"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid body / mode / ssn (invalid_ssn carries the offending indexes; a streamed body over 1 MiB may surface as invalid_body)"},"413":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"More than 5000 entries (too_many_entries) or a body over 1 MiB (request_too_large)"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Employee lending block not configured"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Push the employee block list (merge or replace)","tags":["Employee Block"],"x-required-permission":"underwriting:edit"}},"/hardship-campaigns":{"get":{"parameters":[{"description":"Default 50","in":"query","name":"limit","required":false,"schema":{"type":"integer"}},{"description":"","in":"query","name":"offset","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"campaigns":{"items":{"$ref":"#/components/schemas/HardshipCampaign"},"type":"array"}},"type":"object"}}},"description":"Campaigns"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"List hardship / disaster campaigns","tags":["Collections"],"x-required-permission":"collections:view"},"post":{"description":"A campaign enrolls loans by borrower region (states, 5-digit zips or 3-digit prefixes; county lists are recorded but do not narrow eligibility) and/or an uploaded loan list, and applies Skip-a-Pay (skip_payments periods or extend_days), Forgive-a-Pay (forgive_per_payment_cents), and a late-fee / interest waiver window (effective_from..effective_to). enrollment=auto applies to every eligible loan; opt_in invites borrowers and applies when they accept. Preview materializes the eligible set; apply routes through the company's hardship_campaign maker-checker policy.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HardshipCampaignInput"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"properties":{"campaign":{"$ref":"#/components/schemas/HardshipCampaign"}},"type":"object"}}},"description":"Draft campaign"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"invalid_campaign"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Create a draft hardship / disaster campaign","tags":["Collections"],"x-required-permission":"collections:edit"}},"/hardship-campaigns/{campaign_uuid}":{"get":{"parameters":[{"description":"Campaign UUID","in":"path","name":"campaign_uuid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"campaign":{"$ref":"#/components/schemas/HardshipCampaign"},"enrollments":{"items":{"$ref":"#/components/schemas/HardshipCampaignEnrollment"},"type":"array"}},"type":"object"}}},"description":"Campaign"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"campaign_not_found"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Campaign with its enrollments","tags":["Collections"],"x-required-permission":"collections:view"}},"/hardship-campaigns/{campaign_uuid}/apply":{"post":{"description":"Requests application through the hardship_campaign approval policy: 202 pending_approval and the approvals queue applies it on sign-off, or 200 with the per-loan report when the policy resolves to inline. Idempotent per loan (deterministic deferral / credit keys); a re-run heals failed rows.","parameters":[{"description":"Campaign UUID","in":"path","name":"campaign_uuid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"campaign":{"$ref":"#/components/schemas/HardshipCampaign"},"report":{"type":"object"},"status":{"description":"","type":"string"}},"type":"object"}}},"description":"Applied"},"202":{"content":{"application/json":{"schema":{"properties":{"approval_uuid":{"description":"","type":"string"},"status":{"description":"pending_approval","type":"string"}},"type":"object"}}},"description":"Pending approval"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"campaign_not_found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"campaign_not_draft"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Apply the campaign (maker-checker)","tags":["Collections"],"x-required-permission":"collections:edit"}},"/hardship-campaigns/{campaign_uuid}/preview":{"post":{"parameters":[{"description":"Campaign UUID","in":"path","name":"campaign_uuid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"eligible":{"description":"","type":"integer"},"enrollments":{"items":{"$ref":"#/components/schemas/HardshipCampaignEnrollment"},"type":"array"},"skip_notes":{"items":{"description":"","type":"string"},"type":"array"},"skipped":{"description":"","type":"integer"}},"type":"object"}}},"description":"Preview"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"campaign_not_found"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Materialize the eligible loans (no money moves)","tags":["Collections"],"x-required-permission":"collections:edit"}},"/leads":{"post":{"description":"Decides a posted lead against the company's products: a product accepts the lead when its marketplace policy is enabled for the applicant's state, the requested amount is inside the product's marketplace (or loan amount) range, the quoted price is at or under the product's cap, and the product is sold online. affiliate_id is the affiliate's public code from the company's affiliate registry; the resulting application and loan are attributed to it. An accepted lead returns a one-time redirect_url (24h) into the online application, prefilled from the lead. Reposting the same external_lead_id returns the original decision with replayed=true and never re-prices. Rate limit: 600 posts per minute per API key (429 with Retry-After).","requestBody":{"content":{"application/json":{"schema":{"properties":{"affiliate_id":{"description":"Affiliate code (max 64)","type":"string"},"applicant":{"properties":{"dob":{"description":"YYYY-MM-DD, optional","type":"string"},"email":{"description":"","type":"string"},"first_name":{"description":"","type":"string"},"last_name":{"description":"","type":"string"},"monthly_income_cents":{"description":"optional","type":"integer"},"phone":{"description":"10-digit US","type":"string"},"requested_amount_cents":{"description":"\u003e 0","type":"integer"},"state":{"description":"Two-letter","type":"string"},"zip":{"description":"","type":"string"}},"required":["first_name","last_name","email","phone","state","requested_amount_cents"],"type":"object"},"consent":{"properties":{"credit":{"type":"boolean"},"tcpa":{"type":"boolean"}},"required":["tcpa","credit"],"type":"object"},"external_lead_id":{"description":"The marketplace's lead id (max 128); the replay key","type":"string"},"loan_type_uuid":{"description":"Optional: post to one product; otherwise the first accepting product is chosen","type":"string"},"price_cents":{"description":"The lead price quoted, optional; compared with the product's marketplace.max_price_cents","type":"integer"}},"required":["affiliate_id","external_lead_id","applicant","consent"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeadDecision"}}},"description":"Replayed: the same external_lead_id was posted before; the original decision (an accepted, unused lead gets a fresh redirect_url)"},"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeadDecision"}}},"description":"Decided"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"affiliate_unknown | invalid_lead"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"rate_limited"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Post a lead from a lead marketplace / ping tree","tags":["Leads"],"x-required-permission":"borrowers:create"}},"/lender-pools":{"get":{"parameters":[{"description":"Name or facility-code search","in":"query","name":"q","required":false,"schema":{"type":"string"}},{"description":"Default 50","in":"query","name":"limit","required":false,"schema":{"type":"integer"}},{"description":"","in":"query","name":"offset","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Pool"},"type":"array"}}},"description":"Pools"}},"summary":"List pools and facilities","tags":["Lending"]},"post":{"description":"A pool with kind=credit_facility carries a commitment, advance rate, eligibility rules and concentration limits; every assignment into it is checked against them and refused with a named rule (see POST /loans/{loan_uuid}/assign-lender). A plain pool (kind omitted or lender_pool) must not carry facility settings.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PoolInput"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Pool"}}},"description":"Created"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Create failed"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"facility_invalid — the detail names the bad field or rule"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Create a lender pool or credit facility","tags":["Lending"],"x-required-permission":"lending:create"}},"/lender-pools/{pool_uuid}":{"get":{"parameters":[{"description":"","in":"path","name":"pool_uuid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Pool"}}},"description":"Pool"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"pool_not_found"}},"summary":"Get a pool","tags":["Lending"]},"put":{"parameters":[{"description":"","in":"path","name":"pool_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PoolInput"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Status"}}},"description":"Updated"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Update failed"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"pool_not_found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"facility_invalid"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Update a pool (omitted facility fields keep their value)","tags":["Lending"],"x-required-permission":"lending:edit"}},"/lender-pools/{pool_uuid}/facility":{"get":{"description":"Outstanding principal across active assignments, the share that still passes the eligibility rules today, the borrowing base (eligible × advance rate, capped at the commitment), the shortfall (outstanding above the borrowing base), utilization, and concentration buckets by state / product / channel / borrower with any breached limits. Shares are measured against the larger of outstanding and the commitment.","parameters":[{"description":"","in":"path","name":"pool_uuid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FacilitySnapshot"}}},"description":"Snapshot"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"facility_not_found — the pool is not a credit facility"}},"summary":"Credit-facility position as of now","tags":["Lending"]}},"/lender-pools/{pool_uuid}/loan-tape.csv":{"get":{"description":"One row per active assignment: facility_code, loan_number, loan_uuid, borrower_uuid, borrower_state, product, product_base_type_uuid, channel, collateral_type, lender_uuid, principal_outstanding, apr_percent, status, dt_opened, delinquent_since, days_past_due, dt_assigned, restructured, eligible. Every download is written to the PII-export audit log under the API key.","parameters":[{"description":"","in":"path","name":"pool_uuid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"text/csv"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"facility_not_found"}},"summary":"Facility loan tape (CSV)","tags":["Lending"]}},"/lender-pools/{pool_uuid}/members":{"get":{"parameters":[{"description":"","in":"path","name":"pool_uuid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"properties":{"lender_uuid":{"description":"","type":"string"},"max_allocation_cents":{"description":"","type":"integer"},"order_index":{"description":"","type":"integer"},"weight_bps":{"description":"","type":"integer"}},"type":"object"},"type":"array"}}},"description":"Members"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"pool_not_found"}},"summary":"List pool members","tags":["Lending"]},"put":{"parameters":[{"description":"","in":"path","name":"pool_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"members":{"items":{"properties":{"lender_uuid":{"description":"","type":"string"},"max_allocation_cents":{"description":"Per-lender cap in this pool; 0 = unlimited","type":"integer"},"order_index":{"description":"Round-robin tie-breaker","type":"integer"},"weight_bps":{"description":"0-10000 (100 bps = 1%)","type":"integer"}},"type":"object"},"type":"array"}},"required":["members"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Status"}}},"description":"Replaced"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"pool_not_found"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Replace ALL pool members atomically","tags":["Lending"],"x-required-permission":"lending:edit"}},"/lenders":{"get":{"parameters":[{"description":"Name search","in":"query","name":"q","required":false,"schema":{"type":"string"}},{"description":"Default 50","in":"query","name":"limit","required":false,"schema":{"type":"integer"}},{"description":"","in":"query","name":"offset","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Lender"},"type":"array"}}},"description":"Lenders"}},"summary":"List lenders","tags":["Lending"]},"post":{"requestBody":{"content":{"application/json":{"schema":{"properties":{"email":{"description":"Optional contact email printed on documents","type":"string"},"name":{"description":"","type":"string"},"phone":{"description":"Optional contact phone printed on documents","type":"string"},"portal_url":{"description":"Optional portal address printed on documents, stored as typed","type":"string"},"total_allocation_cents":{"description":"≥ 0 — hard capital cap","type":"integer"}},"required":["name","total_allocation_cents"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Lender"}}},"description":"Created"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Negative allocation or invalid contact detail"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Create a lender","tags":["Lending"],"x-required-permission":"lending:create"}},"/lenders/{lender_uuid}":{"get":{"parameters":[{"description":"","in":"path","name":"lender_uuid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Lender"}}},"description":"Lender"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"}},"summary":"Get a lender","tags":["Lending"]},"put":{"parameters":[{"description":"","in":"path","name":"lender_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"email":{"description":"Contact email; omit to keep, send \"\" to clear","type":"string"},"name":{"description":"","type":"string"},"phone":{"description":"Contact phone; omit to keep, send \"\" to clear","type":"string"},"portal_url":{"description":"Portal address printed on documents; omit to keep, send \"\" to clear","type":"string"},"total_allocation_cents":{"description":"","type":"integer"}},"required":["name","total_allocation_cents"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Status"}}},"description":"Updated"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Update failed or invalid contact detail"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Update a lender (name and allocation required; omitted contact fields keep their current value)","tags":["Lending"],"x-required-permission":"lending:edit"}},"/lending/strategies":{"get":{"parameters":[{"description":"Name search","in":"query","name":"q","required":false,"schema":{"type":"string"}},{"description":"Default 50","in":"query","name":"limit","required":false,"schema":{"type":"integer"}},{"description":"","in":"query","name":"offset","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Strategy"},"type":"array"}}},"description":"Strategies"}},"summary":"List strategies","tags":["Lending"]},"post":{"requestBody":{"content":{"application/json":{"schema":{"properties":{"config_json":{"description":"Type-specific JSON configuration","type":"string"},"name":{"description":"","type":"string"},"type":{"enum":["rule_based","round_robin","balance"],"type":"string"}},"required":["name","type"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Strategy"}}},"description":"Created"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Create failed"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Create an allocation strategy","tags":["Lending"],"x-required-permission":"lending:create"}},"/lending/strategies/{strategy_uuid}":{"put":{"parameters":[{"description":"","in":"path","name":"strategy_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"config_json":{"description":"","type":"string"},"name":{"description":"","type":"string"},"type":{"description":"","type":"string"}},"required":["name","type"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Status"}}},"description":"Updated"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Update failed"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Update a strategy","tags":["Lending"],"x-required-permission":"lending:edit"}},"/loan-types":{"get":{"parameters":[{"description":"Name search","in":"query","name":"q","required":false,"schema":{"type":"string"}},{"description":"1-1000, default 50","in":"query","name":"limit","required":false,"schema":{"type":"integer"}},{"description":"","in":"query","name":"offset","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/LoanType"},"type":"array"}}},"description":"Loan types"}},"summary":"List loan types","tags":["Loan Types"]},"post":{"requestBody":{"content":{"application/json":{"schema":{"properties":{"config_json":{"description":"JSON blob: interest, fees, statements, underwriting, limits (LoanTypeConfig)","type":"string"},"name":{"description":"Internal admin name","type":"string"}},"required":["name","config_json"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoanType"}}},"description":"Created"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid body"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Create a loan type","tags":["Loan Types"],"x-required-permission":"settings:create"}},"/loan-types/import":{"post":{"description":"Body is one of three shapes: the export envelope from the admin Export action ({schema_version, exported_from, loan_type:{name, ..., config}}), the authoring wrapper {name, portal_name?, description?, config:{...}}, or a bare LoanTypeConfig object (then ?name= is required). Every object is closed — an unknown key anywhere is reported with its full path and blocks the import. Error-severity validator findings block too; warnings do not. Staff-gated: settings:create via X-User-UUID. Schema: GET /admin/loan-types/import/schema.json.","parameters":[{"description":"1 to validate only and return the review (nothing written)","in":"query","name":"dry_run","required":false,"schema":{"type":"string"}},{"description":"Loan type name — required for a bare config, otherwise overrides the document's name","in":"query","name":"name","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"description":"Export envelope | {name, config} wrapper | bare LoanTypeConfig","type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Dry run: {dry_run, would_create, review:{shape, name, config_json, unknown_keys, errors[], warnings[], info[], key_terms[]}}"},"201":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object"}}},"description":"Created: {loan_type, requirements_synced, audit_recorded, review} — document_requirement rows are synced and the creation audited before this is returned"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"invalid_document — not JSON, not an object, or none of the three shapes"},"413":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"payload_too_large (1 MB)"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"import_blocked — review.errors lists unknown keys and error-severity findings with field paths"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"requirements_sync_failed — row created but document requirements did not sync (loan_type returned); re-save in the admin UI"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Import a loan type from a JSON document","tags":["Loan Types"],"x-required-permission":"settings:create"}},"/loan-types/{loan_type_uuid}":{"get":{"parameters":[{"description":"","in":"path","name":"loan_type_uuid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoanType"}}},"description":"Loan type"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"}},"summary":"Get a loan type","tags":["Loan Types"]},"put":{"description":"Refused once loans exist on the type (types are immutable after first origination — create a new version instead).","parameters":[{"description":"","in":"path","name":"loan_type_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"config_json":{"description":"","type":"string"},"description":{"description":"","type":"string"},"name":{"description":"","type":"string"},"portal_name":{"description":"","type":"string"}},"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Status"}}},"description":"Updated"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid body, or loans already exist on this type"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Update a loan type","tags":["Loan Types"],"x-required-permission":"settings:edit"}},"/loans":{"get":{"description":"With loan_number or q present this becomes a compact lookup (max 10 hits: loan_uuid, number, borrower, status, payable flag, amount due). Otherwise a paginated list of full loan objects.","parameters":[{"description":"Exact loan number (dash/space/case-insensitive)","in":"query","name":"loan_number","required":false,"schema":{"type":"string"}},{"description":"Borrower name / email / phone","in":"query","name":"q","required":false,"schema":{"type":"string"}},{"description":"Filter by borrower","in":"query","name":"borrower_uuid","required":false,"schema":{"type":"string"}},{"description":"Max 1000, default 50","in":"query","name":"limit","required":false,"schema":{"type":"integer"}},{"description":"","in":"query","name":"offset","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"items":{"items":{"type":"object"},"type":"array"},"limit":{"description":"","type":"integer"},"offset":{"description":"","type":"integer"}},"type":"object"}}},"description":"Loans or lookup hits"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"lookup_failed"}},"summary":"List loans, or teller-counter lookup","tags":["Loans"]},"post":{"requestBody":{"content":{"application/json":{"schema":{"properties":{"amount_negotiation":{"description":"Declare that credit_limit_cents was negotiated away from the amount your underwriting approved. Judged against the product's loan_amount.negotiation policy: 422 amount_negotiation_not_allowed (product does not allow it), amount_out_of_band (outside the product's band; min_cents/max_cents returned), amount_negotiation_reason_required. Omit when the amount is the approved amount. Independently, an amount below the product's loan_amount.min_origination_cents answers 422 min_origination_amount.","properties":{"reason":{"description":"Reason code / note; required when the product says so; activity-logged","type":"string"},"reference_amount_cents":{"description":"The approved amount (\u003e 0)","type":"integer"}},"type":"object"},"borrower_uuid":{"description":"","type":"string"},"credit_limit_cents":{"description":"≥ 0","type":"integer"},"currency_code":{"description":"ISO 4217, e.g. USD","type":"string"},"dt_maturity":{"format":"date-time","type":"string"},"dt_opened":{"format":"date-time","type":"string"},"loan_type_uuid":{"description":"","type":"string"},"origination_channel":{"description":"Channel of this origination for the product compatibility matrix's in_store_only rule; default online","enum":["store","online"],"type":"string"},"pool_uuid":{"description":"Auto-assign a lender from this pool. If assignment fails the loan is still created and the response carries a warning.","type":"string"},"store_uuid":{"description":"Originating store (must belong to the company and be active, else 422 store_not_found): the loan's home store for the GL store dimension and per-store filings, and the store whose title fee override applies on vehicle-title products. Blank = the company's default store","type":"string"},"term_days":{"description":"Single-pay only: explicit term in days (1-366), refused when outside the product's or state's range; mutually exclusive with dt_maturity","type":"integer"},"title_fee_cash_receipt":{"description":"Counter receipt for a product whose title fees are collected in cash at origination (collateral.title_fee_payer=borrower_cash); required for such products (422 title_fee_receipt_required otherwise)","properties":{"method":{"description":"Counter tender; card payments go through the payment gateway","enum":["cash","check","money_order"],"type":"string"},"reference":{"description":"Receipt / register reference","type":"string"}},"type":"object"}},"required":["borrower_uuid","loan_type_uuid","currency_code","credit_limit_cents"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Loan"}}},"description":"Created (with optional warning when pool auto-assign failed)"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Missing/invalid fields"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Originate a loan","tags":["Loans"],"x-required-permission":"loans:create","x-title-fee-receipt":"A cash-fee title product (title_fee_payer=borrower_cash) takes title_fee_cash_receipt {method, reference} with the origination when the LMS owns cash (422 title_fee_receipt_required when missing). On a company whose cash tender source is the register a receipt here is refused (422 cash_tender_register_only); originate WITHOUT it and the register posts the receipt through POST /loans/{loan_uuid}/title-fee-receipt."}},"/loans/{loan_uuid}":{"get":{"description":"Every Loan field at the top level, PLUS the register's counter view: borrower_name, payable (the SAME predicate POST .../payments refuses on — sold_debt / settled / forfeited title pawn / OFAC hold — so a loan reported payable is never then refused), amount_due_cents (the borrower's CURRENT obligation: the scheduled installment plus arrears on an installment contract — a precomputed loan's whole finance charge is never quoted as due — otherwise interest + fees owed; never more than total_outstanding_cents), total_outstanding_cents, next_due_date (YYYY-MM-DD), store_uuid (home store), title_collateral / title_custody / lien_status / collateral_uuid for a loan with a collateral vehicle (title loan, title pawn, auto-equity — the register offers the title-return / lien-release action, POST .../title-custody, from these), and title_fee_counter_receipt {status at_origination|pending|received|voided, amount_cents, transaction_uuid, receipt_number, external_ref, method, store_uuid, receipted_at, voided_at} for a loan whose title fees are collected in cash (absent otherwise) — status pending means the register still owes the counter receipt (POST .../title-fee-receipt).","parameters":[{"description":"","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoanCounterView"}}},"description":"Loan + counter view"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"loan_not_found"}},"summary":"Get a loan (with the teller-counter view)","tags":["Loans"]}},"/loans/{loan_uuid}/adjustments":{"post":{"description":"Signed adjustment to one bucket. Maker-checker: adjustment_debit / adjustment_credit approval policies (by sign) can park this as 202 pending_approval; the custom `when` is honored only on the inline path.","parameters":[{"description":"","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"amount_cents":{"description":"Non-zero, signed","type":"integer"},"bucket":{"enum":["principal","interest","fees"],"type":"string"},"description":{"description":"","type":"string"},"note":{"description":"Audit reason","type":"string"},"when":{"format":"date-time","type":"string"}},"required":["bucket","amount_cents"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Loan"}}},"description":"Applied inline (auto-approved or no policy)"},"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}},"description":"Parked in the maker-checker queue (company approval policy covers this action). Nothing was executed; the request appears in Pending Approvals for sign-off."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Zero amount or invalid bucket"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Loan not found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"approval_duplicate"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"approval_unavailable"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Manual balance adjustment","tags":["Money"],"x-required-permission":"loans:edit"}},"/loans/{loan_uuid}/apply-interest":{"post":{"parameters":[{"description":"","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"as_of":{"description":"Effective timestamp for the operation (default: now)","format":"date-time","type":"string"}},"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Loan"}}},"description":"Updated loan"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Failed to apply interest"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Accrue + post interest as of a date","tags":["Money"],"x-required-permission":"loans:edit"}},"/loans/{loan_uuid}/apply-late-fee":{"post":{"parameters":[{"description":"","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"as_of":{"description":"Effective timestamp for the operation (default: now)","format":"date-time","type":"string"}},"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Loan"}}},"description":"Updated loan"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Failed to apply late fee"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Apply a late fee as of a date","tags":["Money"],"x-required-permission":"loans:edit"}},"/loans/{loan_uuid}/assign-lender":{"post":{"description":"When the pool is a credit facility the loan is first checked against its eligibility rules, commitment and concentration limits; a refusal answers 422 with the rule that fired (eligibility.\u003cfield\u003e, commitment, concentration.\u003cstate|product|channel|borrower\u003e) and leaves any existing assignment in place. Every assignment and reassignment is written to the loan's activity log.","parameters":[{"description":"","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"pool_uuid":{"description":"","type":"string"},"strategy_uuid":{"description":"Optional override of the pool's strategy","type":"string"}},"required":["pool_uuid"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Assignment"}}},"description":"Assignment"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Assign failed"},"422":{"content":{"application/json":{"schema":{"properties":{"detail":{"description":"Human-readable reason","type":"string"},"error":{"description":"facility_refused","type":"string"},"facility":{"description":"Facility code","type":"string"},"pool_uuid":{"description":"","type":"string"},"rule":{"description":"eligibility.\u003cfield\u003e | commitment | concentration.\u003cdimension\u003e","type":"string"}},"type":"object"}}},"description":"facility_refused"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Assign (or re-assign) a lender via a pool's strategy","tags":["Lending"],"x-required-permission":"lending:edit"}},"/loans/{loan_uuid}/assignment":{"get":{"parameters":[{"description":"","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Assignment"}}},"description":"Assignment"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"}},"summary":"Current lender assignment","tags":["Lending"]}},"/loans/{loan_uuid}/auto-default":{"post":{"description":"Idempotent: replaying on an already-defaulted loan answers defaulted=false.","parameters":[{"description":"","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"as_of":{"description":"Effective timestamp for the operation (default: now)","format":"date-time","type":"string"}},"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"defaulted":{"description":"true only when THIS call transitioned the loan","type":"boolean"},"loan":{"$ref":"#/components/schemas/Loan"}},"type":"object"}}},"description":"Result"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Failed"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Default the loan if past due as of a date","tags":["Money"],"x-required-permission":"loans:edit"}},"/loans/{loan_uuid}/collateral/lien":{"get":{"description":"The pledged title's lien: method (electronic_elt | dmv_submission | paper | none), status (none_yet → pending → submitted → perfected → release_requested → released; failed from pending / submitted), filing and release references and dates, title custody, vehicle class, whether the product's funding gate (collateral.require_lien_perfected_before_funding) currently holds disbursement, and whether an elt_http connector is wired.","parameters":[{"description":"Loan UUID","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LienView"}}},"description":"Lien"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Loan not found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"No pledged title on this loan (no_title_lien)"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Title lien perfection state","tags":["Loans"],"x-required-permission":"loans:view"},"post":{"description":"action=update (default) records the transition the DMV / vendor reported: status (required unless only method changes), method, reference (filing or release id), reason (failures / releases), date (YYYY-MM-DD ET, default today). On a vehicle whose method is electronic_elt with an elt_http connector wired and NO reference supplied, status=submitted files the lien through the vendor (lien_add) and status=release_requested releases it (lien_release); the vendor's answer sets the status. action=poll asks the vendor for the current status of a submitted lien (lien_status). Illegal transitions answer 409.","parameters":[{"description":"Loan UUID","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"action":{"enum":["update","poll"],"type":"string"},"date":{"description":"YYYY-MM-DD (ET)","type":"string"},"method":{"enum":["electronic_elt","dmv_submission","paper","none"],"type":"string"},"reason":{"description":"","type":"string"},"reference":{"description":"DMV / ELT filing or release reference","type":"string"},"status":{"enum":["none_yet","pending","submitted","perfected","release_requested","released","failed"],"type":"string"}},"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LienView"}}},"description":"Lien"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid body"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Loan not found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Illegal transition (illegal_transition)"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"No pledged title (no_title_lien), no elt_http connector (elt_not_configured), connector not live (elt_not_live)"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Record or drive a lien transition","tags":["Loans"],"x-required-permission":"loans:edit"}},"/loans/{loan_uuid}/convert-loc":{"post":{"description":"Closes the line (no further draws) and amortizes its full outstanding balance onto a fixed-installment product over new_term_months at an optional pinned new_apr_bps. Same once-only, resume-safe semantics as refinance.","parameters":[{"description":"","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"new_apr_bps":{"description":"0-100000; blank keeps the product rate","type":"integer"},"new_loan_type_uuid":{"description":"Fixed-installment product (not a LOC)","type":"string"},"new_term_months":{"description":"1-600","type":"integer"},"reason":{"description":"Max 500","type":"string"}},"required":["reason","new_loan_type_uuid","new_term_months"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefinanceResult"}}},"description":"Restructure result"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid body"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Loan not found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Loan already refinanced or converted"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Refused (not a LOC, zero balance, target is a LOC, origination guard)"},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Refinance not configured"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Convert a line of credit to an installment loan","tags":["Loans"],"x-required-permission":"loans:edit"}},"/loans/{loan_uuid}/details":{"get":{"parameters":[{"description":"","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"aggregates":{"properties":{"total_draw_principal_cents":{"description":"","type":"integer"},"total_fees_posted_cents":{"description":"","type":"integer"},"total_interest_posted_cents":{"description":"","type":"integer"},"total_payments_fees_cents":{"description":"","type":"integer"},"total_payments_interest_cents":{"description":"","type":"integer"},"total_payments_principal_cents":{"description":"","type":"integer"},"total_refunds_cents":{"description":"","type":"integer"},"txn_count":{"description":"","type":"integer"}},"type":"object"},"loan":{"$ref":"#/components/schemas/Loan"},"transactions":{"items":{"type":"object"},"type":"array"}},"type":"object"}}},"description":"Details"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"}},"summary":"Loan with full transaction history and aggregates","tags":["Loans"]}},"/loans/{loan_uuid}/disburse-draw":{"post":{"description":"Records the draw and sends funds over the chosen rail through the payment-gateway connector. rail=repay pushes to the borrower's saved, verified debit card (payment_method_uuid) and usually answers synchronously; rail=ach_credit sends an ACH credit to the borrower's saved, gateway-vaulted bank account (payment_method_uuid) and usually answers 202 pending, posting to the loan once the gateway confirms the credit settled. Both rails REQUIRE an Idempotency-Key header (reuse it verbatim on every retry; never mint a new one for the same draw) and refuse `when`. Any gateway-reported processor fee is deducted from the amount delivered; the loan is charged the full gross. rail=ach (the retired in-house NACHA credit) answers 422. Responses: 200 succeeded, 202 pending (poll; do not retry with a new key), 402 failed (proven: nothing moved), 409 idempotency_conflict, 422 refused before anything moved, 502 needs_reconcile (do not retry).","parameters":[{"description":"","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"amount_cents":{"description":"\u003e 0, gross","type":"integer"},"borrower_uuid":{"description":"","type":"string"},"description":{"description":"","type":"string"},"invoice_number":{"description":"Card rail only","type":"string"},"network":{"description":"money_transfer rail only","enum":["moneygram","western_union"],"type":"string"},"payment_method_uuid":{"description":"repay: the saved, verified debit card; ach_credit: the saved, gateway-vaulted bank account","type":"string"},"rail":{"description":"repay = push to saved debit card; ach_credit = ACH credit to saved bank account; cash = paid out at the store register (202 awaiting_register: a cash disbursement intent the register claims, pays and confirms); money_transfer = MoneyGram / Western Union sent from the register (202 awaiting_register; network required); check = printed check to the borrower through the gateway check printer (no payment_method_uuid; ach = retired (422)","enum":["repay","ach_credit","cash","check","money_transfer","ach"],"type":"string"},"receiver_account":{"description":"Retired ach rail only; ignored","type":"string"},"receiver_account_type":{"description":"Retired ach rail only; ignored","enum":["checking","savings"],"type":"string"},"receiver_name":{"description":"Retired ach rail only; ignored","type":"string"},"receiver_routing_number":{"description":"Retired ach rail only; ignored","type":"string"},"store_uuid":{"description":"cash / money_transfer: the store whose register pays it out","type":"string"},"when":{"format":"date-time","type":"string"}},"required":["rail","borrower_uuid","amount_cents"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"disbursement":{"$ref":"#/components/schemas/Disbursement"},"loan":{"type":"object"},"loan_transaction_uuid":{"description":"","type":"string"},"status":{"description":"succeeded","type":"string"}},"type":"object"}}},"description":"Funded and posted"},"202":{"content":{"application/json":{"schema":{"properties":{"cash_disbursement":{"$ref":"#/components/schemas/CashDisbursement"},"code":{"description":"disbursement_pending | disbursement_awaiting_register","type":"string"},"disbursement":{"$ref":"#/components/schemas/Disbursement"},"message":{"description":"","type":"string"},"status":{"description":"pending | awaiting_register","type":"string"}},"type":"object"}}},"description":"Pending: the processor has not confirmed yet (status pending, code disbursement_pending; poll, never retry with a new key) — or, on the cash / money_transfer rails, the payout is queued for the register (status awaiting_register, code disbursement_awaiting_register, cash_disbursement: CashDisbursement; poll GET /cash-disbursements/{uuid})"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid request, or Idempotency-Key missing / when supplied on a gateway or register rail, or network_required on money_transfer"},"402":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Failed: proven nothing moved (card_declined | ach_credit_refused)"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Idempotency-Key reused for a different draw"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Refused before anything moved (rail retired, funding not provisioned, rail switched off, destination not eligible)"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Needs reconciliation; do not retry"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Draw + send funds (push-to-card or ACH credit)","tags":["Loans"],"x-required-permission":"refunds:create"}},"/loans/{loan_uuid}/disburse-draw/{intent_uuid}/unclaim":{"post":{"description":"Loan-scoped alias of POST /cash-disbursements/{uuid}/unclaim (same body and answers); 404 not_found when the payout does not belong to the loan.","parameters":[{"description":"","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}},{"description":"The cash_disbursement_uuid","in":"path","name":"intent_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"posted_by":{"description":"Teller attribution (alias: teller)","properties":{"external_id":{"description":"Max 64","type":"string"},"name":{"description":"Max 128","type":"string"},"subject":{"description":"Max 255","type":"string"}},"type":"object"},"reason":{"description":"Max 255","type":"string"},"register_uuid":{"description":"The register releasing the claim: must be the claiming register","type":"string"}},"required":["register_uuid"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CashDisbursement"}}},"description":"Released (status pending)"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"register_required"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found / not on this loan"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"claim_conflict or closed"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Release this register's claim on a loan's payout","tags":["Cash disbursements"],"x-required-permission":"cash_disbursements:create"}},"/loans/{loan_uuid}/disbursements/{uuid}/void-check":{"post":{"description":"Reverses a succeeded check-rail draw after the instrument was voided (stop payment, lost or misprinted check): the principal the draw posted is voided on the loan and the disbursement is flagged check_voided=true (its status stays succeeded; the ledger carries the post and the void). Idempotent.","parameters":[{"description":"","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}},{"description":"The disbursement_uuid","in":"path","name":"uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"reason":{"description":"","type":"string"}},"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"disbursement":{"$ref":"#/components/schemas/Disbursement"},"status":{"description":"voided","type":"string"}},"type":"object"}}},"description":"Voided"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Unknown disbursement"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"check_draw_not_voidable (not a succeeded check draw) | disbursement_busy"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Void a printed-check draw","tags":["Loans"],"x-required-permission":"refunds:create"}},"/loans/{loan_uuid}/dmv-fees":{"post":{"description":"Posts a pass-through DMV / title fee (fee_dmv) at cost on a loan that carries vehicle collateral with a title lien (auto-equity loans take no lien and are refused). Idempotent on external_ref — the DMV receipt / confirmation number — so a re-push of the same receipt answers 409 rather than double-posting. A markup_cents other than zero is refused: a marked-up fee is not a pass-through.","parameters":[{"description":"Loan UUID","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"amount_cents":{"description":"\u003e 0 and at most 500000 ($5,000.00)","type":"integer"},"dmv_state":{"description":"Two-letter US state code","type":"string"},"external_ref":{"description":"DMV receipt / confirmation number; the idempotency identity","type":"string"},"markup_cents":{"description":"Must be 0 (pass-through at cost)","type":"integer"},"note":{"description":"","type":"string"},"payee":{"description":"Agency paid (e.g. the state DMV)","type":"string"},"sub_kind":{"enum":["lien_filing","title_transfer","registration","lien_release","duplicate_title","plate","other"],"type":"string"},"when":{"description":"Posting instant (default: now)","format":"date-time","type":"string"}},"required":["sub_kind","amount_cents"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"properties":{"amount_cents":{"description":"","type":"integer"},"at_cost":{"description":"","type":"boolean"},"fees_outstanding_cents":{"description":"Loan fees outstanding after the posting","type":"integer"},"loan_transaction_uuid":{"description":"","type":"string"},"loan_uuid":{"description":"","type":"string"},"type":{"description":"fee_dmv","type":"string"}},"type":"object"}}},"description":"Fee posted"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid body"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Loan not found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"A DMV fee with this external_ref is already posted"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Refused: bad sub_kind, bad amount, markup, bad dmv_state, no vehicle collateral, terminal loan, or pending signature"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Assess a DMV / title fee on a title-secured loan","tags":["Loans"],"x-required-permission":"loans:edit"}},"/loans/{loan_uuid}/dmv-fees/{txn_uuid}/cancel":{"post":{"description":"Reverses a DMV fee that is still outstanding on the loan and relieves DMV Fees Payable. A fee already paid down, already cancelled, or not on this loan is refused.","parameters":[{"description":"Loan UUID","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}},{"description":"The fee_dmv transaction UUID","in":"path","name":"txn_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"reason":{"description":"Audit reason","type":"string"}},"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"properties":{"cancels":{"description":"The cancelled fee_dmv transaction UUID","type":"string"},"fees_outstanding_cents":{"description":"","type":"integer"},"loan_transaction_uuid":{"description":"The reversing transaction","type":"string"}},"type":"object"}}},"description":"Fee cancelled"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Loan or fee not found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Fee no longer outstanding or already cancelled"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Cancel an outstanding DMV / title fee","tags":["Loans"],"x-required-permission":"loans:edit"}},"/loans/{loan_uuid}/documents":{"get":{"parameters":[{"description":"","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}},{"description":"Default 50","in":"query","name":"limit","required":false,"schema":{"type":"integer"}},{"description":"","in":"query","name":"offset","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"items":{"items":{"$ref":"#/components/schemas/DocumentMeta"},"type":"array"},"limit":{"description":"","type":"integer"},"offset":{"description":"","type":"integer"}},"type":"object"}}},"description":"Documents page"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"List failed"}},"summary":"List loan documents","tags":["Documents"]},"post":{"parameters":[{"description":"","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"multipart/form-data":{"schema":{"properties":{"bucket":{"description":"Storage bucket hint","type":"string"},"description":{"description":"Human-readable description","type":"string"},"doc_type":{"description":"Document type/category","type":"string"},"file":{"description":"The document file","format":"binary","type":"string"}},"required":["file"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentMeta"}}},"description":"Uploaded"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"missing_file_part / upload_failed"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Upload a loan document","tags":["Documents"],"x-required-permission":"loans:edit"}},"/loans/{loan_uuid}/documents/{document_uuid}":{"get":{"parameters":[{"description":"","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}},{"description":"","in":"path","name":"document_uuid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentMeta"}}},"description":"Metadata"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"}},"summary":"Document metadata","tags":["Documents"]}},"/loans/{loan_uuid}/documents/{document_uuid}/download":{"get":{"parameters":[{"description":"","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}},{"description":"","in":"path","name":"document_uuid","required":true,"schema":{"type":"string"}},{"description":"\"1\" for inline","in":"query","name":"inline","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Binary file"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"}},"summary":"Download the file","tags":["Documents"]}},"/loans/{loan_uuid}/draws":{"post":{"description":"Books principal the borrower already received by some other means (no money moves here). method names the tender; an omitted method counts as cash. On a company whose cash tender source is the store register, a cash / money_transfer draw is REFUSED (422 cash_tender_register_only; the body's `use` names POST .../disburse-draw with rail=cash) — principal is never booked without a counter payout behind it; the register's paid confirmation posts the draw.","parameters":[{"description":"","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"amount_cents":{"description":"\u003e 0","type":"integer"},"description":{"description":"","type":"string"},"method":{"description":"How the principal reached the borrower; default cash","enum":["cash","check","ach","card","wire","money_transfer"],"type":"string"},"when":{"format":"date-time","type":"string"}},"required":["amount_cents"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Loan"}}},"description":"Updated loan"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"invalid_request / invalid_amount / invalid_method / draw_refused"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"cash_tender_register_only — request a register payout through disburse-draw (rail cash) instead"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"cash_policy_unavailable"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Record a LEDGER-ONLY principal draw","tags":["Loans"],"x-required-permission":"loans:edit"}},"/loans/{loan_uuid}/held-checks":{"get":{"description":"custody=awaiting|held|released selects register-custody records by the state of the paper (what the Store Operations register still has to file or release). On a register-owned company every new record is register custody (custody_source=register) and shows awaiting until the register posts .../custody.","parameters":[{"description":"","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}},{"in":"query","name":"custody","schema":{"enum":["awaiting","held","released"],"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"held_checks":{"items":{"$ref":"#/components/schemas/HeldCheckListItem"},"type":"array"}},"type":"object"}}},"description":"Held checks: each HeldCheck plus custody (awaiting | held | released; empty for LMS custody), loan_number, store_uuid (custody store, else the loan's home store) and bank_name (= drawer_bank)"},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Held checks not configured"}},"security":[{"apiKey":[],"companyHeader":[]}],"summary":"List held checks (deferred presentment)","tags":["Loans"]},"post":{"description":"Records the post-dated check the store holds for a single-pay loan. Supplying routing_number + account_number vaults the drawer account at the payment gateway so the check can later be presented as an ACH debit (check conversion); authorization_text — the consent the borrower agreed to — is then required and is persisted as a single-use debit authorization that is retired once the check reaches an outcome. The account number is sent to the gateway vault only and is never stored or logged here. Send an Idempotency-Key so a retried request reuses the vaulted instrument instead of vaulting twice.","parameters":[{"description":"","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}},{"description":"Client-generated key (max 128 chars); reuse the same value on retries. Takes precedence over the idempotency_key body field.","in":"header","name":"Idempotency-Key","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"account_number":{"description":"Drawer account; vaulted at the gateway only","type":"string"},"account_type":{"enum":["checking","savings"],"type":"string"},"amount_cents":{"description":"\u003e 0; check face amount","type":"integer"},"authorization_text":{"description":"Verbatim consent the borrower agreed to; required with bank coordinates","type":"string"},"check_number":{"description":"Max 40","type":"string"},"custody_notes":{"description":"Max 500","type":"string"},"drawer_bank":{"description":"Max 120","type":"string"},"holder_name":{"description":"Name on the account","type":"string"},"presentment_date":{"description":"YYYY-MM-DD (ET); agreed deposit date","type":"string"},"routing_number":{"description":"ABA routing; with account_number enables ACH presentment","type":"string"},"written_date":{"description":"YYYY-MM-DD (ET)","type":"string"}},"required":["check_number","amount_cents"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"properties":{"held_check":{"$ref":"#/components/schemas/HeldCheck"}},"type":"object"}}},"description":"Recorded"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid body"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Loan not found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Refused (loan not an active single-pay, ACH presentment not configured, vault failure)"},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Held checks not configured"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Record a held check","tags":["Loans"],"x-required-permission":"loans:edit"}},"/loans/{loan_uuid}/held-checks/{held_check_uuid}/custody":{"post":{"description":"Store Operations bridge: records which register tender instrument holds the physical check. The LMS record stays the legal hold; the register is the custody trail. Idempotent on instrument_ref (a replay answers 200 with replayed=true). A DIFFERENT instrument on a record already in custody is 409 custody_conflict. Only register-custody records (custody_source=register) accept this; an LMS-custody record answers 422. Presentment and deposit from the LMS require the paper to be held (or released for presentment), so a check nobody holds cannot be presented. Refused once the record is terminal.","parameters":[{"description":"","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}},{"description":"","in":"path","name":"held_check_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"drawer_session_uuid":{"description":"Max 64","type":"string"},"external_source":{"enum":["cash_register"],"type":"string"},"instrument_ref":{"description":"Register tender_instrument id (max 128); the idempotency key","type":"string"},"register_uuid":{"description":"Max 64","type":"string"},"store_uuid":{"description":"Store holding the paper (must be a store in this company)","type":"string"},"teller":{"properties":{"external_id":{"description":"Max 64","type":"string"},"name":{"description":"Max 128","type":"string"}},"type":"object"}},"required":["instrument_ref","external_source"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HeldCheckCustody"}}},"description":"Replay — same instrument already on file"},"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HeldCheckCustody"}}},"description":"Custody recorded"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"invalid_request"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"not_found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"custody_conflict / bad_transition (terminal)"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"refused (LMS-custody record, external_source not cash_register)"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Register files the paper (custody)","tags":["Loans"],"x-required-permission":"loans:edit | payments:create"}},"/loans/{loan_uuid}/held-checks/{held_check_uuid}/custody/release":{"post":{"description":"The paper left register custody: presented (taken to the bank / handed to the LMS for ACH presentment), returned_to_borrower, deposited, or missing (raises an ops alert). Idempotent on (instrument_ref, reason). A release by an instrument that does not hold the paper, or a second release with a different reason, is 409 not_in_custody. Releasing does not move the LMS lifecycle: the loan-side presentment / deposit is still recorded through .../present or .../resolve, which accept a release of presented (and deposited for a deposit).","parameters":[{"description":"","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}},{"description":"","in":"path","name":"held_check_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"instrument_ref":{"description":"","type":"string"},"reason":{"enum":["presented","returned_to_borrower","deposited","missing"],"type":"string"}},"required":["instrument_ref","reason"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HeldCheckCustody"}}},"description":"Released (replayed=true when already released the same way)"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"invalid_request"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"not_found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"not_in_custody"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Register releases the paper","tags":["Loans"],"x-required-permission":"loans:edit | payments:create"}},"/loans/{loan_uuid}/held-checks/{held_check_uuid}/present":{"post":{"description":"Claims the record (held → presented) and debits the vaulted drawer account through the payment gateway for the check amount. Accepted debits post the loan payment at once; a settled debit marks the check deposited, a settling one stays presented until the gateway's webhook finalizes it (a later return flips it to returned and posts the returned-payment fee). A gateway decline answers 402 with the check marked returned and nothing moved.","parameters":[{"description":"","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}},{"description":"","in":"path","name":"held_check_uuid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HeldCheckPresentment"}}},"description":"Accepted"},"402":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Declined by the gateway; check marked returned"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not in 'held' (already presented or resolved)"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Refused (no vaulted account, authorization revoked, loan not eligible, rail not configured)"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Present a held check as an ACH debit","tags":["Loans"],"x-required-permission":"loans:edit"}},"/loans/{loan_uuid}/held-checks/{held_check_uuid}/resolve":{"post":{"description":"Manual lifecycle moves for a paper-deposited check: presented, deposited (optionally posting the loan payment), returned (posts the returned-payment fee for R01/R09 when the product opts in; a previously posted payment is reversed by the operator), returned_to_borrower, or voided.","parameters":[{"description":"","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}},{"description":"","in":"path","name":"held_check_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"notes":{"description":"Appended to the custody trail","type":"string"},"post_payment":{"description":"deposited only: post the check amount as a loan payment","type":"boolean"},"return_code":{"description":"returned only; NACHA code such as R01","type":"string"},"return_reason":{"description":"","type":"string"},"status":{"enum":["presented","deposited","returned","returned_to_borrower","voided"],"type":"string"}},"required":["status"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"held_check_uuid":{"description":"","type":"string"},"nsf_fee_txn_uuid":{"description":"","type":"string"},"payment_txn_uuid":{"description":"","type":"string"},"status":{"description":"","type":"string"}},"type":"object"}}},"description":"Updated"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid body"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Illegal transition"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Refused"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Resolve a held check manually","tags":["Loans"],"x-required-permission":"loans:edit"}},"/loans/{loan_uuid}/pawn":{"get":{"description":"The pawn's pledge record (ticket, pledge / maturity / grace-end dates, pinned per-period charge rate and step-downs, renewals, charges posted) and today's readiness: can_renew — COUNTER semantics: the grace window is open and renewals / the state's maximum term allow it; an outstanding pawnshop charge does NOT refuse it but is reported as renew_rule=charge_unpaid with charge_due_cents (pay-and-renew collects it with the renewal; the admin shape of POST .../pawn/renew still requires it paid) — with the next maturity and the next period's charge; can_redeem / redeem_rule (a payoff today redeems the pledge: active and not past the grace end); forfeiture_status (active | in_grace | grace_closed | forfeited | redeemed); and can_forfeit (the grace window closed). Past the grace end can_renew and can_redeem are both false with rule grace_closed. A pawn is never delinquent: no late fee, default, charge-off, collections, credit reporting or repossession — an unredeemed pledge is forfeited after the grace window.","parameters":[{"description":"Loan UUID","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PawnStanding"}}},"description":"Pawn standing"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Loan not found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not a title pawn (not_a_pawn)"},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Pawn not configured"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Title pawn standing","tags":["Loans"],"x-required-permission":"loans:view"}},"/loans/{loan_uuid}/pawn/forfeit":{"post":{"description":"After the redemption grace window closes, forfeits the pledge to the pawnbroker: every balance (principal, charges, fees) is written off against the title, the loan lands in the terminal status forfeited with NO deficiency, the title custody reads forfeited_to_pawnbroker and pawn.forfeited fires (the Store Operations register takes the vehicle into inventory). The company's pawn_forfeiture maker-checker policy (seeded single approval) answers 202 pending_approval; the forfeiture runs when a reviewer approves. Idempotent: an already-forfeited pledge answers 200 already_forfeited.","parameters":[{"description":"Loan UUID","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"reason":{"description":"Audit reason (required)","type":"string"}},"required":["reason"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"forfeiture":{"$ref":"#/components/schemas/PawnForfeiture"},"loan":{"$ref":"#/components/schemas/Loan"},"status":{"description":"","type":"string"}},"type":"object"}}},"description":"Forfeited (status forfeited | already_forfeited)"},"202":{"content":{"application/json":{"schema":{"properties":{"approval_request_uuid":{"description":"","type":"string"},"status":{"description":"pending_approval","type":"string"},"ticket_number":{"description":"","type":"string"}},"type":"object"}}},"description":"Pending maker-checker approval; nothing moved"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid body"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Loan not found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Pawn not active (pawn_not_active) or an identical request awaits approval (approval_duplicate)"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Refused: grace_open (the redemption window has not closed), loan_not_active, not_a_pawn"},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Pawn not configured"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Forfeit a title pawn's pledge","tags":["Loans"],"x-required-permission":"loans:edit"}},"/loans/{loan_uuid}/pawn/forfeiture-intake":{"post":{"description":"After pawn.forfeited the Store Operations register takes the vehicle into inventory and calls back with the inventory_item_uuid it created; the LMS stores it on the collateral row (collateral_vehicle.inventory_item_uuid) and every later pawn.* / repossession.* event carries it under vehicle.inventory_item_uuid. The same item is idempotent (status replayed); a different item on an already-linked vehicle is 409 intake_conflict. The register may also answer the pawn.forfeited delivery itself with {inventory_item_uuid, store_uuid} as its 2xx body.","parameters":[{"description":"Loan UUID","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"inventory_item_uuid":{"description":"The register's inventory_item UUID","type":"string"},"posted_by":{"properties":{"external_id":{"description":"","type":"string"},"name":{"description":"","type":"string"},"subject":{"description":"","type":"string"}},"type":"object"},"store_uuid":{"description":"Store that took the vehicle in","type":"string"}},"required":["inventory_item_uuid"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"intake":{"properties":{"collateral_uuid":{"description":"","type":"string"},"inventory_item_uuid":{"description":"","type":"string"},"replayed":{"description":"","type":"boolean"},"ticket_number":{"description":"","type":"string"}},"type":"object"},"status":{"description":"","type":"string"}},"type":"object"}}},"description":"Recorded (status recorded | replayed)"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"inventory_item_required"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Loan not found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"pawn_not_forfeited | intake_conflict | store_not_permitted"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"no_vehicle | not_a_pawn | invalid_posted_by"},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Pawn not configured"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Register reports the forfeited vehicle's inventory item","tags":["Loans"],"x-required-permission":"loans:edit | payments:create"}},"/loans/{loan_uuid}/pawn/renew":{"post":{"description":"ADMIN SHAPE {reason}: extends the maturity one period (30 days) on the SAME ticket and moves the grace window with it; the accrued pawnshop charge must be paid in full first (interest + fees outstanding = 0). Refused past the grace window (grace_closed — forfeit or redeem), at the product's max_renewals, or when the renewed maturity would exceed the state's maximum pawn term. On a per-period product the new period's charge posts at the renewal when the period has started. COUNTER SHAPE: see pay_and_renew below.","parameters":[{"description":"Loan UUID","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}},{"description":"Client-generated key (max 128 chars); reuse the same value on retries. Takes precedence over the idempotency_key body field.","in":"header","name":"Idempotency-Key","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"external_display":{"description":"Receipt label stamped on the ledger rows","type":"string"},"external_ref":{"description":"Origin system's transaction id","type":"string"},"external_source":{"description":"Origin system, e.g. cash_register (with external_ref)","type":"string"},"idempotency_key":{"description":"Body fallback for the Idempotency-Key header","type":"string"},"pay_and_renew":{"description":"COUNTER SHAPE (Store Operations register): collect the pawnshop charge and renew in ONE atomic call. The payment posts through the ordinary payment path (external_ref anchor, allocation, posted_by stamps, payment.received) and the pledge extends in the same transaction; a refusal after the money posted rolls the money back. amount_cents must be ≥ charge_due_cents (GET .../pawn) and \u003c redeem_amount_cents — an amount that pays the pledge off is a redemption (POST .../payments with a payoff quote). Requires Idempotency-Key / idempotency_key or external_source + external_ref; a replay answers status=replayed with the original renewal and posts nothing.","properties":{"amount_cents":{"description":"Cash collected; the excess over the charge reduces principal through the product waterfall","type":"integer"},"check_number":{"description":"","type":"string"},"method":{"description":"cash | check | money_order (default cash)","type":"string"},"store_uuid":{"description":"Point-of-receipt store (validated in the company; store-scoped keys must name one in their scope)","type":"string"},"when":{"format":"date-time","type":"string"}},"required":["amount_cents"],"type":"object"},"posted_by":{"description":"Teller attribution {external_id, name, subject} (alias: teller)","properties":{"external_id":{"description":"","type":"string"},"name":{"description":"","type":"string"},"subject":{"description":"","type":"string"}},"type":"object"},"reason":{"description":"Audit reason (required on the admin shape; optional with pay_and_renew)","type":"string"},"teller":{"description":"Alias of posted_by","properties":{"external_id":{"description":"","type":"string"},"name":{"description":"","type":"string"},"subject":{"description":"","type":"string"}},"type":"object"}},"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"amount_cents":{"description":"","type":"integer"},"charge_paid_cents":{"description":"","type":"integer"},"loan":{"$ref":"#/components/schemas/Loan"},"payment_ref":{"description":"","type":"string"},"receipt":{"$ref":"#/components/schemas/CounterReceipt"},"renewal":{"$ref":"#/components/schemas/PawnRenewal"},"replayed":{"description":"","type":"boolean"},"standing":{"$ref":"#/components/schemas/PawnStanding"},"status":{"description":"renewed | replayed","type":"string"},"transaction_uuids":{"items":{"type":"string"},"type":"array"}},"type":"object"}}},"description":"Renewed (status renewed | replayed). The counter shape adds standing, transaction_uuids, payment_ref (= transaction_uuids[0]), amount_cents, charge_paid_cents, idempotency_key, external_source/ref, posted_by, replayed."},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid body (invalid_amount, invalid_method, reason_required)"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Loan not found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Counter refusals carry {max_renewals, renewals_used, charge_due_cents, redeem_amount_cents}: charge_short, amount_is_payoff, max_renewals, grace_closed, pawn_max_term_days, pawn_not_active, charge_not_cleared; idempotency_conflict; store_not_permitted (store-scoped key)"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Admin shape refusals: charge_unpaid, grace_closed, max_renewals, pawn_max_term_days, loan_not_active, not_a_pawn; counter: idempotency_key_required, invalid_external_ref, invalid_store, cash_tender_register_only, invalid_posted_by"},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Pawn not configured"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Renew a title pawn one period","tags":["Loans"],"x-required-permission":"payments:create | loans:edit"}},"/loans/{loan_uuid}/payment-preview":{"get":{"description":"Projects what POST .../payments will do with a payment BEFORE the register rings it, so the counter slip can print the allocation, the balance after, the next due date and 'paid in full' (NRS 604A.5088; the CA / SC / GA / FL / OH every-payment receipt statutes). The projection runs the real allocation engine — the product waterfall, payment-time accrual, principal-only gating, pawn redemption — on in-memory shadow copies of the loan, its ledger and its pawn row; no row is written and no event fires. receipt_lines[] are the labelled disclosure lines the slip must carry (kind is stable: licensee_name, licensee_address, license, loan_number, account_holder, payment_date, amount_paid, tender, applied, balance_after | paid_in_full, next_due | maturity, counter). Every refusal the posting would raise surfaces here with the same code (409 loan_not_payable, 422 principal_only_*).","parameters":[{"description":"","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}},{"description":"\u003e 0","in":"query","name":"amount_cents","required":true,"schema":{"type":"integer"}},{"description":"Tender (cash by default)","in":"query","name":"method","schema":{"enum":["cash","check","money_order","money_transfer","card"],"type":"string"}},{"in":"query","name":"check_number","schema":{"type":"string"}},{"description":"Omit for the product waterfall","in":"query","name":"allocation","schema":{"enum":["principal_only"],"type":"string"}},{"description":"RFC3339 or YYYY-MM-DD (noon ET); now by default","in":"query","name":"when","schema":{"type":"string"}},{"description":"Point-of-receipt store (the store block / counter line resolve as the posting's will)","in":"query","name":"store_uuid","schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentPreview"}}},"description":"The projection"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"invalid_amount / invalid_method / invalid_allocation / invalid_when"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"loan_not_found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"loan_not_payable"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"invalid_store / principal_only_*"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"preview_failed"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"preview_unavailable"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Pre-ring payment preview (nothing is written)","tags":["Money"],"x-required-permission":"loans:view | payments:view"}},"/loans/{loan_uuid}/payments":{"post":{"description":"A stable identity is REQUIRED on every tender: send an Idempotency-Key (or body idempotency_key) or external_source + external_ref; a request with neither is refused 422 idempotency_key_required before any money moves, because a retry after a lost response would otherwise post the cash/check twice. Idempotent twice over: Idempotency-Key (or body idempotency_key; header wins) replays the original posting, and external_source+external_ref (your register's txn id) is exactly-once globally — a replayed ref returns the original result, a mismatched reuse answers 409. store_uuid (point of receipt) is tenant-validated before any money moves. posted_by attributes the human teller on every ledger row and on the payment.received webhook.","parameters":[{"description":"","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}},{"description":"Client-generated key (max 128 chars); reuse the same value on retries. Takes precedence over the idempotency_key body field.","in":"header","name":"Idempotency-Key","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"allocation":{"description":"Omit for the product waterfall. principal_only applies the whole amount to principal (interest, fees and CAB fee untouched); gated by payment_settings.principal_only_payments, refused above the principal outstanding and on precomputed-charge products (422 principal_only_*). Not a scheduled payment: the next due stays due.","enum":["principal_only"],"type":"string"},"amount_cents":{"description":"\u003e 0","type":"integer"},"check_number":{"description":"For method=check","type":"string"},"description":{"description":"","type":"string"},"external_display":{"description":"Human label (max 255) — kept as metadata and rendered once on the LMS receipt; NEVER stamped as the receipt number","type":"string"},"external_ref":{"description":"Register txn id (max 128); unique per source","type":"string"},"external_source":{"description":"Register id (max 64); required with external_ref. On a company whose cash tender source is the register, a cash / check / money_order payment is refused (422 cash_tender_register_only) unless external_source is cash_register.","type":"string"},"gateway_txn_id":{"description":"method=card only: the gateway payment id (max 128). A card posting also needs a stable key (Idempotency-Key or external_ref) so a retry resumes the same counter transaction; a replayed key with a different amount answers 409 idempotency_conflict. Refusals: 422 gateway_txn_not_found / gateway_txn_not_settled / gateway_amount_mismatch (gateway_amount_cents in the body), 409 gateway_txn_already_used, 503 gateway_unavailable (retry with the same key).","type":"string"},"idempotency_key":{"description":"Max 128; header wins","type":"string"},"method":{"description":"money_transfer = received through MoneyGram / Western Union at the register (network + reference required); a paper tender like cash on a register-owned company. card = a card the teller took on the gateway terminal, already settled there: gateway_txn_id is required and the LMS verifies it with the gateway (succeeded, same merchant, same amount, not yet credited) before crediting the loan — no new charge is made.","enum":["cash","check","money_order","money_transfer","card"],"type":"string"},"network":{"description":"method=money_transfer only","enum":["moneygram","western_union"],"type":"string"},"payoff_quote_id":{"description":"Honor a quote from GET .../payoff: amount_cents must equal the quoted total and the posting day must be on or before good_through. The payment then closes the loan as a FULL payoff (per-diem that accrued since the quote's as-of day is waived as a system adjustment; precomputed / CAB rebates and pawn redemption run as for any payoff). 422 payoff_quote_mismatch / payoff_quote_expired carry a fresh_quote; 409 payoff_quote_used; 422 payoff_quote_unknown. Fail-closed: a loan carrying held credit, a loan that moved by anything but interest since the quote, or a product whose interest posts only at statement cut answers payoff_quote_mismatch — servicing prepares that payoff.","type":"string"},"posted_by":{"properties":{"external_id":{"description":"Max 64","type":"string"},"name":{"description":"Max 128","type":"string"},"subject":{"description":"Max 255: the teller's identity-provider subject when the register user signed in via SSO; persisted on every ledger row and resolved to the LMS staff user with the same subject","type":"string"}},"type":"object"},"receipt_number":{"description":"The register's BARE counter receipt number — letters, digits, - _ . / (max 64, no spaces; 422 invalid_receipt_number otherwise): stamped on every ledger row and rendered as 'register receipt #N' on LMS receipts","type":"string"},"recovery_of_external_ref":{"description":"NSF recovery: the register ref (same external_source) of the RETURNED (voided) payment this cash recovers. Attribution only: the payment allocates normally and the return's NSF fee stands. 409 recovery_target_not_returned when the ref is unknown or the payment is still live.","type":"string"},"reference":{"description":"method=money_transfer only: the network reference number (MTCN), max 128; persisted on every ledger row as payment_reference","type":"string"},"store_uuid":{"description":"Point-of-receipt store","type":"string"},"when":{"format":"date-time","type":"string"}},"required":["amount_cents"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentResult"}}},"description":"Posted (or the original posting replayed, replayed=true). payment_ref = transaction_uuids[0]: stamp it on your ledger row; GET .../payments/{key} reports the same value. receipt = the retained counter receipt (number, ReceiptView, receipt_lines, balance_after_cents, next_due_date, paid_in_full): print it as the authoritative slip when its figures differ from the pre-ring preview"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"invalid_request / invalid_amount"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"loan_not_found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"loan_not_payable / idempotency_conflict / external_ref_conflict (original_* fields included) / gateway_txn_already_used / payoff_quote_used / recovery_target_not_returned"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"idempotency_key_required (no Idempotency-Key / idempotency_key and no external_source+external_ref) / invalid_method / invalid_network / invalid_reference / invalid_external_ref / invalid_store / cash_tender_register_only / principal_only_* / gateway_txn_required / gateway_txn_not_found / gateway_txn_not_settled / gateway_amount_mismatch / payoff_quote_unknown / payoff_quote_expired / payoff_quote_mismatch (fresh_quote included)"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"payment_failed — safe to retry with the SAME key"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"external_refs_unavailable / gateway_unavailable / payoff_quotes_unavailable — retry with the same key"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Post a payment (register bridge)","tags":["Money"],"x-required-permission":"payments:create"}},"/loans/{loan_uuid}/payments/void":{"post":{"description":"Atomically reverses every row one payment POST created. Select the group by idempotency_key XOR external_source+external_ref. Maker-checker: a transaction_void approval policy parks this as 202 pending_approval — poll GET .../payments/{key}. Idempotent: voiding an already-voided group returns 200 with the terminal state.","parameters":[{"description":"","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"external_ref":{"description":"","type":"string"},"external_source":{"description":"","type":"string"},"idempotency_key":{"description":"Group selector (XOR with external_source+external_ref)","type":"string"},"posted_by":{"properties":{"external_id":{"description":"","type":"string"},"name":{"description":"","type":"string"}},"type":"object"},"when":{"format":"date-time","type":"string"}},"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentGroup"}}},"description":"Voided (or already-terminal state)"},"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}},"description":"Parked in the maker-checker queue (company approval policy covers this action). Nothing was executed; the request appears in Pending Approvals for sign-off."},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"group_not_found on THIS loan"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"approval_duplicate / external_ref_conflict / gateway_void_required (a gateway-settled card payment with no gateway wired)"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Selector invalid: provide idempotency_key XOR external_source+external_ref"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"void_failed (retryable)"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"approval_unavailable / gateway_void_failed (the gateway could not return a gateway-settled card payment; nothing was voided, retry)"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Void a whole payment group","tags":["Money"],"x-required-permission":"payments:edit"}},"/loans/{loan_uuid}/payments/{key}":{"get":{"parameters":[{"description":"","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}},{"description":"The idempotency key used when posting","in":"path","name":"key","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentGroup"}}},"description":"Group status (payment_ref = the first ledger row, the same value POST .../payments returned; void_pending_approval=true while a void is parked in the approval queue)"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"group_not_found"}},"summary":"Poll a payment group by idempotency key","tags":["Money"]}},"/loans/{loan_uuid}/payoff":{"get":{"description":"Issues a payoff quote from the production payoff engine and persists it: total_cents is the amount owed if the borrower pays ON the civil day as_of (today ET by default; never in the past), that day's per-diem included. Honor it with POST .../payments {payoff_quote_id, amount_cents = total_cents} on or before good_through: the loan closes as a full payoff. 409 payoff_quote_unavailable when the engine cannot project the loan's charges exactly (the amount must then be prepared by servicing).","parameters":[{"description":"","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}},{"description":"YYYY-MM-DD, today ET by default","in":"query","name":"as_of","schema":{"format":"date","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PayoffQuote"}}},"description":"The quote"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"invalid_as_of"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"loan_not_found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"payoff_quote_unavailable"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"payoff_quotes_unavailable"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Counter payoff quote","tags":["Money"],"x-required-permission":"loans:view | payments:view"}},"/loans/{loan_uuid}/rate-override":{"post":{"description":"Sets a per-loan negotiated APR (basis points) with a reason code from the catalog (retention, hardship, price_match, relationship, error_correction, legal_settlement, promotional, other). Refused outside the product's negotiable range (interest.negotiable_apr_min_bps / negotiable_apr_max_bps). The TILA disclosure is re-issued at the new rate. When the company requires approval for rate overrides the request is parked: 202 with approval_request_uuid, and the current rate stays in effect until approved.","parameters":[{"description":"","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"new_apr_bps":{"description":"0-100000","type":"integer"},"notes":{"description":"Max 500; audit trail","type":"string"},"reason_code":{"description":"Catalog code","type":"string"}},"required":["new_apr_bps","reason_code"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"loan":{"$ref":"#/components/schemas/Loan"},"loan_uuid":{"description":"","type":"string"},"new_apr_bps":{"description":"","type":"integer"},"old_apr_bps":{"description":"Prior override, null when the loan was on the product rate","type":"integer"},"queued":{"type":"boolean"}},"type":"object"}}},"description":"Applied"},"202":{"content":{"application/json":{"schema":{"properties":{"approval_request_uuid":{"description":"","type":"string"},"loan_uuid":{"description":"","type":"string"},"queued":{"type":"boolean"}},"type":"object"}}},"description":"Queued for approval"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid body or reason code"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Loan not found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Outside the negotiable range or refused"},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not configured"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Apply a negotiated APR","tags":["Loans"],"x-required-permission":"loans:edit"}},"/loans/{loan_uuid}/rate-override/clear":{"post":{"description":"Removes the negotiated APR so the loan returns to the product rate; the TILA disclosure is re-issued. When clearing would RAISE the borrower's rate and the company requires approval for rate overrides, the clear is parked like an increase: 202 with approval_request_uuid and the negotiated rate stays in effect until approved. Idempotent.","parameters":[{"description":"","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"notes":{"description":"","type":"string"},"reason_code":{"description":"Catalog code","type":"string"}},"required":["reason_code"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"cleared":{"type":"boolean"},"loan":{"$ref":"#/components/schemas/Loan"},"loan_uuid":{"description":"","type":"string"}},"type":"object"}}},"description":"Cleared"},"202":{"content":{"application/json":{"schema":{"properties":{"approval_request_uuid":{"description":"","type":"string"},"loan_uuid":{"description":"","type":"string"},"queued":{"type":"boolean"}},"type":"object"}}},"description":"Queued for approval"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid reason code"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Loan not found"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Clear a negotiated APR","tags":["Loans"],"x-required-permission":"loans:edit"}},"/loans/{loan_uuid}/receipts":{"get":{"description":"Every receipt the LMS rendered for the loan (teller, borrower portal, counter reference, API), newest first, including the CHECK HELD / CHECK RELEASED custody slips (kind held_check, issued when a held check is taken into custody — the register's custody filing or the LMS record — and when it leaves) and the renewal / extension statements (kind renewal). A voided posting keeps its receipt (voided=true, voided_by_receipt_uuid) and gains a kind=void receipt referencing it. Retained at least two years (retain_until).","parameters":[{"description":"","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"receipts":{"items":{"$ref":"#/components/schemas/LoanReceipt"},"type":"array"}},"type":"object"}}},"description":"Receipts"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Loan not found"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Receipt retention unavailable"}},"summary":"List the receipts retained for a loan","tags":["Money"]}},"/loans/{loan_uuid}/receipts/{receipt_uuid}":{"get":{"description":"Returns the receipt metadata, the structured ReceiptView the surface rendered (`view`: amount, allocation, balances after, paid_in_full, next due, store licence block, renewal / held_check blocks), the exact HTML the customer saw (`html`) for reprinting at the counter, and `receipt_lines` (ReceiptLine[]: the labelled disclosure lines).","parameters":[{"description":"","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}},{"description":"","in":"path","name":"receipt_uuid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"html":{"description":"Rendered receipt document","type":"string"},"receipt":{"$ref":"#/components/schemas/LoanReceipt"},"view":{"description":"ReceiptView JSON","type":"object"}},"type":"object"}}},"description":"Receipt"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Loan or receipt not found"}},"summary":"One retained receipt with its view and rendered HTML","tags":["Money"]}},"/loans/{loan_uuid}/refinance":{"post":{"description":"Pays the loan off in full and opens a NEW loan carrying the rolled balance (roll_balance, default true) plus any new_advance_cents, optionally on another fixed-installment product at a pinned rate/term. The prior loan is closed and linked to the new one; the new loan gets its own TILA disclosure. A loan is refinanced at most once and the call is resume-safe (a retry after a timeout completes the same restructure). Single-pay loans renew via rollover/EPP, CSO/CAB loans are refused, and a line of credit must use convert-loc or name an installment new_loan_type_uuid.","parameters":[{"description":"","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"buydown_cents":{"description":"\u003e= 0; a borrower paydown posted to the prior loan before payoff (kind buydown); not combined with new_advance_cents","type":"integer"},"buydown_method":{"description":"Tender label for the paydown receipt (Cash, Check #123, …)","type":"string"},"new_advance_cents":{"description":"\u003e= 0; additional cash on the new loan","type":"integer"},"new_apr_bps":{"description":"0-100000; blank keeps the product rate","type":"integer"},"new_loan_type_uuid":{"description":"Fixed-installment product; blank keeps the current product","type":"string"},"new_term_months":{"description":"0-600; 0 keeps the product term","type":"integer"},"reason":{"description":"Max 500","type":"string"},"roll_balance":{"description":"Carry the full outstanding balance into the new loan (default true)","type":"boolean"}},"required":["reason"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefinanceResult"}}},"description":"Restructure result"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid body"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Loan not found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Loan already refinanced or converted"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Refused: not active, no value, CSO/CAB, product mismatch, origination guard, or a product refinance rule (codes refinance_disabled, kind_not_allowed, max_count_reached, min_principal_reduction, min_payments_before, min_days_before, defaulted_not_allowed)"},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Refinance not configured"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Refinance into a new loan","tags":["Loans"],"x-required-permission":"loans:edit"}},"/loans/{loan_uuid}/refinance/cancel-pending":{"post":{"description":"A refinance records its intent before any money moves and is resumed by retrying with the same terms. This abandons a pending restructure that has NOT yet posted a buydown receipt or opened its replacement loan, so a new refinance with different terms can start. 409 cannot_cancel once money moved (retry it to completion instead); 404 when nothing is pending. A refinance retry naming different terms than the pending one answers 409 pending_terms_differ.","parameters":[{"description":"","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"cancelled":{"type":"boolean"}},"type":"object"}}},"description":"Cancelled"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Nothing pending"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Cannot cancel: money moved or replacement opened"},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Refinance not configured"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Cancel a pending refinance","tags":["Loans"],"x-required-permission":"loans:edit"}},"/loans/{loan_uuid}/refunds":{"post":{"parameters":[{"description":"","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"amount_cents":{"description":"\u003e 0","type":"integer"},"description":{"description":"","type":"string"},"when":{"format":"date-time","type":"string"}},"required":["amount_cents"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Loan"}}},"description":"Updated loan"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid amount or refund refused"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Post a refund","tags":["Money"],"x-required-permission":"refunds:create"}},"/loans/{loan_uuid}/repayment-plan":{"get":{"description":"The loan's standing against the governing state's post-default repayment-plan rule (Nevada NRS 604A.475 / 604A.5083: a written offer within 15 days of default, 30 days to accept, at least 90 days to repay, no additional interest or fees, and no civil action, ADR or repossession while the offer stands or the plan is current), the open offer / plan with its installments, the adverse actions it currently holds (civil_action, repossession, placement, charge_off, credit_report_default, dunning), and the history.","parameters":[{"description":"Loan UUID","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RepaymentPlanStanding"}}},"description":"Repayment plan standing"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Loan not found"},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Repayment plans not configured"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Post-default repayment plan standing","tags":["Loans"],"x-required-permission":"loans:view"}},"/loans/{loan_uuid}/repayment-plan/decline":{"post":{"description":"Closes the open offer as declined; the adverse-action holds release. 409 repayment_plan_no_offer when nothing is open.","parameters":[{"description":"Loan UUID","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"reason":{"description":"Optional, audit","type":"string"}},"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RepaymentPlan"}}},"description":"Offer declined"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Loan not found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"repayment_plan_no_offer"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Record the borrower's refusal of the offer","tags":["Loans"],"x-required-permission":"loans:edit"}},"/loans/{loan_uuid}/repayment-plan/elect":{"post":{"description":"Lays the loan's LOCKED balance out as equal installments over at least the plan term through the promise-plan rails, freezes interest and fees for the plan window, and keeps the adverse-action holds in force. Idempotent: an already-active plan returns 200. 409 repayment_plan_no_offer when nothing is open, repayment_plan_offer_expired after accept_by.","parameters":[{"description":"Loan UUID","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"auto_draft":{"type":"boolean"},"payment_method_uuid":{"description":"Optional stored bank method to auto-draft the installments","type":"string"}},"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RepaymentPlan"}}},"description":"Plan active"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Loan not found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"repayment_plan_no_offer | repayment_plan_offer_expired"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"repayment_plan_loan_not_active | repayment_plan_no_balance"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Accept the open repayment-plan offer","tags":["Loans"],"x-required-permission":"loans:edit"}},"/loans/{loan_uuid}/repayment-plan/offer":{"post":{"description":"Creates the written offer. The automatic offer fires the day a loan defaults where the state mandates it; this endpoint covers voluntary offers (product collections.repayment_plan.enabled) and a missed mandated offer. 201 with the offer; 409 repayment_plan_already_open when an offer or plan is already open; 422 repayment_plan_not_applicable when neither the state nor the product allows one.","parameters":[{"description":"Loan UUID","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"reason":{"description":"Optional, audit","type":"string"}},"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RepaymentPlan"}}},"description":"Offer created"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Loan not found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"repayment_plan_already_open"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"repayment_plan_not_applicable | repayment_plan_loan_not_active | repayment_plan_no_balance"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Offer a repayment plan by hand","tags":["Loans"],"x-required-permission":"loans:edit"}},"/loans/{loan_uuid}/repossession":{"get":{"description":"The loan's open (else latest) repossession case with its dates, costs and status, the pledged vehicle (incl. inventory_item_uuid once the register took it in), the governing state's rule (non_recourse: a deficiency is written down at the sale; surplus_to_borrower: the overage is queued for refund), can_record_sale with the refusal reason, the post-sale accounting once sold, and a redemption quote while the case is open.","parameters":[{"description":"Loan UUID","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RepossessionStanding"}}},"description":"Standing"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Loan not found"},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Repossession not configured"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Repossession standing","tags":["Loans"],"x-required-permission":"loans:view | collections:view"}},"/loans/{loan_uuid}/repossession/record-sale":{"post":{"description":"Settles the loan's scheduled sale (status sale_scheduled): net = sale_cents − costs_cents applies to the balance through the payment rail exactly once; the state rule decides the rest — surplus_to_borrower routes the overage into the overpayment-refund queue (surplus_refund_due_cents), non_recourse writes any shortfall down (deficiency_cents 0, non_recourse_settled true), otherwise the deficiency stays collectible. Buyer, store and teller land on the case and on repossession.sold. A sale already recorded with the same figures answers status replayed; different figures on a crashed settlement are refused.","parameters":[{"description":"Loan UUID","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"buyer":{"description":"max 255","type":"string"},"costs_cents":{"description":"Sale costs (auction fees, transport)","type":"integer"},"posted_by":{"properties":{"external_id":{"description":"","type":"string"},"name":{"description":"","type":"string"},"subject":{"description":"","type":"string"}},"type":"object"},"sale_cents":{"description":"Gross sale proceeds","type":"integer"},"sold_at":{"description":"YYYY-MM-DD (ET civil day) or RFC3339; default today","type":"string"},"store_uuid":{"description":"Store that sold the vehicle","type":"string"}},"required":["sale_cents"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"posted_by":{"type":"object"},"sale":{"$ref":"#/components/schemas/RepossessionSale"},"status":{"description":"","type":"string"}},"type":"object"}}},"description":"Sold (status sold | replayed)"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"invalid_amount | invalid_sold_at"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Loan not found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"no_open_case | sale_not_scheduled | sale_already_recorded | loan_paid_off | store_not_permitted"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"not_repossessable | state_rule_blocked (sale inside the redemption window / minimum sale wait) | invalid_store | invalid_buyer | invalid_posted_by"},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Repossession not configured"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Record the disposition sale from the counter","tags":["Money"],"x-required-permission":"collections:edit | payments:create"}},"/loans/{loan_uuid}/rollover":{"get":{"description":"Where a single-pay (payday) loan stands against the governing state's rollover registry: the roll number it would take, max_rollovers and how many remain, the minimum principal paydown a roll must collect, the fee the renewal would assess, the lifetime interest+fee cap where one applies (MO), and whether a roll with the minimum paydown would be accepted now (eligible=false carries the refusing rule and reason). Read-only.","parameters":[{"description":"Loan UUID","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RolloverEligibility"}}},"description":"Rollover standing"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Loan not found"},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rollover not configured"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Rollover standing","tags":["Loans"],"x-required-permission":"loans:view"},"post":{"description":"Closes the single-pay loan and opens a NEW single-pay loan for the carried balance (the full outstanding balance less buydown_cents, the principal paydown collected at the roll); the renewal assesses its own term fee on the state's fee basis and the two loans are linked in the rollover chain. The governing state's registry is enforced from the registry and fails closed: max_rollovers, the minimum paydown (from the Nth roll where the state says so), the renewal term floor and the lifetime cost cap. A company that opts the single_pay_rollover action into maker-checker review gets 202 pending_approval and nothing moves until a reviewer approves. A loan rolls at most once; the call is resume-safe (a retry after a timeout completes the same roll).","parameters":[{"description":"Loan UUID","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"buydown_cents":{"description":"Principal paydown collected at the roll; must meet the state minimum (see GET). Default 0","type":"integer"},"reason":{"description":"Audit reason (required)","type":"string"}},"required":["reason"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RolloverResult"}}},"description":"Rolled"},"202":{"content":{"application/json":{"schema":{"properties":{"approval_uuid":{"description":"","type":"string"},"prior_loan_uuid":{"description":"","type":"string"},"status":{"description":"pending_approval","type":"string"}},"type":"object"}}},"description":"Pending maker-checker approval; nothing moved"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid body"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Loan not found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Loan already rolled (already_rolled)"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Refused: state registry rule (state_rule_blocked — max_rollovers, rollover_min_buydown_pct_bps, title_max_renewals, title_min_renewal_paydown_bps, lifetime_cost_cap_pct_bps), loan not active (loan_not_active), not a single-pay product (not_single_pay), or an origination refusal on the renewal (refused)"},"501":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rollover not configured"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Roll a single-pay loan into a new term","tags":["Loans"],"x-required-permission":"loans:edit"}},"/loans/{loan_uuid}/simulate":{"post":{"description":"Steps the loan day-by-day (or monthly) applying interest / late fees / statement cuts as flagged, plus your custom payment/draw events. Range capped at 10 years. Events outside [start,end] are dropped and reported. Includes the TILA disclosure when available.","parameters":[{"description":"","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"apply_interest":{"description":"","type":"boolean"},"apply_late_fee":{"description":"","type":"boolean"},"custom_events":{"items":{"properties":{"amount_cents":{"description":"","type":"integer"},"at":{"format":"date-time","type":"string"},"description":{"description":"","type":"string"},"kind":{"enum":["payment","draw"],"type":"string"}},"type":"object"},"type":"array"},"cut_statements":{"description":"","type":"boolean"},"end":{"description":"Default start+30d","format":"date-time","type":"string"},"frequency":{"enum":["daily","monthly"],"type":"string"},"start":{"format":"date-time","type":"string"}},"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"dropped_events":{"items":{"type":"object"},"type":"array"},"paydate_schedule":{"items":{"format":"date-time","type":"string"},"type":"array"},"steps":{"items":{"type":"object"},"type":"array"},"tila":{"type":"object"},"transactions_simulated":{"items":{"type":"object"},"type":"array"}},"type":"object"}}},"description":"Simulation result"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Range exceeds 10 years"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Project the loan forward (nothing persists)","tags":["Simulation"],"x-required-permission":"loans:view"}},"/loans/{loan_uuid}/statements":{"post":{"parameters":[{"description":"","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"as_of":{"format":"date-time","type":"string"},"auto_email":{"description":"Send via the email service","type":"boolean"},"email_to":{"description":"Override recipient","type":"string"}},"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Statement document"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Failed"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Generate a statement document (view/email)","tags":["Statements"],"x-required-permission":"loans:edit"}},"/loans/{loan_uuid}/statements/cut":{"post":{"description":"Leasing products refuse direct cut (their statement pipeline bills rent).","parameters":[{"description":"","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"as_of":{"description":"Effective timestamp for the operation (default: now)","format":"date-time","type":"string"}},"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"properties":{"current_min_due_cents":{"description":"","type":"integer"},"due_date":{"format":"date-time","type":"string"},"end":{"format":"date-time","type":"string"},"min_payment_cents":{"description":"Minimum payment due: past due + this period's minimum","type":"integer"},"new_charges_cents":{"description":"","type":"integer"},"past_due_cents":{"description":"","type":"integer"},"payments_applied_cents":{"description":"","type":"integer"},"prior_balance_cents":{"description":"","type":"integer"},"start":{"format":"date-time","type":"string"},"total_due_cents":{"description":"","type":"integer"},"total_min_due_cents":{"description":"","type":"integer"},"transactions":{"items":{"type":"object"},"type":"array"}},"type":"object"}}},"description":"Statement"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Failed (e.g. leasing product)"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Cut a statement (updates loan state + due date)","tags":["Statements"],"x-required-permission":"loans:edit"}},"/loans/{loan_uuid}/title-custody":{"post":{"description":"Records where the physical title certificate is on a title-secured loan (title loan or title pawn) and emits title.custody_changed. status: held (the store holds the paper); returned (paper handed back to the borrower — requires a zero balance); released_to_borrower (lien released AND paper handed back — requires a zero balance); lien_released (lien released, paper custody unchanged). A forfeited pledge's title is the pawnbroker's (409 pawn_forfeited). Recording the state the vehicle already reads answers status unchanged (idempotent).","parameters":[{"description":"Loan UUID","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"note":{"description":"max 1000","type":"string"},"posted_by":{"properties":{"external_id":{"description":"","type":"string"},"name":{"description":"","type":"string"},"subject":{"description":"","type":"string"}},"type":"object"},"reference":{"description":"Lien release reference (max 128)","type":"string"},"status":{"description":"returned | held | released_to_borrower | lien_released","type":"string"},"store_uuid":{"description":"","type":"string"},"when":{"description":"YYYY-MM-DD or RFC3339; default now","type":"string"}},"required":["status"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"custody":{"properties":{"action":{"description":"","type":"string"},"collateral_uuid":{"description":"","type":"string"},"lien_status":{"description":"","type":"string"},"lien_status_before":{"description":"","type":"string"},"title_custody":{"description":"","type":"string"},"title_custody_before":{"description":"","type":"string"},"unchanged":{"description":"","type":"boolean"},"when":{"description":"RFC3339","type":"string"}},"type":"object"},"status":{"description":"","type":"string"}},"type":"object"}}},"description":"Recorded (status recorded | unchanged)"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"invalid_status | invalid_when"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Loan not found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"balance_outstanding | pawn_forfeited | illegal_transition | store_not_permitted"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"no_title_collateral | invalid_request | invalid_posted_by"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Record paper-title custody / lien release from the counter","tags":["Loans"],"x-required-permission":"loans:edit | payments:create"}},"/loans/{loan_uuid}/title-fee-receipt":{"post":{"description":"A title product that collects its title fees in cash (title_fee_payer=borrower_cash) receipts them WITH the origination when the LMS owns cash. On a company whose cash tender source is the register the origination refuses a direct receipt (422 cash_tender_register_only on POST /loans) and originates with the fee posted and the receipt PENDING; the register collects the fee and posts it here. amount_cents must equal the fee decided at origination (lien + processing). Exactly-once per register transaction: external_source+external_ref is claimed as the (company, source, ref) anchor inside the money write — a replay with the same reference answers 200 with replayed=true; the same reference under a different key is 409 external_ref_conflict with the original posting. A different reference against a loan already receipted is 409 title_fee_not_pending. On an LMS-owned company the endpoint records a late counter receipt without an external reference (one per loan, keyed on Idempotency-Key when sent).","parameters":[{"description":"","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}},{"description":"Client-generated key (max 128 chars); reuse the same value on retries. Takes precedence over the idempotency_key body field.","in":"header","name":"Idempotency-Key","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"amount_cents":{"description":"Must equal the fee due at origination","type":"integer"},"check_number":{"description":"For method=check","type":"string"},"external_display":{"description":"Register receipt number (max 255)","type":"string"},"external_ref":{"description":"Register txn id (max 128); the exactly-once key","type":"string"},"external_source":{"description":"cash_register (required on a register-owned company; max 64)","type":"string"},"idempotency_key":{"description":"Max 128; header wins","type":"string"},"method":{"enum":["cash","check","money_order"],"type":"string"},"posted_by":{"properties":{"external_id":{"description":"Max 64","type":"string"},"name":{"description":"Max 128","type":"string"}},"type":"object"},"store_uuid":{"description":"Point of receipt (must be a store in this company)","type":"string"},"when":{"format":"date-time","type":"string"}},"required":["amount_cents","method"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TitleFeeReceipt"}}},"description":"Replay — the receipt on file for this reference (receipt included)"},"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TitleFeeReceipt"}}},"description":"Receipt posted; receipt = the retained counter receipt (CounterReceipt)"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"invalid_request / invalid_amount / invalid_method"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"loan_not_found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"title_fee_not_pending / title_fee_not_assessed (fee not on the ledger yet — pending signature, or a fee row voided; not retryable) / title_fee_already_settled (the fee bucket was already paid down by an ordinary payment; not retryable — reverse the register sale or ring the cash as a loan payment) / external_ref_conflict (original_* fields) / title_fee_receipt_conflict / period_closed"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"title_fee_amount_mismatch (expected_cents) / invalid_external_ref / invalid_store / cash_tender_register_only / invalid_posted_by"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"receipt_failed — safe to retry with the same external_ref"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"external_refs_unavailable"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Counter receipt for cash-at-origination title fees","tags":["Money"],"x-required-permission":"loans:edit | payments:create"}},"/loans/{loan_uuid}/title-fee-receipt/void":{"post":{"description":"The register's reversal of a posted receipt. Names the receipt by external_source+external_ref (must match the receipt on file; omit both to void whatever receipt is on file). The receipt row is voided through the ordinary void path (balance re-opened, payment.voided emitted carrying the external reference) and the fee is owed again: a corrected receipt may post under a NEW reference. Already voided → 409 title_fee_receipt_voided. Maker-checker: the company's transaction_void approval policy gates this exactly as /transactions/{uuid}/void (register reversals included) — when review is required the response is 202 pending_approval and the void executes on approval.","parameters":[{"description":"","in":"path","name":"loan_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"external_ref":{"description":"The receipt being reversed","type":"string"},"external_source":{"description":"","type":"string"},"posted_by":{"properties":{"external_id":{"description":"","type":"string"},"name":{"description":"","type":"string"}},"type":"object"},"reason":{"description":"Max 255","type":"string"},"when":{"format":"date-time","type":"string"}},"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TitleFeeReceipt"}}},"description":"Voided"},"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}},"description":"Parked in the maker-checker queue (company approval policy covers this action). Nothing was executed; the request appears in Pending Approvals for sign-off."},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"loan_not_found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"title_fee_receipt_voided (already) / title_fee_not_pending (no such receipt on file) / approval_duplicate / period_closed"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"approval_unavailable"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Reverse a title-fee counter receipt","tags":["Money"],"x-required-permission":"loans:edit | payments:edit"}},"/manage/users":{"get":{"description":"Scope = intersection of the API key's companies and the acting user's companies. standard-role actors are refused.","parameters":[{"description":"Default 50","in":"query","name":"limit","required":false,"schema":{"type":"integer"}},{"description":"","in":"query","name":"offset","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/User"},"type":"array"}}},"description":"Users"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Insufficient role"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"List staff users (scoped)","tags":["Users"],"x-required-permission":"users:view"},"post":{"description":"company-role actors cannot create super users and are limited to their own companies; the API key's company scope binds everyone.","requestBody":{"content":{"application/json":{"schema":{"properties":{"companies":{"items":{"type":"string"},"type":"array"},"email":{"description":"","type":"string"},"name":{"description":"","type":"string"},"password":{"description":"","type":"string"},"role":{"enum":["standard","company","super","teller"],"type":"string"}},"required":["email","name","role","password","companies"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}}},"description":"Created"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Role/company scope refused"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Create a staff user","tags":["Users"],"x-required-permission":"users:create"}},"/manage/users/password/reset/cancel":{"post":{"requestBody":{"content":{"application/json":{"schema":{"properties":{"token":{"description":"","type":"string"}},"required":["token"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Canceled"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid or expired token"}},"security":[{"apiKey":[],"companyHeader":[]}],"summary":"Cancel a staff password-reset token","tags":["Auth"]}},"/manage/users/password/reset/confirm":{"post":{"requestBody":{"content":{"application/json":{"schema":{"properties":{"new_password":{"description":"","type":"string"},"token":{"description":"","type":"string"}},"required":["token","new_password"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Password updated"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid or expired token"}},"security":[{"apiKey":[],"companyHeader":[]}],"summary":"Confirm a staff password reset","tags":["Auth"]}},"/manage/users/password/reset/request":{"post":{"description":"Always answers 200 (no account enumeration). Rate-limited 5/(company,IP)/15min.","requestBody":{"content":{"application/json":{"schema":{"properties":{"email":{"description":"","type":"string"}},"required":["email"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"status":{"description":"","type":"string"}},"type":"object"}}},"description":"Accepted (whether or not the email exists)"},"429":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Rate limited"}},"security":[{"apiKey":[],"companyHeader":[]}],"summary":"Request a staff password reset","tags":["Auth"]}},"/manage/users/{user_uuid}":{"put":{"description":"Cross-tenant targets answer 404 (indistinguishable from unknown). company-role actors cannot touch super users.","parameters":[{"description":"","in":"path","name":"user_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"email":{"description":"","type":"string"},"enabled":{"description":"","type":"boolean"},"name":{"description":"","type":"string"},"role":{"description":"","type":"string"}},"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Status"}}},"description":"Updated"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Refused"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Update a staff user","tags":["Users"],"x-required-permission":"users:edit"}},"/manage/users/{user_uuid}/companies":{"put":{"parameters":[{"description":"","in":"path","name":"user_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"companies":{"items":{"type":"string"},"minItems":1,"type":"array"}},"required":["companies"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Status"}}},"description":"Updated"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Empty list refused"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Scope refused"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Set a staff user's companies (non-empty)","tags":["Users"],"x-required-permission":"users:edit"}},"/manage/users/{user_uuid}/password":{"put":{"parameters":[{"description":"","in":"path","name":"user_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"password":{"description":"","type":"string"}},"required":["password"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Status"}}},"description":"Updated"},"403":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Refused"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Set a staff user's password","tags":["Users"],"x-required-permission":"users:edit"}},"/openapi.json":{"get":{"responses":{"200":{"description":"OpenAPI 3.0 document"}},"security":[],"summary":"This OpenAPI document","tags":["Discovery"]}},"/reports/aging":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"additionalProperties":{"type":"integer"},"type":"object"}}},"description":"Bucket → loan count"}},"summary":"Delinquency aging buckets","tags":["Reports"]}},"/reports/lending/lenders":{"get":{"parameters":[{"description":"RFC3339 point-in-time (default now)","in":"query","name":"as_of","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"type":"object"},"type":"array"}}},"description":"Lender rows"}},"summary":"Per-lender allocation / in-use / past-due","tags":["Reports"]}},"/reports/lending/pools":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"items":{"type":"object"},"type":"array"}}},"description":"Pools with members and totals"}},"summary":"Pool allocation rollup","tags":["Reports"]}},"/reports/lending/pools/health":{"get":{"parameters":[{"description":"RFC3339 (default now)","in":"query","name":"as_of","required":false,"schema":{"type":"string"}},{"description":"Filter to one pool","in":"query","name":"pool_uuid","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"type":"object"},"type":"array"}}},"description":"Health rows"}},"summary":"Pool health: utilization, delinquency buckets, NPL, DPD","tags":["Reports"]}},"/reports/outstanding":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"total_outstanding_cents":{"description":"","type":"integer"}},"type":"object"}}},"description":"Total"}},"summary":"Total outstanding principal","tags":["Reports"]}},"/reports/past-due":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"loans_past_due":{"description":"","type":"integer"}},"type":"object"}}},"description":"Count"}},"summary":"Count of past-due loans","tags":["Reports"]}},"/rewards/accounts/{borrower_uuid}":{"get":{"description":"Read-only; a borrower who has never earned answers zero balances (no account is created by a GET).","parameters":[{"description":"","in":"path","name":"borrower_uuid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"balance_cents":{"description":"At the CURRENT rate","type":"integer"},"balance_points":{"description":"","type":"integer"},"borrower_uuid":{"description":"","type":"string"},"enabled":{"description":"","type":"boolean"},"last_activity_at":{"format":"date-time","type":"string"},"lifetime_earned":{"description":"","type":"integer"},"lifetime_expired":{"description":"","type":"integer"},"lifetime_redeemed":{"description":"","type":"integer"},"points_name":{"description":"","type":"string"},"program_name":{"description":"","type":"string"},"rate_cents":{"description":"","type":"integer"},"rate_points":{"description":"","type":"integer"},"referral_code":{"description":"","type":"string"},"status":{"enum":["active","frozen"],"type":"string"}},"type":"object"}}},"description":"Points account"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Borrower not found (or another tenant's)"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"A customer's points account","tags":["Rewards"],"x-required-permission":"rewards:view"}},"/rewards/accounts/{borrower_uuid}/ledger":{"get":{"parameters":[{"description":"","in":"path","name":"borrower_uuid","required":true,"schema":{"type":"string"}},{"description":"Default 50","in":"query","name":"limit","required":false,"schema":{"type":"integer"}},{"description":"","in":"query","name":"offset","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"items":{"items":{"properties":{"balance_after":{"description":"","type":"integer"},"description":{"description":"","type":"string"},"dt_created":{"format":"date-time","type":"string"},"entry_type":{"enum":["earn","redeem","adjust","expire","reverse"],"type":"string"},"entry_uuid":{"description":"","type":"string"},"points_delta":{"description":"Signed","type":"integer"},"source":{"description":"","type":"string"}},"type":"object"},"type":"array"},"limit":{"description":"","type":"integer"},"offset":{"description":"","type":"integer"},"points_name":{"description":"","type":"string"},"program_name":{"description":"","type":"string"},"total":{"description":"","type":"integer"}},"type":"object"}}},"description":"Ledger page"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Borrower not found (or another tenant's)"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"A customer's points history (newest first)","tags":["Rewards"],"x-required-permission":"rewards:view"}},"/rewards/earn":{"post":{"description":"A partner system (loyalty engine, CRM, register bridge) credits points to a borrower's rewards account. Idempotent: the Idempotency-Key header (wins) or the idempotency_key body field replays the original ledger entry on retry with a matching fingerprint; a mismatched reuse answers 409. Unknown or cross-tenant borrower UUIDs answer 404. Per-award ceiling: 100,000,000 points.","parameters":[{"description":"Client-generated key (max 128 chars); reuse the same value on retries. Takes precedence over the idempotency_key body field.","in":"header","name":"Idempotency-Key","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"borrower_uuid":{"description":"","type":"string"},"idempotency_key":{"description":"Max 128; the header takes precedence","type":"string"},"points":{"description":"Positive integer points to award","type":"integer"},"reason":{"description":"Ledger description (max 512)","type":"string"},"source_ref":{"description":"Your system's reference id (max 128) — part of the idempotency fingerprint","type":"string"}},"required":["borrower_uuid","points"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"properties":{"balance_after":{"description":"","type":"integer"},"borrower_uuid":{"description":"","type":"string"},"dt_created":{"format":"date-time","type":"string"},"entry_type":{"description":"","type":"string"},"entry_uuid":{"description":"","type":"string"},"points":{"description":"","type":"integer"},"points_name":{"description":"","type":"string"},"program_name":{"description":"","type":"string"},"source":{"description":"","type":"string"},"source_ref":{"description":"","type":"string"}},"type":"object"}}},"description":"Points credited (or the original entry replayed)"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"borrower_uuid missing or points not positive"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Borrower not found (or another tenant's)"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Idempotency key reused for a different request, or account frozen"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Program not enabled, points over the per-award maximum, or field lengths exceeded"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Award points to a customer account","tags":["Rewards"],"x-required-permission":"rewards:create"}},"/rewards/referrals":{"post":{"description":"Records that a borrower was referred, by the referrer's shareable code OR their UUID (exactly one). Must predate the referred borrower's first funded loan; the points award fires when that loan funds. Referrer/referred identity overlap routes to pending_review instead of paying.","requestBody":{"content":{"application/json":{"schema":{"properties":{"code":{"description":"The referrer's shareable code (max 32)","type":"string"},"referred_borrower_uuid":{"description":"","type":"string"},"referrer_borrower_uuid":{"description":"Provide this OR code, not both","type":"string"}},"required":["referred_borrower_uuid"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"Referral recorded"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Missing/ambiguous referrer identification"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Borrower or referral code not found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Already referred, or already has a loan (referrals must predate first funding)"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Referral earning not enabled, or self-referral"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Link a referral","tags":["Rewards"],"x-required-permission":"rewards:create"}},"/statements/cut":{"post":{"description":"Returns the generated count even on partial failure.","requestBody":{"content":{"application/json":{"schema":{"properties":{"as_of":{"format":"date-time","type":"string"},"auto_email":{"description":"","type":"boolean"},"loan_uuids":{"items":{"type":"string"},"maxItems":500,"type":"array"}},"required":["loan_uuids"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"generated":{"description":"","type":"integer"}},"type":"object"}}},"description":"Count"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Over 500 loans"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Batch cut (max 500 loans)","tags":["Statements"],"x-required-permission":"loans:edit"}},"/statements/cut/auto":{"post":{"requestBody":{"content":{"application/json":{"schema":{"properties":{"as_of":{"format":"date-time","type":"string"},"auto_email":{"description":"","type":"boolean"}},"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"generated":{"description":"","type":"integer"}},"type":"object"}}},"description":"Count"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Failed"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Auto-select and cut due statements (cron-friendly)","tags":["Statements"],"x-required-permission":"loans:edit"}},"/stores":{"get":{"parameters":[{"description":"Name / city / store number","in":"query","name":"q","required":false,"schema":{"type":"string"}},{"description":"\"true\" to include deactivated","in":"query","name":"include_inactive","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"items":{"items":{"properties":{"city":{"description":"","type":"string"},"gl_segment":{"description":"ERP entity / segment code the GL export emits for this store","type":"string"},"is_default":{"description":"","type":"boolean"},"legal_entity_name":{"description":"Operating legal entity (licensee) the store originates under","type":"string"},"legal_entity_uuid":{"description":"","type":"string"},"license_expires":{"description":"YYYY-MM-DD; absent when none recorded","type":"string"},"license_number":{"description":"","type":"string"},"license_state":{"description":"","type":"string"},"license_type":{"description":"State regulatory license type","type":"string"},"name":{"description":"","type":"string"},"state":{"description":"","type":"string"},"store_number":{"description":"","type":"string"},"store_uuid":{"description":"","type":"string"},"timezone":{"description":"","type":"string"}},"type":"object"},"type":"array"}},"type":"object"}}},"description":"Stores"}},"summary":"Company stores / branches","tags":["Stores"]}},"/transactions/{txn_uuid}/void":{"post":{"description":"Maker-checker on the transaction_void action (inline by default until the company opts in at Settings → Approvals; the ORIGINAL transaction amount prices the threshold).","parameters":[{"description":"","in":"path","name":"txn_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"when":{"format":"date-time","type":"string"}},"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Status"}}},"description":"Voided"},"202":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingApproval"}}},"description":"Parked in the maker-checker queue (company approval policy covers this action). Nothing was executed; the request appears in Pending Approvals for sign-off."},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Transaction not found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"approval_duplicate"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"approval_unavailable"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Void a single transaction","tags":["Money"],"x-required-permission":"loans:edit"}},"/underwriting/applications":{"get":{"parameters":[{"description":"Application UUID or borrower search","in":"query","name":"q","required":false,"schema":{"type":"string"}},{"description":"pending|approved|declined|converted","in":"query","name":"status","required":false,"schema":{"type":"string"}},{"description":"","in":"query","name":"loan_type_uuid","required":false,"schema":{"type":"string"}},{"description":"","in":"query","name":"borrower_uuid","required":false,"schema":{"type":"string"}},{"description":"Created after (RFC3339)","in":"query","name":"start","required":false,"schema":{"type":"string"}},{"description":"Created before (RFC3339)","in":"query","name":"end","required":false,"schema":{"type":"string"}},{"description":"Default 50","in":"query","name":"limit","required":false,"schema":{"type":"integer"}},{"description":"","in":"query","name":"offset","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"items":{"items":{"$ref":"#/components/schemas/Application"},"type":"array"},"total":{"description":"","type":"integer"}},"type":"object"}}},"description":"Applications page"}},"summary":"List applications","tags":["Underwriting"]},"post":{"description":"The engine scores immediately; status lands pending / approved / declined. An archived loan_type_uuid auto-resolves to the active version in its family.","requestBody":{"content":{"application/json":{"schema":{"properties":{"application":{"description":"Arbitrary applicant data evaluated by the rules/scorecard","type":"object"},"borrower_uuid":{"description":"","type":"string"},"loan_type_uuid":{"description":"","type":"string"},"requested_cents":{"description":"\u003e 0","type":"integer"}},"required":["borrower_uuid","loan_type_uuid","requested_cents","application"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Application"}}},"description":"Submitted + decided"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"invalid_payload / borrower_not_found / loan_type_not_found"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Submit an application (auto-evaluated)","tags":["Underwriting"],"x-required-permission":"underwriting:create"}},"/underwriting/applications/{application_uuid}":{"delete":{"parameters":[{"description":"","in":"path","name":"application_uuid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Status"}}},"description":"Deleted"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Delete failed"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Delete (withdraw) an application","tags":["Underwriting"],"x-required-permission":"underwriting:delete"},"get":{"parameters":[{"description":"","in":"path","name":"application_uuid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Application"}}},"description":"Application"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"}},"summary":"Get an application","tags":["Underwriting"]}},"/underwriting/applications/{application_uuid}/convert-loan":{"post":{"description":"Atomic (compare-and-swap): a racing double-POST gets 409, never a duplicate loan. credit_limit_cents defaults to the approved (else requested) amount and may not exceed it. Approval expiry is enforced lazily (underwriting.approval_expiry_days). Pool auto-assign is best-effort (warning on failure).","parameters":[{"description":"","in":"path","name":"application_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"credit_limit_cents":{"description":"\u003e 0, ≤ approved amount","type":"integer"},"currency_code":{"description":"e.g. USD","type":"string"},"pool_uuid":{"description":"Optional lender pool","type":"string"}},"required":["currency_code"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"properties":{"application":{"description":"","type":"string"},"approved_amount_cents":{"description":"","type":"integer"},"credit_limit_cents":{"description":"","type":"integer"},"loan_uuid":{"description":"","type":"string"},"pool_auto_assigned":{"description":"","type":"string"},"warning":{"description":"Present when pool auto-assign failed","type":"string"}},"type":"object"}}},"description":"Converted"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Validation failed"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Already converted, or not in approved status"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"approval_expired"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Convert an approved application into a loan","tags":["Underwriting"],"x-required-permission":"loans:create"}},"/underwriting/applications/{application_uuid}/decision":{"put":{"description":"Irreversible once the application converts. approved_amount_cents caps the later conversion.","parameters":[{"description":"","in":"path","name":"application_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"acted_at":{"format":"date-time","type":"string"},"approved_amount_cents":{"description":"Optional cap for conversion","type":"integer"},"reason":{"description":"","type":"string"},"status":{"enum":["approved","declined"],"type":"string"},"user_name":{"description":"","type":"string"}},"required":["status"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Application"}}},"description":"Decided"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid status"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"application_converted / decision conflict (terminal or concurrent write)"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Manual decision (approve / decline)","tags":["Underwriting"],"x-required-permission":"underwriting:approve"}},"/underwriting/applications/{application_uuid}/decline-letter.pdf":{"get":{"parameters":[{"description":"","in":"path","name":"application_uuid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"PDF (attachment)"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not declined, or letter not generated"}},"summary":"Decline / adverse-action letter (PDF)","tags":["Underwriting"]}},"/underwriting/applications/{application_uuid}/documents":{"get":{"parameters":[{"description":"","in":"path","name":"application_uuid","required":true,"schema":{"type":"string"}},{"description":"Default 50","in":"query","name":"limit","required":false,"schema":{"type":"integer"}},{"description":"","in":"query","name":"offset","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"items":{"items":{"$ref":"#/components/schemas/DocumentMeta"},"type":"array"},"limit":{"description":"","type":"integer"},"offset":{"description":"","type":"integer"}},"type":"object"}}},"description":"Documents page"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"List failed"}},"summary":"List application documents","tags":["Documents"]},"post":{"parameters":[{"description":"","in":"path","name":"application_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"multipart/form-data":{"schema":{"properties":{"bucket":{"description":"Storage bucket hint","type":"string"},"description":{"description":"Human-readable description","type":"string"},"doc_type":{"description":"Document type/category","type":"string"},"file":{"description":"The document file","format":"binary","type":"string"}},"required":["file"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentMeta"}}},"description":"Uploaded"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"missing_file_part / upload_failed"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Upload an application document","tags":["Documents"],"x-required-permission":"underwriting:edit"}},"/underwriting/applications/{application_uuid}/documents/{document_uuid}":{"get":{"parameters":[{"description":"","in":"path","name":"application_uuid","required":true,"schema":{"type":"string"}},{"description":"","in":"path","name":"document_uuid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DocumentMeta"}}},"description":"Metadata"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"}},"summary":"Document metadata","tags":["Documents"]}},"/underwriting/applications/{application_uuid}/documents/{document_uuid}/download":{"get":{"parameters":[{"description":"","in":"path","name":"application_uuid","required":true,"schema":{"type":"string"}},{"description":"","in":"path","name":"document_uuid","required":true,"schema":{"type":"string"}},{"description":"\"1\" to view in-browser instead of attachment","in":"query","name":"inline","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Binary file"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"}},"summary":"Download the file","tags":["Documents"]}},"/underwriting/applications/{application_uuid}/logs":{"get":{"parameters":[{"description":"","in":"path","name":"application_uuid","required":true,"schema":{"type":"string"}},{"description":"Default 100","in":"query","name":"limit","required":false,"schema":{"type":"integer"}},{"description":"","in":"query","name":"offset","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"type":"object"},"type":"array"}}},"description":"Transitions"}},"summary":"Status-transition history","tags":["Underwriting"]}},"/underwriting/applications/{application_uuid}/notes":{"get":{"parameters":[{"description":"","in":"path","name":"application_uuid","required":true,"schema":{"type":"string"}},{"description":"Default 50","in":"query","name":"limit","required":false,"schema":{"type":"integer"}},{"description":"","in":"query","name":"offset","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"items":{"type":"object"},"type":"array"}}},"description":"Notes"}},"summary":"List notes","tags":["Underwriting"]},"post":{"parameters":[{"description":"","in":"path","name":"application_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"body":{"description":"Non-empty","type":"string"}},"required":["body"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"properties":{"body":{"description":"","type":"string"},"dt_created":{"format":"date-time","type":"string"},"note_uuid":{"description":"","type":"string"},"user_uuid":{"description":"","type":"string"}},"type":"object"}}},"description":"Note"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Empty body"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Add a note (immutable)","tags":["Underwriting"],"x-required-permission":"underwriting:edit"}},"/underwriting/applications/{application_uuid}/retry":{"post":{"parameters":[{"description":"","in":"path","name":"application_uuid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Application"}}},"description":"Re-evaluated application"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Retry failed"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Re-run the underwriting engine","tags":["Underwriting"],"x-required-permission":"underwriting:edit"}},"/webhooks":{"get":{"parameters":[{"description":"Default 50, max 200","in":"query","name":"limit","required":false,"schema":{"type":"integer"}},{"description":"","in":"query","name":"offset","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"count":{"description":"","type":"integer"},"items":{"items":{"$ref":"#/components/schemas/Webhook"},"type":"array"},"limit":{"description":"","type":"integer"},"offset":{"description":"","type":"integer"}},"type":"object"}}},"description":"Webhooks page"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"List subscriptions (secrets never included)","tags":["Webhooks"],"x-required-permission":"webhooks:view"},"post":{"description":"SIGNATURES — every delivery carries three headers: `X-Webhook-Timestamp: \u003cunix seconds\u003e`, `X-Webhook-Signature-V2: t=\u003cunix\u003e,v1=\u003chex HMAC-SHA256(secret, \"\u003cunix\u003e.\" + raw body)\u003e` (verify THIS one: recompute over t + '.' + body and reject when |now - t| exceeds your tolerance, 5 minutes recommended), and the DEPRECATED body-only `X-Webhook-Signature: sha256=\u003chex HMAC-SHA256(secret, raw body)\u003e`, kept for one version and then removed. ENVELOPE: {version (1), event_id (UUID; dedupe on it — delivery is at-least-once), event, company_uuid, timestamp (RFC3339), data}; the test ping (event \"ping\") carries the same envelope. DELIVERY: events are written to an outbox inside the transaction that produced them and drained at-least-once; retries: exponential backoff with jitter up to ~72h on 5xx/timeouts/429 (Retry-After honored); other 4xx fail immediately; 5 consecutive failures in 10min auto-suspends the endpoint (ops alerted; re-enable in the admin UI). URL must be HTTPS and publicly routable (SSRF-guarded). Event types: loan.created, loan.updated, loan.paid_off, payment.received, payment.voided, application.submitted, application.decided, application.withdrawn, rewards.earned, rewards.redeemed, rewards.reversed, held_check.created, held_check.presentment_requested, held_check.returned, held_check.deposited, held_check.voided, cash_disbursement.voided, lease.created, lease.item_returned, lease.item_swapped, lease.item_owned, lease.repossessed, lease.terminated, lease.reinstated, repayment_plan.offered, repayment_plan.elected, repayment_plan.declined, repayment_plan.expired, repayment_plan.completed, repayment_plan.breached, hardship_campaign.applied, pawn.renewed, pawn.redeemed, pawn.forfeited, repossession.ordered, repossession.recovered, repossession.sold, repossession.redeemed, repossession.released, title.custody_changed. Register consumers: held_check.* carries {held_check_uuid, loan_uuid, loan_number, borrower_uuid, amount_cents, check_number, bank_name, hold_until (YYYY-MM-DD), status, presentment_mode, custody_source, custody_state, register_instrument_ref, store_uuid, occurred_at} and, on held_check.returned / deposited / voided, {return_code, return_reason, nsf_fee_txn_uuid (returned), payment_txn_uuid (deposited), reason}; payment.voided carries {loan_uuid, loan_number, borrower_uuid, transaction_uuid, transaction_type, bucket, amount_cents (as stored, negative), total_outstanding_cents, loan_status, payment_method_label, store_uuid, external_source, external_ref, receipt_number, payment_network, payment_reference, posted_by, voided_by_uuid, occurred_at}; cash_disbursement.voided carries {cash_disbursement_uuid, loan_uuid, loan_number, borrower_uuid, kind, rail, network, amount_cents, store_uuid, register_uuid, register_txn_ref, receipt_number, loan_transaction_uuid, status (voided), reason, voided_by_uuid, occurred_at} — book a LOAN_ADVANCE_RETURN paid-in against the payout it names; lease.* carries {lease_uuid, lease_number, loan_uuid, company_uuid, store_uuid, borrower_uuid, status, items[{lease_item_uuid, inventory_item_uuid, serial, brand, model, description, status}], swapped_item_uuid/replacement_item_uuid (item_swapped), occurred_at}. pawn.* carries {loan_uuid, loan_number, company_uuid, store_uuid, borrower_uuid, ticket_number, status, pledge_date, maturity_date, grace_end_date, renewal_count, principal_cents, charge_rate_bps_per_period, period_days, occurred_at, vehicle{vin, year, make, model, trim, color, mileage, plate, plate_state, title_state, title_number, vehicle_class, lien_status, title_custody}} plus vehicle.odometer / vehicle.inventory_item_uuid / vehicle.collateral_uuid; money fields are ALWAYS present (never omitted when 0). pawn.forfeited adds {forfeited_at, principal_forfeited_cents, charges_cents, lien_fee_cents, fees_cents, balance_at_forfeiture_cents} — the register takes the vehicle into inventory at that balance (a zero-basis vehicle is still emitted) and may answer the delivery with {inventory_item_uuid, store_uuid} or call POST /loans/{loan_uuid}/pawn/forfeiture-intake. pawn.renewed adds {renewal_number, new_maturity_date, new_grace_end_date, charge_paid_cents, charge_posted_cents, posted_by, receipt{transaction_uuids, amount_cents, method, idempotency_key, external_source, external_ref, store_uuid, when}} when the counter renewed it; pawn.redeemed adds {redeemed_cents, posted_by, receipt} when a register payoff redeemed it. repossession.* carries {repo_case_uuid, loan_uuid, loan_number, company_uuid, store_uuid, borrower_uuid, status, reason (released: reinstated | cancelled), repo_vendor, repo_ordered_at, repossessed_date, redemption_deadline_date, sale_scheduled_date, sale_date, repo_cost_cents, storage_cost_cents, sale_proceeds_cents, sale_costs_cents, net_proceeds_cents, applied_cents, surplus_refund_due_cents, deficiency_cents, non_recourse, non_recourse_settled, surplus_refund_queued, buyer, total_outstanding_cents, vehicle{…}, posted_by, occurred_at}. title.custody_changed carries {loan_uuid, loan_number, company_uuid, store_uuid, borrower_uuid, collateral_uuid, action (returned | held | released_to_borrower | lien_released), title_custody_before, title_custody, lien_status_before, lien_status, when, note, vehicle{…}, posted_by, occurred_at}.","requestBody":{"content":{"application/json":{"schema":{"properties":{"description":{"description":"","type":"string"},"events":{"description":"Allow-listed event types (see description)","items":{"type":"string"},"type":"array"},"is_active":{"description":"Default true","type":"boolean"},"url":{"description":"HTTPS endpoint","type":"string"}},"required":["url","events"],"type":"object"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookWithSecret"}}},"description":"Created — store the secret NOW, it is never returned again"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Bad URL (non-HTTPS / SSRF) or unknown event type"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Create a subscription — the HMAC secret is returned ONCE","tags":["Webhooks"],"x-required-permission":"webhooks:create"}},"/webhooks/{webhook_uuid}":{"delete":{"parameters":[{"description":"","in":"path","name":"webhook_uuid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"deleted":{"description":"","type":"boolean"}},"type":"object"}}},"description":"Deleted"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Delete a subscription","tags":["Webhooks"],"x-required-permission":"webhooks:delete"},"get":{"parameters":[{"description":"","in":"path","name":"webhook_uuid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Webhook"}}},"description":"Webhook"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Get a subscription (no secret)","tags":["Webhooks"],"x-required-permission":"webhooks:view"},"put":{"parameters":[{"description":"","in":"path","name":"webhook_uuid","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"properties":{"description":{"description":"","type":"string"},"events":{"items":{"type":"string"},"type":"array"},"is_active":{"description":"Omitted preserves current","type":"boolean"},"url":{"description":"","type":"string"}},"required":["url","events"],"type":"object"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Webhook"}}},"description":"Updated"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Validation failed"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Update url/events/description/is_active (secret preserved)","tags":["Webhooks"],"x-required-permission":"webhooks:edit"}},"/webhooks/{webhook_uuid}/deliveries":{"get":{"parameters":[{"description":"","in":"path","name":"webhook_uuid","required":true,"schema":{"type":"string"}},{"description":"Default 50, max 200","in":"query","name":"limit","required":false,"schema":{"type":"integer"}},{"description":"","in":"query","name":"offset","required":false,"schema":{"type":"integer"}}],"responses":{"200":{"content":{"application/json":{"schema":{"properties":{"count":{"description":"","type":"integer"},"items":{"items":{"$ref":"#/components/schemas/WebhookDelivery"},"type":"array"},"limit":{"description":"","type":"integer"},"offset":{"description":"","type":"integer"}},"type":"object"}}},"description":"Deliveries page"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Delivery history (newest first)","tags":["Webhooks"],"x-required-permission":"webhooks:view"}},"/webhooks/{webhook_uuid}/rotate-secret":{"post":{"parameters":[{"description":"","in":"path","name":"webhook_uuid","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookWithSecret"}}},"description":"New secret — shown once"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Rotate the HMAC secret (old one dies immediately)","tags":["Webhooks"],"x-required-permission":"webhooks:edit"}},"/webhooks/{webhook_uuid}/test":{"post":{"parameters":[{"description":"","in":"path","name":"webhook_uuid","required":true,"schema":{"type":"string"}}],"responses":{"202":{"content":{"application/json":{"schema":{"properties":{"message":{"description":"","type":"string"},"status":{"description":"","type":"string"}},"type":"object"}}},"description":"Queued — outcome appears in the delivery history"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"}},"security":[{"apiKey":[],"companyHeader":[],"userHeader":[]}],"summary":"Queue a signed test ping","tags":["Webhooks"],"x-required-permission":"webhooks:edit"}}},"security":[{"apiKey":[],"companyHeader":[]}],"servers":[{"description":"Partner / staff API","url":"https://vectralms.com/api/v1"}],"tags":[{"description":"Key scope and this spec","name":"Discovery"},{"description":"Staff login and staff password reset","name":"Auth"},{"description":"Legacy borrower login/2FA/password-reset for custom frontends (prefer the Customer Portal API for new builds)","name":"Borrower Auth"},{"description":"Borrower records, addresses, phones. SSN is write-only — never returned","name":"Borrowers"},{"description":"Loan products and their JSON configuration","name":"Loan Types"},{"description":"Loan reads, origination, draws, disbursement","name":"Loans"},{"description":"Payments, voids, refunds, adjustments, interest/late-fee posting. Idempotent; maker-checker approval policies can park writes as 202 pending_approval","name":"Money"},{"description":"Cutting and generating statements","name":"Statements"},{"description":"Non-persisting what-if projections","name":"Simulation"},{"description":"Application submit, decisioning, conversion to loan, notes, history","name":"Underwriting"},{"description":"Company deny-list push and read (global tier read-only)","name":"Deny List"},{"description":"Employee lending block list kept current by an HR system, plus the audited membership check","name":"Employee Block"},{"description":"Document upload/download on applications and loans","name":"Documents"},{"description":"Lenders, pools, allocation strategies, loan assignment","name":"Lending"},{"description":"Award points from external systems and read accounts/ledgers","name":"Rewards"},{"description":"Self-service event subscriptions with HMAC-signed deliveries","name":"Webhooks"},{"description":"Portfolio and lending rollups","name":"Reports"},{"description":"Company stores / branches","name":"Stores"},{"description":"Staff user management (scoped to the key's and actor's companies)","name":"Users"}]}