Mortgage Decisions

Score a mortgage application against ability-to-repay and qualified mortgage rules. Combines cash-flow underwriting with DTI limits, LTV analysis, and independent property valuation. Returns a TILA-RESPA disclosure, or a decline with ECOA reason codes.

Create a decision

POST/v1/decisions/mortgage

Request body

FieldTypeDescription
consumerRequiredobjectIdentifies the consumer. See Consumer object below. Either consumer_id (for returning customers) OR full PII fields (for new consumers).
loanRequiredobjectThe mortgage terms requested. See Loan object below.
propertyRequiredobjectThe subject property being financed. Used for LTV and valuation. See Property object below.

Consumer object

FieldTypeDescription
consumer_iduuidUUID of an existing consumer in your tenant. Skip the rest of the consumer fields if provided.
first_namestringRequired for new consumers.
last_namestringRequired for new consumers.
emailstringRequired for new consumers. Used for verification flows.
phonestringE.164 format. Used for SMS verification during hard KYC.
dobstringISO 8601 date (YYYY-MM-DD). Required for new consumers.
ssn_last4stringLast 4 digits of SSN. Required for hard KYC. Encrypted at rest.
addressobjectCurrent mailing address. Distinct from the subject property.

Loan object

FieldTypeDescription
amountRequireddecimalRequested loan amount. Use a string to avoid floating-point issues.
currencyRequiredstringISO 4217 currency code. Currently only USD is supported.
term_monthsRequiredintegerRequested term. Mortgages support 180 to 360 months (15 to 30 years).
loan_purposeRequiredstringpurchase, refinance, or cash_out_refinance.
occupancystringprimary_residence, second_home, or investment. Affects risk pricing. Defaults to primary_residence.

Property object

FieldTypeDescription
addressRequiredobjectThe subject property address. line1, city, state, zip required. Used for the ATTOM valuation lookup.
property_typeRequiredstringsingle_family, condo, townhouse, or multi_family.
purchase_priceRequireddecimalAgreed purchase price (or appraised value for refinances). Used for LTV.
down_paymentdecimalDown payment amount. Combined with purchase_price to compute LTV.

Example request

Request body

{
  "consumer": {
    "first_name": "Robert",
    "last_name": "Nakamura",
    "email": "robert@example.com",
    "phone": "+15553219876",
    "dob": "1979-07-14",
    "ssn_last4": "3310",
    "address": {
      "line1": "88 Beacon St",
      "city": "Boston",
      "state": "MA",
      "zip": "02108"
    }
  },
  "loan": {
    "amount": 420000.00,
    "currency": "USD",
    "term_months": 360,
    "loan_purpose": "purchase",
    "occupancy": "primary_residence"
  },
  "property": {
    "address": {
      "line1": "12 Maple Ave",
      "city": "Newton",
      "state": "MA",
      "zip": "02458"
    },
    "property_type": "single_family",
    "purchase_price": 525000.00,
    "down_payment": 105000.00
  }
}

Property valuation via ATTOM

AltFiScore looks up an independent property valuation through ATTOM to inform the LTV gate. This valuation is advisory — it bounds the approvable amount but never persists to the consumer's feature set and never affects the underlying credit score. It is a separate use case from borrower scoring.

Response

Mortgage decisions always run hard KYC given the loan size and regulatory requirements. The initial response is typically needs_consumer_action while the consumer completes identity verification and links financial accounts.

Approved

Status complete with outcome approved. The response includes ltv_pct, dti_pct, a qm_status flag, and the ATTOM property_valuation.

200 OK · approved

{
  "decision_id": "e55f2c37-8b4a-4e1c-a2d9-3f5e7b2c8d44",
  "application_id": "e55f2c37-8b4a-4e1c-a2d9-3f5e7b2c8d44",
  "status": "complete",
  "kyc_tier": "hard",
  "kyc_status": "matched",
  "outcome": "approved",
  "score": 774,
  "risk_grade": "A",
  "approved_amount": "420000.00",
  "currency": "USD",
  "apr": "6.85",
  "term_months": 360,
  "monthly_payment": "2751.94",
  "ltv_pct": "80.0",
  "dti_pct": "34.2",
  "qm_status": "qualified_mortgage",
  "property_valuation": {
    "source": "attom",
    "estimated_value": "528000.00",
    "confidence": "high"
  },
  "tila_disclosure": {
    "amount_financed": "420000.00",
    "finance_charge": "570698.40",
    "total_of_payments": "990698.40",
    "apr": "6.85",
    "payment_frequency": "monthly"
  },
  "decline_reasons": [],
  "expires_at": "2026-05-22T20:30:20.566665Z"
}

Pending (hard KYC)

Status needs_consumer_action. The consumer must visit consumer_complete_url to complete verification and link accounts. Poll polling_url or wait for a webhook (coming Q3 2026).

200 OK · pending

{
  "decision_id": "e55f2c37-...",
  "application_id": "e55f2c37-...",
  "status": "needs_consumer_action",
  "kyc_tier": "hard",
  "kyc_status": "pending",
  "consumer_complete_url": "https://verify.altfiscore.com/c/e55f2c37...",
  "polling_url": "/v1/decisions/mortgage/e55f2c37-8b4a-4e1c-a2d9-3f5e7b2c8d44",
  "estimated_completion_seconds": 120,
  "expires_at": "2026-05-22T21:00:00Z"
}

Declined

Status complete with outcome declined. The decline_reasons array contains ECOA-compliant codes. DTI and LTV breaches are the most common mortgage declines. See compliance for adverse action handling.

200 OK · declined

{
  "decision_id": "f66a3d48-...",
  "application_id": "f66a3d48-...",
  "status": "complete",
  "outcome": "declined",
  "score": 640,
  "risk_grade": "C",
  "dti_pct": "48.6",
  "decline_reasons": [
    { "code": "AA-08", "reason": "Debt-to-income ratio exceeds the qualified mortgage limit of 43%." },
    { "code": "AA-14", "reason": "Loan-to-value ratio exceeds program limits for this credit tier." }
  ]
}

A decline is still a 200

The HTTP status indicates whether the API call succeeded, not whether the consumer was approved. A successfully-evaluated decline is HTTP 200 with outcome: "declined". See the errors page for genuine error cases.

Response fields

FieldTypeDescription
decision_iduuidUnique identifier for this decision. Store for reconciliation and later lookup.
application_iduuidIdentifier for the underlying application. Typically equals decision_id for first-time decisions.
statusstringcomplete or needs_consumer_action.
kyc_tierstringMortgage decisions run hard KYC.
kyc_statusstringmatched, pending, or skipped.
outcomestringapproved, declined, or referred. null when status is needs_consumer_action.
scoreintegerAltFiScore from 300 to 1000.
risk_gradestringLetter grade A+ through D derived from the score.
approved_amountdecimalString. The approved loan amount.
aprdecimalString. Annual percentage rate as a percentage.
term_monthsintegerApproved term in months.
monthly_paymentdecimalString. Calculated monthly principal and interest.
ltv_pctdecimalString. Loan-to-value ratio as a percentage.
dti_pctdecimalString. Debt-to-income ratio as a percentage.
qm_statusstringqualified_mortgage or non_qm. Indicates ATR/QM classification.
property_valuationobjectATTOM valuation: { source, estimated_value, confidence }. Advisory only.
tila_disclosureobjectTILA-RESPA disclosure block ready to display to the consumer.
decline_reasonsarrayArray of { code, reason } objects. Populated when outcome is declined or referred.
expires_attimestampISO 8601 timestamp. The offer or pending state expires at this time.

Poll a decision

GET/v1/decisions/mortgage/{decision_id}

Returns the current state of a previously-created decision. Idempotent and safe to call repeatedly. Recommended polling interval: 3–5 seconds given the longer verification window.

Poll a pending decision

curl 'https://api.altfiscore.com/v1/decisions/mortgage/e55f2c37-8b4a-4e1c-a2d9-3f5e7b2c8d44' \
  -H 'Authorization: Bearer altfi_test_YOUR_KEY'

Use idempotency

Both POST and GET endpoints are safe to retry. The POST endpoint with the same Idempotency-Key returns the cached decision. The GET endpoint is naturally idempotent.

Errors

Returns standard HTTP error codes. The full catalog is in the errors reference. Most common errors for this endpoint:

  • 400 invalid_request — required field missing (e.g., property.address)
  • 401 invalid_api_key — bad or missing API key
  • 409 idempotency_conflict — same idempotency key with a different body
  • 422 unprocessable_entity — request is well-formed but fails business rules (e.g., term outside 180–360 months)

Next steps