Product Schema
Product Schema
Section titled âProduct SchemaâThe product schema represents a lenderâs mortgage product with detailed configuration including rates, fees, LVR tiers, and product features.
Overview
Section titled âOverviewâ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
Schema Structure
Section titled âSchema Structureâ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;}Core Fields
Section titled âCore Fieldsâ| Field | Type | Description |
|---|---|---|
_id | string | Unique MongoDB ObjectId |
lender | string | Lender identifier (e.g., 'cba', 'westpac') |
productType | enum | 'variable_package', 'variable_basic', 'fixed', 'line_of_credit', 'construction' |
productName | string | Display name of the product |
setupFee | number | One-time setup/application fee (AUD) |
ongoingFee | number | Annual ongoing/package fee (AUD) |
loanAmountThresholds | number[] | Loan amount breakpoints for rate tiers |
lvrThresholds | number[] | LVR breakpoints for rate adjustments |
rateTableFields | object | Configuration for rate table calculations |
productFeatures | object | Product features and eligibility criteria |
isForServicing | boolean | Whether product is used in servicing calculations |
Product Types
Section titled âProduct TypesâVariable Package
Section titled âVariable Packageâ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}Variable Basic
Section titled âVariable Basicâ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
Line of Credit
Section titled âLine of CreditâRevolving credit facility.
Features:
- Interest-only repayments
- Flexibility to redraw
- Higher rates
- Annual reviews
Construction
Section titled âConstructionâLoan for building a new home.
Features:
- Progressive drawdowns
- Interest-only during construction
- Converts to standard loan after completion
Rate Table Fields
Section titled âRate Table Fieldsâ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 }}Product Features
Section titled âProduct Featuresâ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;}Example Response
Section titled âExample Responseâ{ "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 }}Using Products
Section titled âUsing ProductsâCalculate Effective Rate
Section titled âCalculate Effective Rateâ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;}Check Product Eligibility
Section titled âCheck Product Eligibilityâ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;}Compare Products
Section titled âCompare Productsâ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);}Filtering Products
Section titled âFiltering ProductsâWhen calling GET /api/v1/products, products are filtered by:
- Lender access: Only products from lenders you have permission for
- Product type: Hardcoded to
variable_basicandvariable_package(servicing products) - Servicing flag: Only products where
isForServicing: true
Product Updates
Section titled âProduct Updatesâ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.
Common Use Cases
Section titled âCommon Use CasesâFinding Best Rate
Section titled âFinding Best Rateâ# Get all products for multiple lenderscurl -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 rateChecking Package Savings
Section titled âChecking Package Savingsâ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 feeif (annualSaving > packageProduct.ongoingFee) { console.log('Package saves money!');} else { console.log('Basic product is cheaper overall');}Next Steps
Section titled âNext Stepsâ- Get products endpoint - Fetch product data
- Compute servicing endpoint - See how products affect calculations
- Servicing result schema - Understand rate breakdowns
Last updated: 2025-11-03