Skip to content

Product Schema

The product schema represents a lender’s mortgage product with detailed configuration including rates, fees, LVR tiers, and product features.

Products are used in servicing calculations to determine:

  • Interest rates based on loan characteristics
  • Applicable fees (setup and ongoing)
  • Product eligibility criteria
  • Rate discounts and premiums
interface LenderProduct {
_id: string;
lender: LenderName;
productType: ProductType;
productName: string;
setupFee: number;
ongoingFee: number;
loanAmountThresholds: number[];
lvrThresholds: number[];
rateTableFields: RateTableConfig;
productFeatures: ProductFeatures;
isForServicing: boolean;
lastUpdated: string;
}
FieldTypeDescription
_idstringUnique MongoDB ObjectId
lenderstringLender identifier (e.g., 'cba', 'westpac')
productTypeenum'variable_package', 'variable_basic', 'fixed', 'line_of_credit', 'construction'
productNamestringDisplay name of the product
setupFeenumberOne-time setup/application fee (AUD)
ongoingFeenumberAnnual ongoing/package fee (AUD)
loanAmountThresholdsnumber[]Loan amount breakpoints for rate tiers
lvrThresholdsnumber[]LVR breakpoints for rate adjustments
rateTableFieldsobjectConfiguration for rate table calculations
productFeaturesobjectProduct features and eligibility criteria
isForServicingbooleanWhether product is used in servicing calculations

Premium product with offset accounts, redraw, and rate discounts.

Features:

  • Package/annual fee (typically $350-$400)
  • Better interest rates
  • Offset accounts
  • Unlimited redraws
  • Free extra repayments

Example:

{
"productType": "variable_package",
"productName": "Home Loan Package",
"setupFee": 600,
"ongoingFee": 395
}

No-frills product with higher rates but no ongoing fees.

Features:

  • No annual fee
  • Higher interest rates
  • Basic redraw (may have limits)
  • Limited offset features

Example:

{
"productType": "variable_basic",
"productName": "Basic Variable Home Loan",
"setupFee": 600,
"ongoingFee": 0
}

Fixed rate for a set period (1-5 years).

Features:

  • Rate locked for fixed period
  • May have break fees
  • Limited extra repayments
  • Converts to variable after fixed period

Revolving credit facility.

Features:

  • Interest-only repayments
  • Flexibility to redraw
  • Higher rates
  • Annual reviews

Loan for building a new home.

Features:

  • Progressive drawdowns
  • Interest-only during construction
  • Converts to standard loan after completion

The rateTableFields object defines how rates are calculated:

interface RateTableConfig {
baseRate: number;
lvrDiscounts: { [lvr: string]: number };
loanAmountDiscounts: { [amount: string]: number };
ownerOccupiedDiscount: number;
investmentPremium: number;
principalAndInterestDiscount: number;
interestOnlyPremium: number;
}

Example:

{
"rateTableFields": {
"baseRate": 6.54,
"lvrDiscounts": {
"60": -0.20,
"70": -0.10,
"80": 0.00,
"90": 0.50
},
"loanAmountDiscounts": {
"150000": -0.10,
"250000": -0.20,
"500000": -0.30
},
"ownerOccupiedDiscount": 0.00,
"investmentPremium": 0.25,
"principalAndInterestDiscount": 0.00,
"interestOnlyPremium": 0.30
}
}

The productFeatures object defines eligibility and restrictions:

interface ProductFeatures {
maxLVR: number;
minLoanAmount: number;
maxLoanAmount: number;
allowedPropertyTypes: PropertyType[];
allowedSecurityTypes: SecurityType[];
offsetAccount: boolean;
redrawFacility: boolean;
extraRepayments: boolean;
}
{
"data": [
{
"_id": "507f1f77bcf86cd799439011",
"lender": "cba",
"productType": "variable_package",
"productName": "Wealth Package",
"setupFee": 600,
"ongoingFee": 395,
"loanAmountThresholds": [150000, 250000, 500000, 1000000],
"lvrThresholds": [60, 70, 80, 90, 95],
"rateTableFields": {
"baseRate": 6.54,
"lvrDiscounts": {
"60": -0.20,
"70": -0.10,
"80": 0.00,
"90": 0.50
},
"loanAmountDiscounts": {
"250000": -0.10,
"500000": -0.20
},
"ownerOccupiedDiscount": 0.00,
"investmentPremium": 0.25,
"principalAndInterestDiscount": 0.00,
"interestOnlyPremium": 0.30
},
"productFeatures": {
"maxLVR": 95,
"minLoanAmount": 50000,
"maxLoanAmount": 5000000,
"offsetAccount": true,
"redrawFacility": true,
"extraRepayments": true
},
"isForServicing": true,
"lastUpdated": "2025-10-15T00:00:00.000Z"
}
],
"meta": {
"timestamp": "2025-11-03T10:30:00.000Z",
"count": 1
}
}
function calculateRate(product: LenderProduct, loan: HomeLoan): number {
let rate = product.rateTableFields.baseRate;
// Apply LVR discount/premium
const lvrTier = findApplicableTier(loan.lvr, product.lvrThresholds);
rate += product.rateTableFields.lvrDiscounts[lvrTier] || 0;
// Apply loan amount discount
const amountTier = findApplicableTier(loan.loan_amount, product.loanAmountThresholds);
rate += product.rateTableFields.loanAmountDiscounts[amountTier] || 0;
// Apply loan type adjustment
if (loan.loan_type === 'investment') {
rate += product.rateTableFields.investmentPremium;
} else {
rate += product.rateTableFields.ownerOccupiedDiscount;
}
// Apply repayment type adjustment
if (loan.interest_only_period > 0) {
rate += product.rateTableFields.interestOnlyPremium;
} else {
rate += product.rateTableFields.principalAndInterestDiscount;
}
return rate;
}
function isEligible(product: LenderProduct, loan: HomeLoan, property: Security): boolean {
// Check LVR
if (loan.lvr > product.productFeatures.maxLVR) {
return false;
}
// Check loan amount
if (loan.loan_amount < product.productFeatures.minLoanAmount ||
loan.loan_amount > product.productFeatures.maxLoanAmount) {
return false;
}
// Check property type
if (!product.productFeatures.allowedPropertyTypes.includes(property.property_type)) {
return false;
}
return true;
}
function compareProducts(products: LenderProduct[], loan: HomeLoan) {
return products.map(product => ({
lender: product.lender,
productName: product.productName,
rate: calculateRate(product, loan),
setupFee: product.setupFee,
ongoingFee: product.ongoingFee,
totalFirstYearCost: product.setupFee + product.ongoingFee,
hasOffset: product.productFeatures.offsetAccount
})).sort((a, b) => a.rate - b.rate);
}

When calling GET /api/v1/products, products are filtered by:

  1. Lender access: Only products from lenders you have permission for
  2. Product type: Hardcoded to variable_basic and variable_package (servicing products)
  3. Servicing flag: Only products where isForServicing: true

Products are updated regularly to reflect:

  • Rate changes (typically monthly)
  • Policy changes
  • New product launches
  • Product discontinuations

Check the lastUpdated field to see when product data was last refreshed.

Terminal window
# Get all products for multiple lenders
curl -X GET 'https://api.quickli.com/api/v1/products?lenders=cba,westpac,anz' \
-H "Authorization: Bearer {credentials}"
# Compare rates for your scenario
# Calculate effective rate for each product
# Sort by lowest rate
const basicProduct = products.find(p => p.productType === 'variable_basic');
const packageProduct = products.find(p => p.productType === 'variable_package');
const basicRate = calculateRate(basicProduct, loan);
const packageRate = calculateRate(packageProduct, loan);
const rateSaving = basicRate - packageRate;
const monthlySaving = (loan.loan_amount * rateSaving / 100) / 12;
const annualSaving = monthlySaving * 12;
// Compare annual saving vs package fee
if (annualSaving > packageProduct.ongoingFee) {
console.log('Package saves money!');
} else {
console.log('Basic product is cheaper overall');
}

Last updated: 2025-11-03