Singapore mortgage rates, bank packages and calculators (23 read-only tools)
Maintain this server? Claim it
Call this server's tools from your browser. Nothing to install.
Requests go through mcptry because browsers can't call most MCP servers directly. Header values are sent to the server for that request only and are never stored or logged.
Tool list from our last check. Connect live to refresh it.
Analyse decoupling: the cost versus benefit of transferring one co-owner's share so a borrower is freed up for another purchase (including stamp duty).
The co-owner buying out the share and refinancing.
The co-owner transferring out their share.
Current market valuation of the property, in SGD.
Outstanding loan on the property, in SGD.
Price of a subsequent property purchase the freed-up buyer plans, in SGD.
Analyse decoupling: the cost versus benefit of transferring one co-owner's share so a borrower is freed up for another purchase (including stamp duty).
| Argument | Type | Description |
|---|---|---|
| buyer* | object | The co-owner buying out the share and refinancing. |
| seller* | object | The co-owner transferring out their share. |
| valuation* | number | Current market valuation of the property, in SGD. |
| outstandingLoan | number | Outstanding loan on the property, in SGD. |
| newPropertyPrice | number | Price of a subsequent property purchase the freed-up buyer plans, in SGD. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"valuation",
"seller",
"buyer"
],
"properties": {
"buyer": {
"type": "object",
"properties": {
"age": {
"type": "integer",
"default": 35,
"maximum": 80,
"minimum": 21,
"description": "Buyer age."
},
"cpfInOA": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Buyer CPF Ordinary Account balance usable, in SGD."
},
"legalFees": {
"type": "number",
"default": 3000,
"minimum": 0,
"description": "Buyer legal fees, in SGD."
},
"citizenship": {
"enum": [
"singaporean",
"pr",
"foreigner"
],
"type": "string",
"default": "singaporean",
"description": "Buyer citizenship."
},
"financingPercent": {
"enum": [
75,
55,
45,
25
],
"type": "number",
"description": "MAS LTV tier for the loan on the seller's share (75, 55, 45 or 25). Capped by what the buyer's age and tenure allow; omit for the highest allowed tier."
},
"loanInterestRate": {
"type": "number",
"default": 2.6,
"maximum": 15,
"minimum": 0.1,
"description": "Assumed annual loan rate, as a percentage (e.g. 2.6 = 2.6%)."
},
"remainingLeaseYears": {
"type": "number",
"maximum": 99,
"minimum": 0,
"description": "Remaining lease in years; omit for freehold."
}
},
"description": "The co-owner buying out the share and refinancing."
},
"seller": {
"type": "object",
"properties": {
"shares": {
"type": "number",
"default": 50,
"maximum": 100,
"minimum": 0,
"description": "Seller (transferor) ownership share being sold, as a percentage (0–100). The buyer holds the rest."
},
"legalFees": {
"type": "number",
"default": 3000,
"minimum": 0,
"description": "Seller legal fees, in SGD."
},
"citizenship": {
"enum": [
"singaporean",
"pr",
"foreigner"
],
"type": "string",
"default": "singaporean",
"description": "Seller citizenship."
},
"sellerStampDuty": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Seller's Stamp Duty payable, in SGD."
},
"cpfUsageInterest": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "CPF principal + accrued interest the seller must refund, in SGD."
}
},
"description": "The co-owner transferring out their share."
},
"valuation": {
"type": "number",
"minimum": 0,
"description": "Current market valuation of the property, in SGD."
},
"outstandingLoan": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Outstanding loan on the property, in SGD."
},
"newPropertyPrice": {
"type": "number",
"minimum": 0,
"description": "Price of a subsequent property purchase the freed-up buyer plans, in SGD."
}
}
}Analyse a 'sell one, buy two' restructuring: the costs, loan sizing and stamp-duty impact of selling one property to purchase two.
| Argument | Type | Description |
|---|---|---|
| buy1 | object | The single property in the "buy one" scenario. |
| buyer1* | object | Income/CPF profile of buyer 1. |
| buyer2* | object | Income/CPF profile of buyer 2. |
| valuation* | number | Current market valuation of the property being sold, in SGD. |
| buyer1Shares | number | Buyer 1 ownership share of the sold property, as a percentage (0–100). |
| buyer2Shares | number | Buyer 2 ownership share of the sold property, as a percentage (0–100). |
| buy2Property1 | object | First property in the "buy two" scenario. |
| buy2Property2 | object | Second property in the "buy two" scenario. |
Estimate the maximum loan and property price a borrower can afford from income, existing debt commitments and tenure, applying Singapore TDSR/MSR limits. Use this when you know the income and want the budget; to go the other way (a target price → the income needed), use `calculate_required_income`.
| Argument | Type | Description |
|---|---|---|
| age | integer | Age of the (oldest) borrower. |
| cpfOA | number | CPF Ordinary Account balance usable, in SGD. |
| cashSavings | number | Cash available for downpayment, in SGD. |
| citizenship | string | Buyer citizenship: singaporean, pr or foreigner. Drives ABSD rate. |
| monthlyDebt | number | Total existing monthly debt commitments (other loans, cards), in SGD. |
| interestRate | number | Assumed annual interest rate, as a percentage (e.g. 4.0 = 4%, not 0.04). |
| propertyType | string | Property type: hdb, ec (Executive Condominium) or private. Sets MSR/LTV rules. |
| stressTestRate | number | Override the TDSR/MSR stress-test rate (percentage); defaults to the MAS floor. |
Generate a step-by-step dated timeline for an HDB resale purchase, from securing the Option to Purchase through to completion and key collection. Each step shows what happens, the date, and any payment due.
| Argument | Type | Description |
|---|---|---|
| cpfUsage | number | CPF OA usage toward the purchase, in SGD. |
| legalFee | number | Legal/conveyancing fee, in SGD. |
| buyerType | string | Buyer profile for ABSD calculation. |
| optionFee | number | Option fee, in SGD (default $1,000, HDB cap). |
| otherFees | number | Other costs (valuation, repairs, admin), in SGD. Reported alongside legal fees and stamp duty as part of the total outlay. |
| loanAmount | number | Bank loan amount, in SGD. |
| optionDate* | string | Option to Purchase date (ISO 8601, e.g. 2026-08-01). |
| exerciseFee | number | Exercise fee, in SGD (default $4,000). |
Compute the monthly repayment, total interest and total cost for a hypothetical loan from an interest rate you supply (standard amortisation). Use this for "what-if" rates; for the real rates of a specific package the borrower has chosen, use `calculate_package_schedule` instead.
| Argument | Type | Description |
|---|---|---|
| loanAmount* | number | Loan principal, in SGD. |
| tenureYears* | integer | Loan tenure in years. |
| interestRate* | number | Annual interest rate, as a percentage (e.g. 3.5 = 3.5%, not 0.035). |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"loanAmount",
"interestRate",
"tenureYears"
],
"properties": {
"loanAmount": {
"type": "number",
"maximum": 200000000,
"minimum": 50000,
"description": "Loan principal, in SGD."
},
"tenureYears": {
"type": "integer",
"maximum": 35,
"minimum": 1,
"description": "Loan tenure in years."
},
"interestRate": {
"type": "number",
"maximum": 15,
"minimum": 0.1,
"description": "Annual interest rate, as a percentage (e.g. 3.5 = 3.5%, not 0.035)."
}
}
}Compute the stamp duty payable on a mortgage/loan facility.
| Argument | Type | Description |
|---|---|---|
| loanAmount* | number | Mortgage/loan facility amount, in SGD. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"loanAmount"
],
"properties": {
"loanAmount": {
"type": "number",
"minimum": 0,
"description": "Mortgage/loan facility amount, in SGD."
}
}
}Compute the amortisation schedule for a SPECIFIC mortgage package using that package's real effective rates. Use this once the borrower has picked a package from `recommend_packages`; for a hypothetical "what-if" rate the borrower does not yet hold a package for, use `calculate_mortgage_repayment` instead. Pass a `packageId` from `recommend_packages`, plus `loanAmount` and `tenureYears`. Always returns the full canonical schedule: loan facts (`context`), the per-tier breakdown (`periods`), rate structure, totals, and the per-year schedule (`annual`). The month-by-month schedule (`monthly`) is empty unless you pass `includeMonthly: true`. To also generate downloadable files, pass `formats` (`["pdf"]`, `["xlsx"]`, or both) — the public URLs come back under `documents` (null otherwise). Optional `firstPaymentDate` (YYYY-MM-DD, defaults to next month) dates the schedule.
| Argument | Type | Description |
|---|---|---|
| formats | array | Document formats to generate and upload (pdf, xlsx); the resulting download URLs are returned under `documents`. Omit/empty for data only. |
| packageId* | string | Package id, as returned by `recommend_packages`. |
| loanAmount* | integer | Loan principal in SGD (whole dollars). |
| tenureYears* | integer | Loan tenure in years (1–35). |
| includeMonthly | boolean | Include the full month-by-month schedule (`monthly[]`) in the response. Omitted/false returns an empty `monthly[]` to keep the payload small. |
Generate a step-by-step dated timeline for a private (non-HDB) resale purchase, with the 1% option fee, 4% exercise payment, stamp duties and completion payment at the right steps. Exercise date defaults to 14 calendar days inclusive of the option date if not entered.
| Argument | Type | Description |
|---|---|---|
| cpfUsage | number | CPF OA usage toward the purchase, in SGD. |
| legalFee | number | Legal fee, in SGD (default $2,500 for private). |
| buyerType | string | Buyer profile for ABSD calculation. |
| otherFees | number | Other costs (valuation, repairs, admin), in SGD. Reported alongside legal fees and stamp duty as part of the total outlay. |
| loanAmount | number | Bank loan amount, in SGD. |
| optionDate* | string | Option to Purchase date (ISO 8601). |
| tenureYears | integer | Loan tenure in years. Supply with interestRate to get the monthly instalment. |
| exerciseDate | string |
Model the monthly instalment profile of a Singapore Building Under Construction (BUC) property loan across progressive payment stages. Handles fixed and floating rate packages, per-stage cash/CPF/loan funding split, drawdown timing, rate stepping by year, and optional prepayments. Set includeSchedule to true for the full month-by-month amortisation schedule (summary is always included).
| Argument | Type | Description |
|---|---|---|
| stages | array | Custom stage schedule. Defaults to the standard 10-stage BUC schedule. Percentages must sum to 100. |
| otpDate* | string | Option to Purchase date (ISO 8601). |
| topDate* | string | Expected Temporary Occupation Permit date (ISO 8601). |
| rateMode | string | Rate type for the loan package. |
| loanAmount | number | Loan amount in SGD. Derives loanQuantum if provided. One of loanQuantum or loanAmount is required. |
| loanQuantum | number | Loan as a fraction of price (e.g. 0.75 = 75%). Derives loanAmount if provided. |
| prepayments | array | Optional list of prepayments. |
Calculate the full upfront cost of purchasing a Singapore residential property (HDB or private resale), with each cost line attributed to its funding source (cash, CPF or bank loan). Returns option fee, exercise fee, stamp duties (BSD and ABSD), downpayment breakdown, and a sufficiency check against available funds.
| Argument | Type | Description |
|---|---|---|
| ltv | number | Loan-to-value ratio as a decimal (e.g. 0.75 = 75%). If omitted, derived from loanAmount / purchasePrice. |
| cpfFirst | boolean | When true, fund CPF-eligible costs from a single CPF pool capped at availableCpfOA (CPF first, remainder cash), overriding the per-item cash/cpf funding sources. ABSD stays cash. |
| buyerType | string | Buyer profile driving ABSD rate. |
| loanAmount | number | Bank loan amount, in SGD. If omitted, derived from ltv * purchasePrice. |
| propertyType* | string | HDB or private (non-HDB) purchase. |
| availableCash | number | Available cash for the purchase, in SGD. |
| purchasePrice* | number | Purchase price of the property, in SGD. |
Compare a borrower's existing mortgage against a new rate and report monthly savings, total savings and break-even months. Set `mode` to 'original' (supply the original loan amount, tenure and start date) or 'remaining' (supply the current outstanding balance and remaining tenure).
| Argument | Type | Description |
|---|---|---|
| mode* | string | Use 'original' when supplying the loan's original terms and start date, 'remaining' when supplying the current outstanding balance and tenure left. |
| newRate* | number | Proposed new annual rate, as a percentage (e.g. 3.2 = 3.2%). |
| currentRate* | number | Current annual rate, as a percentage (e.g. 4.0 = 4%). |
| loanStartYear | integer | Year the loan started. |
| loanStartMonth | integer | Month the loan started (1–12). |
| newTenureYears | integer | New tenure in years; defaults to the remaining tenure if omitted. |
| remainingAmount | number | Current outstanding loan balance, in SGD. |
Compute the stamp duty payable on a property lease from the rent and lease term.
| Argument | Type | Description |
|---|---|---|
| hasPremium | boolean | True if the lease carries a lump-sum premium. |
| monthlyRent* | number | Monthly rent under the lease, in SGD. |
| premiumAmount | number | Lump-sum premium amount, in SGD (only if hasPremium). |
| leasePeriodMonths | integer | Lease term in months. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"monthlyRent"
],
"properties": {
"hasPremium": {
"type": "boolean",
"default": false,
"description": "True if the lease carries a lump-sum premium."
},
"monthlyRent": {
"type": "number",
"minimum": 0,
"description": "Monthly rent under the lease, in SGD."
},
"premiumAmount": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Lump-sum premium amount, in SGD (only if hasPremium)."
},
"leasePeriodMonths": {
"type": "integer",
"default": 12,
"maximum": 1200,
"minimum": 1,
"description": "Lease term in months."
}
}
}Work out the gross monthly income needed to support a target property price under Singapore TDSR/MSR limits. Use this when you know the target property price; to go the other way (income → maximum budget), use `calculate_affordability`.
| Argument | Type | Description |
|---|---|---|
| age | integer | Age of the (oldest) borrower. |
| cpfOA | number | CPF Ordinary Account balance usable, in SGD. |
| cashSavings | number | Cash available for downpayment, in SGD. |
| citizenship | string | Buyer citizenship: singaporean, pr or foreigner. Drives ABSD rate. |
| monthlyDebt | number | Total existing monthly debt commitments, in SGD. |
| interestRate | number | Assumed annual interest rate, as a percentage (e.g. 4.0 = 4%, not 0.04). |
| propertyType | string | Property type: hdb, ec (Executive Condominium) or private. |
| propertyPrice* | number | Target property purchase price, in SGD. |
Compute Buyer's Stamp Duty (BSD) and Additional Buyer's Stamp Duty (ABSD) for a Singapore residential purchase, given price/value, buyer type (singaporean | pr | foreigner | entity | trust) and the number of residential properties already owned.
| Argument | Type | Description |
|---|---|---|
| price* | number | Purchase price or market value (whichever higher), in SGD. |
| buyerType | string | Buyer profile driving the ABSD rate: singaporean, pr, foreigner, entity or trust. |
| propertyType | string | Property type: hdb, ec, private or mixed. |
| purchaseType | string | Ownership structure: single, joint_spouse, joint_non_spouse, entity or trust. |
| existingPropertiesOwned | integer | Count of residential properties the buyer already owns (sets the ABSD tier). |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"price"
],
"properties": {
"price": {
"type": "number",
"minimum": 0,
"description": "Purchase price or market value (whichever higher), in SGD."
},
"buyerType": {
"enum": [
"singaporean",
"pr",
"foreigner",
"entity",
"trust"
],
"type": "string",
"default": "singaporean",
"description": "Buyer profile driving the ABSD rate: singaporean, pr, foreigner, entity or trust."
},
"propertyType": {
"enum": [
"hdb",
"ec",
"private",
"mixed"
],
"type": "string",
"default": "hdb",
"description": "Property type: hdb, ec, private or mixed."
},
"purchaseType": {
"enum": [
"single",
"joint_spouse",
"joint_non_spouse",
"entity",
"trust"
],
"type": "string",
"default": "single",
"description": "Ownership structure: single, joint_spouse, joint_non_spouse, entity or trust."
},
"existingPropertiesOwned": {
"type": "integer",
"default": 0,
"maximum": 10,
"minimum": 0,
"description": "Count of residential properties the buyer already owns (sets the ABSD tier)."
}
}
}Work out what a seller walks away with when selling a property: cash proceeds after redeeming the outstanding loan, refunding each owner’s CPF (principal plus accrued interest, 1 to 4 owners), and paying agent commission (with 9% GST), legal and miscellaneous fees. Flags a negative sale when the deductions exceed the selling price.
| Argument | Type | Description |
|---|---|---|
| owners* | array | CPF usage per owner (1 to 4 owners). Include every owner; use zeros for an owner who used no CPF. |
| legalFees | number | Conveyancing/legal fees, in SGD. |
| sellingPrice* | number | Sale price of the property, in SGD. |
| agentFeePercent | number | Agent commission as a percentage of the selling price, before GST (e.g. 1 = 1%, 1.5 = 1.5%, not 0.01). 9% GST is added on top. |
| miscellaneousFees | number | Other selling costs (repairs, valuation, admin), in SGD. |
| bridgingRatePercent | number | Annual bridging-loan rate for the interest-only instalment, as a percentage; defaults to 5. |
| outstandingLoanAmount | number |
Compute Seller's Stamp Duty (SSD) from the sale price/value and how long the property has been held.
| Argument | Type | Description |
|---|---|---|
| price* | number | Sale price or market value (whichever higher), in SGD. |
| saleDate* | string | Date the property is/was sold (ISO date, YYYY-MM-DD). |
| purchaseDate* | string | Date the property was acquired (ISO date, YYYY-MM-DD). |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"price",
"purchaseDate",
"saleDate"
],
"properties": {
"price": {
"type": "number",
"minimum": 0,
"description": "Sale price or market value (whichever higher), in SGD."
},
"saleDate": {
"type": "string",
"description": "Date the property is/was sold (ISO date, YYYY-MM-DD)."
},
"purchaseDate": {
"type": "string",
"description": "Date the property was acquired (ISO date, YYYY-MM-DD)."
}
}
}Run the regulatory affordability gate for up to four borrowers. Given each borrower's income, rental, pledged/unpledged assets and liabilities plus a proposed loan, returns the qualifying income (after MAS haircuts), the TDSR and MSR for the proposed loan, the income-weighted average age (IWAA), and the maximum loan under both HDB/EC and private property rules. Use this upstream of `recommend_packages` to confirm what a borrower can qualify for.
| Argument | Type | Description |
|---|---|---|
| asOfDate | string | Reference date (ISO YYYY-MM-DD) for age and IWAA; defaults to today. |
| borrowers* | array | One Main borrower plus up to three joint borrowers (4 total). |
| propertyType | string | Property type: hdb, ec or private. Sets the maximum loan tenure (35y private and EC, 30y HDB) and the lower-LTV-tier triggers. EC also carries MSR, like HDB. |
| targetLtvCap | number | Optional manual LTV cap for the target loan, as a decimal (e.g. 0.6 = 60%). When set and below the applicable LTV, toReachTarget is sized at this lower LTV so the loan and unlock figures reflect a smaller loan the borrower chooses to take. |
| ageConvention | string | How borrower age is computed; defaults to birth-date. The tRPC router resolves it per bank from the bank slug. |
| benchmarkRate | number |
Compare 2–3 SPECIFIC mortgage packages side by side: average rate over the lock-in, benchmark index, effective rate and monthly instalment per year, interest paid per year, and flexibility terms (prepayment, free conversion, sale waiver, penalties, minimum loan). Pass `packageIds` from `recommend_packages`, plus `loanAmount` and optionally `tenureYears` (default 25). `dealKind`/`propertyType`/`propertyStatus` only label the report header. Pass `formats: ["pdf"]` to also render the one-page comparison document — the download URL comes back under `documents.pdfUrl` (null otherwise). This comparison has no verdict/recommendation section by default because it carries no borrower context; for a personalised ranking with verdicts, use `recommend_packages` (optionally with `formats`). The optional `verdicts`/`recommendedId` inputs only carry over the reasoning from a prior `recommend_packages` call — never author them.
| Argument | Type | Description |
|---|---|---|
| formats | array | Document formats to generate and upload (currently only "pdf"); the download URL is returned under `documents`. Omit/empty for data only. |
| dealKind | string | Shown in the report header when provided. |
| verdicts | array | Per-package verdict texts carried over from a prior `recommend` call (the LLM reasoning). Adds the verdict row to the comparison; omit for a verdict-free report. Do not author these by hand. |
| loanAmount* | integer | Loan principal in SGD (whole dollars). |
| packageIds |
Return historical quotes (rate by date) for a market benchmark index such as SORA. Requires `marketIndexId` — use `list_market_indices` to resolve a benchmark name/slug into its UUID. Optional `startDate`/`endDate` (ISO dates) narrow the range; results are paginated. Quote `rate` values are decimal fractions (0.0356 = 3.56% p.a.).
| Argument | Type | Description |
|---|---|---|
| page | integer | 1-based page number. |
| limit | integer | Page size (1–100). |
| endDate | string | Only return quotes on/before this date (ISO date, YYYY-MM-DD). |
| startDate | string | Only return quotes on/after this date (ISO date, YYYY-MM-DD). |
| marketIndexId* | string | Market index UUID. Resolve a benchmark name/slug via `list_market_indices`. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"marketIndexId"
],
"properties": {
"page": {
"type": "integer",
"default": 1,
"maximum": 9007199254740991,
"minimum": 1,
"description": "1-based page number."
},
"limit": {
"type": "integer",
"default": 50,
"maximum": 100,
"minimum": 1,
"description": "Page size (1–100)."
},
"endDate": {
"type": "string",
"format": "date",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$",
"description": "Only return quotes on/before this date (ISO date, YYYY-MM-DD)."
},
"startDate": {
"type": "string",
"format": "date",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$",
"description": "Only return quotes on/after this date (ISO date, YYYY-MM-DD)."
},
"marketIndexId": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Market index UUID. Resolve a benchmark name/slug via `list_market_indices`."
}
}
}Indicative SPML valuation for a Singapore unit. Requires postal code and unit number in FF-SS format (e.g. "04-02"). propertyType ("Condo" or "HDB") and areaSqft are optional — when propertyType is omitted, both types are probed and the one that values the unit is used (`propertyTypeUsed` in the result tells you which). A `valued: false` result means SPML could not value the unit — that is a valid answer, not an error.
| Argument | Type | Description |
|---|---|---|
| areaSqft | number | Optional built-in/strata area in sqft; can rescue a "$0" valuation. |
| postalCode* | string | Singapore 6-digit postal code, e.g. "248973". |
| unitNumber* | string | Unit in floor-stack "FF-SS" format, e.g. "04-02". |
| propertyType | string | Optional: "Condo" or "HDB". Omit to auto-detect — both are probed and the one that values the unit is used (`propertyTypeUsed` reports which). |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"postalCode",
"unitNumber"
],
"properties": {
"areaSqft": {
"type": "number",
"description": "Optional built-in/strata area in sqft; can rescue a \"$0\" valuation.",
"exclusiveMinimum": 0
},
"postalCode": {
"type": "string",
"pattern": "^\\d{6}$",
"description": "Singapore 6-digit postal code, e.g. \"248973\"."
},
"unitNumber": {
"type": "string",
"pattern": "^\\d{2,3}-\\d{1,4}$",
"description": "Unit in floor-stack \"FF-SS\" format, e.g. \"04-02\"."
},
"propertyType": {
"enum": [
"Condo",
"HDB"
],
"type": "string",
"description": "Optional: \"Condo\" or \"HDB\". Omit to auto-detect — both are probed and the one that values the unit is used (`propertyTypeUsed` reports which)."
}
}
}List active banks (id + name), sorted by name. Use this to map a borrower's current lender name to the `currentBankId` UUID required by `recommend_packages` for refinance deals.
No arguments.
{
"type": "object",
"properties": {}
}List market benchmark indices (id, slug, name, currency), sorted by slug. Use this to resolve a benchmark name like 'SORA' into the `marketIndexId` required by `get_index_quotes`.
No arguments.
{
"type": "object",
"properties": {}
}Rank Singapore mortgage packages for a borrower. Returns up to 3 LLM-ranked picks with per-package reasoning, the 3 lowest-rate options, and a lookup of full package details. Core inputs (gather these first; ask the borrower if missing — do not guess): `dealKind` (PURCHASE | REFINANCE); `currentBankId` (required only for REFINANCE — use `list_banks` to resolve the lender name into its UUID); `propertyType` (HDB | PRIVATE | COMMERCIAL); `propertyStatus`; `loanAmount`. `propertyStatus` depends on `propertyType` — only these pairings are valid: HDB → COMPLETED (resale) or BTO (Build-To-Order, new); PRIVATE → COMPLETED (ready-built) or BUC (Built-Under-Construction, new); COMMERCIAL → OWNEROCCUPIED, INVESTMENT or PERSONAL. Do not mix across types (e.g. HDB + BUC or PRIVATE + BTO are invalid). Optional inputs (only refine the ranking): `ratesType` (FIXED | FLOATING) and `userContext` (free-text borrower priorities, timeline, bank relationships, citizenship, etc.). Pass `formats: ["pdf"]` to also generate a one-page comparison document of the recommended set (with per-package verdicts); the download URL comes back under `documents`. Optional `tenureYears` (default 25) sets the document's instalment assumptions. Stateless: if any input or preference changes, call this tool again with the full updated set — never infer an updated answer from a previous response.
| Argument | Type | Description |
|---|---|---|
| formats | array | Document formats to generate for the recommended set (currently only "pdf"); the download URL is returned under `documents`. Omit/empty for data only. |
| dealKind* | string | PURCHASE (new loan) or REFINANCE (switching lender). |
| ratesType | string | Optional rate type: FIXED or FLOATING. This is a hard filter on the candidate pool, not a ranking hint. Omit it when the borrower stated no preference, so a mix of rate types stays eligible. |
| buyer1CpfUsage |
| number |
| CPF (with accrued interest) buyer 1 must refund on sale, in SGD. |
| buyer2CpfUsage | number | CPF (with accrued interest) buyer 2 must refund on sale, in SGD. |
| outstandingLoan | number | Outstanding loan on that property, in SGD. |
| sellerStampDuty | number | Seller's Stamp Duty payable on the sale, in SGD. |
| sellingLegalFees | number | Legal fees for the sale, in SGD. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"valuation",
"buyer1",
"buyer2"
],
"properties": {
"buy1": {
"type": "object",
"required": [
"propertyPrice"
],
"properties": {
"legalFees": {
"type": "number",
"default": 3000,
"minimum": 0,
"description": "Conveyancing/legal fees, in SGD."
},
"propertyPrice": {
"type": "number",
"minimum": 0,
"description": "Purchase price of the property, in SGD."
},
"loanInterestRate": {
"type": "number",
"default": 2.6,
"maximum": 15,
"minimum": 0.1,
"description": "Assumed annual loan rate, as a percentage (e.g. 2.6 = 2.6%)."
},
"remainingLeaseYears": {
"type": "number",
"maximum": 99,
"minimum": 0,
"description": "Remaining lease in years; omit for freehold."
}
},
"description": "The single property in the \"buy one\" scenario."
},
"buyer1": {
"type": "object",
"required": [
"monthlyIncome"
],
"properties": {
"age": {
"type": "integer",
"default": 35,
"maximum": 80,
"minimum": 21,
"description": "Borrower age."
},
"cpfOA": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "CPF Ordinary Account balance usable, in SGD."
},
"miscIncome": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Other monthly income, in SGD."
},
"citizenship": {
"enum": [
"singaporean",
"pr",
"foreigner"
],
"type": "string",
"default": "singaporean",
"description": "Buyer citizenship: singaporean, pr or foreigner."
},
"monthlyDebt": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Existing monthly debt commitments, in SGD."
},
"monthlyIncome": {
"type": "number",
"minimum": 0,
"description": "Gross monthly income, in SGD."
},
"isVariableIncome": {
"type": "boolean",
"default": false,
"description": "True if income is variable (bonus/commission); haircut 30% under TDSR."
}
},
"description": "Income/CPF profile of buyer 1."
},
"buyer2": {
"type": "object",
"required": [
"monthlyIncome"
],
"properties": {
"age": {
"type": "integer",
"default": 35,
"maximum": 80,
"minimum": 21,
"description": "Borrower age."
},
"cpfOA": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "CPF Ordinary Account balance usable, in SGD."
},
"miscIncome": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Other monthly income, in SGD."
},
"citizenship": {
"enum": [
"singaporean",
"pr",
"foreigner"
],
"type": "string",
"default": "singaporean",
"description": "Buyer citizenship: singaporean, pr or foreigner."
},
"monthlyDebt": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Existing monthly debt commitments, in SGD."
},
"monthlyIncome": {
"type": "number",
"minimum": 0,
"description": "Gross monthly income, in SGD."
},
"isVariableIncome": {
"type": "boolean",
"default": false,
"description": "True if income is variable (bonus/commission); haircut 30% under TDSR."
}
},
"description": "Income/CPF profile of buyer 2."
},
"valuation": {
"type": "number",
"minimum": 0,
"description": "Current market valuation of the property being sold, in SGD."
},
"buyer1Shares": {
"type": "number",
"default": 50,
"maximum": 100,
"minimum": 0,
"description": "Buyer 1 ownership share of the sold property, as a percentage (0–100)."
},
"buyer2Shares": {
"type": "number",
"default": 50,
"maximum": 100,
"minimum": 0,
"description": "Buyer 2 ownership share of the sold property, as a percentage (0–100)."
},
"buy2Property1": {
"type": "object",
"required": [
"propertyPrice"
],
"properties": {
"legalFees": {
"type": "number",
"default": 3000,
"minimum": 0,
"description": "Conveyancing/legal fees, in SGD."
},
"propertyPrice": {
"type": "number",
"minimum": 0,
"description": "Purchase price of the property, in SGD."
},
"loanInterestRate": {
"type": "number",
"default": 2.6,
"maximum": 15,
"minimum": 0.1,
"description": "Assumed annual loan rate, as a percentage (e.g. 2.6 = 2.6%)."
},
"remainingLeaseYears": {
"type": "number",
"maximum": 99,
"minimum": 0,
"description": "Remaining lease in years; omit for freehold."
}
},
"description": "First property in the \"buy two\" scenario."
},
"buy2Property2": {
"type": "object",
"required": [
"propertyPrice"
],
"properties": {
"legalFees": {
"type": "number",
"default": 3000,
"minimum": 0,
"description": "Conveyancing/legal fees, in SGD."
},
"propertyPrice": {
"type": "number",
"minimum": 0,
"description": "Purchase price of the property, in SGD."
},
"loanInterestRate": {
"type": "number",
"default": 2.6,
"maximum": 15,
"minimum": 0.1,
"description": "Assumed annual loan rate, as a percentage (e.g. 2.6 = 2.6%)."
},
"remainingLeaseYears": {
"type": "number",
"maximum": 99,
"minimum": 0,
"description": "Remaining lease in years; omit for freehold."
}
},
"description": "Second property in the \"buy two\" scenario."
},
"buyer1CpfUsage": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "CPF (with accrued interest) buyer 1 must refund on sale, in SGD."
},
"buyer2CpfUsage": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "CPF (with accrued interest) buyer 2 must refund on sale, in SGD."
},
"outstandingLoan": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Outstanding loan on that property, in SGD."
},
"sellerStampDuty": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Seller's Stamp Duty payable on the sale, in SGD."
},
"sellingLegalFees": {
"type": "number",
"default": 3000,
"minimum": 0,
"description": "Legal fees for the sale, in SGD."
}
}
}| loanTenureYears | integer | Loan tenure in years. |
| existingProperty | boolean | True if the borrower already owns ≥1 property (affects ABSD and LTV). |
| monthlyFixedIncome* | number | Gross fixed monthly income (salary), in SGD. |
| monthlyVariableIncome | number | Gross variable monthly income (bonus/commission), in SGD; haircut 30% under TDSR. |
| existingPropertiesOwned | integer | Count of residential properties already owned; sets the ABSD tier (0, 1 or 2+). Takes precedence over existingProperty when provided. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"monthlyFixedIncome"
],
"properties": {
"age": {
"type": "integer",
"default": 35,
"maximum": 80,
"minimum": 21,
"description": "Age of the (oldest) borrower."
},
"cpfOA": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "CPF Ordinary Account balance usable, in SGD."
},
"cashSavings": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Cash available for downpayment, in SGD."
},
"citizenship": {
"enum": [
"singaporean",
"pr",
"foreigner"
],
"type": "string",
"default": "singaporean",
"description": "Buyer citizenship: singaporean, pr or foreigner. Drives ABSD rate."
},
"monthlyDebt": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Total existing monthly debt commitments (other loans, cards), in SGD."
},
"interestRate": {
"type": "number",
"default": 4,
"maximum": 15,
"minimum": 0.1,
"description": "Assumed annual interest rate, as a percentage (e.g. 4.0 = 4%, not 0.04)."
},
"propertyType": {
"enum": [
"hdb",
"ec",
"private"
],
"type": "string",
"default": "hdb",
"description": "Property type: hdb, ec (Executive Condominium) or private. Sets MSR/LTV rules."
},
"stressTestRate": {
"type": "number",
"maximum": 15,
"minimum": 0.1,
"description": "Override the TDSR/MSR stress-test rate (percentage); defaults to the MAS floor."
},
"loanTenureYears": {
"type": "integer",
"default": 25,
"maximum": 35,
"minimum": 1,
"description": "Loan tenure in years."
},
"existingProperty": {
"type": "boolean",
"default": false,
"description": "True if the borrower already owns ≥1 property (affects ABSD and LTV)."
},
"monthlyFixedIncome": {
"type": "number",
"minimum": 0,
"description": "Gross fixed monthly income (salary), in SGD."
},
"monthlyVariableIncome": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Gross variable monthly income (bonus/commission), in SGD; haircut 30% under TDSR."
},
"existingPropertiesOwned": {
"type": "integer",
"maximum": 10,
"minimum": 0,
"description": "Count of residential properties already owned; sets the ABSD tier (0, 1 or 2+). Takes precedence over existingProperty when provided."
}
}
}| tenureYears |
| integer |
| Loan tenure in years. Supply with interestRate to get the monthly instalment. |
| exerciseDate* | string | Date the option is exercised (ISO 8601). |
| interestRate | number | Annual rate as a percentage (3.5 = 3.5%). Supply with tenureYears to get the monthly instalment; omit and it is returned as null. |
| purchasePrice* | number | Purchase price of the HDB flat, in SGD. |
| completionDate | string | Completion date (ISO 8601). Defaults to exerciseDate + 70 calendar days if not provided. |
| optionExpiryDate* | string | Last day to exercise the option (ISO 8601). |
| existingPropertiesOwned | integer | Number of properties already owned. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"purchasePrice",
"optionDate",
"optionExpiryDate",
"exerciseDate"
],
"properties": {
"cpfUsage": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "CPF OA usage toward the purchase, in SGD."
},
"legalFee": {
"type": "number",
"default": 2000,
"minimum": 0,
"description": "Legal/conveyancing fee, in SGD."
},
"buyerType": {
"enum": [
"singaporean",
"pr",
"foreigner",
"entity",
"trust"
],
"type": "string",
"default": "singaporean",
"description": "Buyer profile for ABSD calculation."
},
"optionFee": {
"type": "number",
"default": 1000,
"minimum": 0,
"description": "Option fee, in SGD (default $1,000, HDB cap)."
},
"otherFees": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Other costs (valuation, repairs, admin), in SGD. Reported alongside legal fees and stamp duty as part of the total outlay."
},
"loanAmount": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Bank loan amount, in SGD."
},
"optionDate": {
"type": "string",
"description": "Option to Purchase date (ISO 8601, e.g. 2026-08-01)."
},
"exerciseFee": {
"type": "number",
"default": 4000,
"minimum": 0,
"description": "Exercise fee, in SGD (default $4,000)."
},
"tenureYears": {
"type": "integer",
"maximum": 35,
"minimum": 1,
"description": "Loan tenure in years. Supply with interestRate to get the monthly instalment."
},
"exerciseDate": {
"type": "string",
"description": "Date the option is exercised (ISO 8601)."
},
"interestRate": {
"type": "number",
"maximum": 15,
"minimum": 0.1,
"description": "Annual rate as a percentage (3.5 = 3.5%). Supply with tenureYears to get the monthly instalment; omit and it is returned as null."
},
"purchasePrice": {
"type": "number",
"minimum": 1,
"description": "Purchase price of the HDB flat, in SGD."
},
"completionDate": {
"type": "string",
"description": "Completion date (ISO 8601). Defaults to exerciseDate + 70 calendar days if not provided."
},
"optionExpiryDate": {
"type": "string",
"description": "Last day to exercise the option (ISO 8601)."
},
"existingPropertiesOwned": {
"type": "integer",
"default": 0,
"maximum": 10,
"minimum": 0,
"description": "Number of properties already owned."
}
}
}| firstPaymentDate | string | First repayment month as an ISO date (YYYY-MM-DD). Defaults to next month. |
| maskBankIdentity | boolean | When true and the bank has maskIdentity set, replace the bank name with "Confidential Rate" and hide the logo in generated documents. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"packageId",
"loanAmount",
"tenureYears"
],
"properties": {
"formats": {
"type": "array",
"items": {
"enum": [
"pdf",
"xlsx"
],
"type": "string"
},
"description": "Document formats to generate and upload (pdf, xlsx); the resulting download URLs are returned under `documents`. Omit/empty for data only."
},
"packageId": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "Package id, as returned by `recommend_packages`."
},
"loanAmount": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "Loan principal in SGD (whole dollars)."
},
"tenureYears": {
"type": "integer",
"maximum": 35,
"minimum": 1,
"description": "Loan tenure in years (1–35)."
},
"includeMonthly": {
"type": "boolean",
"description": "Include the full month-by-month schedule (`monthly[]`) in the response. Omitted/false returns an empty `monthly[]` to keep the payload small."
},
"firstPaymentDate": {
"type": "string",
"format": "date",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$",
"description": "First repayment month as an ISO date (YYYY-MM-DD). Defaults to next month."
},
"maskBankIdentity": {
"type": "boolean",
"description": "When true and the bank has maskIdentity set, replace the bank name with \"Confidential Rate\" and hide the logo in generated documents."
}
}
}| Exercise date (ISO 8601). Defaults to optionDate + 13 calendar days if not provided. |
| interestRate | number | Annual rate as a percentage (3.5 = 3.5%). Supply with tenureYears to get the monthly instalment; omit and it is returned as null. |
| purchasePrice* | number | Purchase price of the private property, in SGD. |
| completionDate | string | Completion date (ISO 8601). Defaults to exerciseDate + 70 calendar days if not provided. |
| optionExpiryDate* | string | Last day to exercise the option (ISO 8601). |
| existingPropertiesOwned | integer | Number of properties already owned. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"purchasePrice",
"optionDate",
"optionExpiryDate"
],
"properties": {
"cpfUsage": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "CPF OA usage toward the purchase, in SGD."
},
"legalFee": {
"type": "number",
"default": 2500,
"minimum": 0,
"description": "Legal fee, in SGD (default $2,500 for private)."
},
"buyerType": {
"enum": [
"singaporean",
"pr",
"foreigner",
"entity",
"trust"
],
"type": "string",
"default": "singaporean",
"description": "Buyer profile for ABSD calculation."
},
"otherFees": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Other costs (valuation, repairs, admin), in SGD. Reported alongside legal fees and stamp duty as part of the total outlay."
},
"loanAmount": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Bank loan amount, in SGD."
},
"optionDate": {
"type": "string",
"description": "Option to Purchase date (ISO 8601)."
},
"tenureYears": {
"type": "integer",
"maximum": 35,
"minimum": 1,
"description": "Loan tenure in years. Supply with interestRate to get the monthly instalment."
},
"exerciseDate": {
"type": "string",
"description": "Exercise date (ISO 8601). Defaults to optionDate + 13 calendar days if not provided."
},
"interestRate": {
"type": "number",
"maximum": 15,
"minimum": 0.1,
"description": "Annual rate as a percentage (3.5 = 3.5%). Supply with tenureYears to get the monthly instalment; omit and it is returned as null."
},
"purchasePrice": {
"type": "number",
"minimum": 1,
"description": "Purchase price of the private property, in SGD."
},
"completionDate": {
"type": "string",
"description": "Completion date (ISO 8601). Defaults to exerciseDate + 70 calendar days if not provided."
},
"optionExpiryDate": {
"type": "string",
"description": "Last day to exercise the option (ISO 8601)."
},
"existingPropertiesOwned": {
"type": "integer",
"default": 0,
"maximum": 10,
"minimum": 0,
"description": "Number of properties already owned."
}
}
}| ratePerYear* | array | Annual interest rates by year as percentages (e.g. [2.5, 2.5, 3.0, 3.5, 3.5, 3.5] for years 1-5 and thereafter). Last value applies to all subsequent years. |
| tenureYears* | integer | Loan tenure in years. |
| lockInMonths | integer | Lock-in period in months from first drawdown. |
| purchasePrice* | number | Purchase price of the BUC property, in SGD. |
| referenceRate | number | Reference rate for floating mode, as a percentage (e.g. 3.5 = 3.5%). |
| spreadPerYear | array | Spread per year for floating mode, as percentages. Last value applies to all subsequent years. |
| availableCpfOA | number | Available CPF Ordinary Account balance, in SGD. When set, each stage’s equity CPF portion is capped at the remaining OA balance and the rest is funded by cash. When omitted, CPF covers all equity above the minimum-cash floor. |
| cscOffsetMonths | integer | Months from TOP to CSC (default 12). |
| includeSchedule | boolean | If true, include the full month-by-month amortisation schedule in output. |
| prepaymentOutcome | string | What happens after a prepayment: lower instalment or shorter tenure. |
| minimumCashPercent | number | Minimum cash portion of equity as a percentage (e.g. 5 = 5%). |
| prepaymentPenaltyPercent | number | Penalty as a percentage of prepaid amount if within lock-in period. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"purchasePrice",
"tenureYears",
"otpDate",
"topDate",
"ratePerYear"
],
"properties": {
"stages": {
"type": "array",
"items": {
"type": "object",
"required": [
"name",
"percent"
],
"properties": {
"name": {
"type": "string",
"description": "Stage name (e.g. \"Foundation work\")."
},
"percent": {
"type": "number",
"maximum": 100,
"minimum": 0,
"description": "Percentage of purchase price due at this stage."
}
}
},
"description": "Custom stage schedule. Defaults to the standard 10-stage BUC schedule. Percentages must sum to 100."
},
"otpDate": {
"type": "string",
"description": "Option to Purchase date (ISO 8601)."
},
"topDate": {
"type": "string",
"description": "Expected Temporary Occupation Permit date (ISO 8601)."
},
"rateMode": {
"enum": [
"fixed",
"floating"
],
"type": "string",
"default": "fixed",
"description": "Rate type for the loan package."
},
"loanAmount": {
"type": "number",
"minimum": 0,
"description": "Loan amount in SGD. Derives loanQuantum if provided. One of loanQuantum or loanAmount is required."
},
"loanQuantum": {
"type": "number",
"maximum": 0.9,
"minimum": 0,
"description": "Loan as a fraction of price (e.g. 0.75 = 75%). Derives loanAmount if provided."
},
"prepayments": {
"type": "array",
"items": {
"type": "object",
"required": [
"month",
"amount"
],
"properties": {
"month": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": 1,
"description": "Month index in the amortisation schedule."
},
"amount": {
"type": "number",
"minimum": 0,
"description": "Prepayment amount, in SGD."
}
}
},
"default": [],
"description": "Optional list of prepayments."
},
"ratePerYear": {
"type": "array",
"items": {
"type": "number",
"minimum": 0
},
"minItems": 1,
"description": "Annual interest rates by year as percentages (e.g. [2.5, 2.5, 3.0, 3.5, 3.5, 3.5] for years 1-5 and thereafter). Last value applies to all subsequent years."
},
"tenureYears": {
"type": "integer",
"maximum": 35,
"minimum": 1,
"description": "Loan tenure in years."
},
"lockInMonths": {
"type": "integer",
"default": 0,
"maximum": 9007199254740991,
"minimum": 0,
"description": "Lock-in period in months from first drawdown."
},
"purchasePrice": {
"type": "number",
"minimum": 1,
"description": "Purchase price of the BUC property, in SGD."
},
"referenceRate": {
"type": "number",
"minimum": 0,
"description": "Reference rate for floating mode, as a percentage (e.g. 3.5 = 3.5%)."
},
"spreadPerYear": {
"type": "array",
"items": {
"type": "number"
},
"description": "Spread per year for floating mode, as percentages. Last value applies to all subsequent years."
},
"availableCpfOA": {
"type": "number",
"minimum": 0,
"description": "Available CPF Ordinary Account balance, in SGD. When set, each stage’s equity CPF portion is capped at the remaining OA balance and the rest is funded by cash. When omitted, CPF covers all equity above the minimum-cash floor."
},
"cscOffsetMonths": {
"type": "integer",
"default": 12,
"maximum": 9007199254740991,
"minimum": 0,
"description": "Months from TOP to CSC (default 12)."
},
"includeSchedule": {
"type": "boolean",
"default": false,
"description": "If true, include the full month-by-month amortisation schedule in output."
},
"prepaymentOutcome": {
"enum": [
"reduceInstalment",
"shortenTenure"
],
"type": "string",
"default": "reduceInstalment",
"description": "What happens after a prepayment: lower instalment or shorter tenure."
},
"minimumCashPercent": {
"type": "number",
"default": 5,
"maximum": 100,
"minimum": 0,
"description": "Minimum cash portion of equity as a percentage (e.g. 5 = 5%)."
},
"prepaymentPenaltyPercent": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Penalty as a percentage of prepaid amount if within lock-in period."
}
}
}| availableCpfOA | number | Available CPF Ordinary Account balance, in SGD. |
| legalFeeFunding | string | Whether legal/conveyancing fee is paid from cash or CPF. |
| legalFeeOverride | number | Override for legal/conveyancing fee, in SGD. Defaults to $2,000 (HDB) or $3,000 (private). |
| stampDutyFunding | string | Whether BSD + ABSD are funded from cash or CPF. When omitted, defaults to the property-type convention (HDB funds BSD from CPF, private from cash; ABSD always cash). |
| minimumCashPercent | number | Minimum cash component as a percentage of price: 5 or 10 on a first housing loan, 25 with one already outstanding. |
| valuationFeeOverride | number | Override for valuation fee, in SGD. Defaults to $120 (HDB) or $350 (private). |
| existingPropertiesOwned | integer | Number of residential properties already owned. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"propertyType",
"purchasePrice"
],
"properties": {
"ltv": {
"type": "number",
"maximum": 0.9,
"minimum": 0,
"description": "Loan-to-value ratio as a decimal (e.g. 0.75 = 75%). If omitted, derived from loanAmount / purchasePrice."
},
"cpfFirst": {
"type": "boolean",
"description": "When true, fund CPF-eligible costs from a single CPF pool capped at availableCpfOA (CPF first, remainder cash), overriding the per-item cash/cpf funding sources. ABSD stays cash."
},
"buyerType": {
"enum": [
"singaporean",
"pr",
"foreigner",
"entity",
"trust"
],
"type": "string",
"default": "singaporean",
"description": "Buyer profile driving ABSD rate."
},
"loanAmount": {
"type": "number",
"minimum": 0,
"description": "Bank loan amount, in SGD. If omitted, derived from ltv * purchasePrice."
},
"propertyType": {
"enum": [
"hdb",
"private"
],
"type": "string",
"description": "HDB or private (non-HDB) purchase."
},
"availableCash": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Available cash for the purchase, in SGD."
},
"purchasePrice": {
"type": "number",
"minimum": 1,
"description": "Purchase price of the property, in SGD."
},
"availableCpfOA": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Available CPF Ordinary Account balance, in SGD."
},
"legalFeeFunding": {
"enum": [
"cash",
"cpf"
],
"type": "string",
"default": "cash",
"description": "Whether legal/conveyancing fee is paid from cash or CPF."
},
"legalFeeOverride": {
"type": "number",
"minimum": 0,
"description": "Override for legal/conveyancing fee, in SGD. Defaults to $2,000 (HDB) or $3,000 (private)."
},
"stampDutyFunding": {
"enum": [
"cash",
"cpf"
],
"type": "string",
"description": "Whether BSD + ABSD are funded from cash or CPF. When omitted, defaults to the property-type convention (HDB funds BSD from CPF, private from cash; ABSD always cash)."
},
"minimumCashPercent": {
"type": "number",
"default": 5,
"description": "Minimum cash component as a percentage of price: 5 or 10 on a first housing loan, 25 with one already outstanding."
},
"valuationFeeOverride": {
"type": "number",
"minimum": 0,
"description": "Override for valuation fee, in SGD. Defaults to $120 (HDB) or $350 (private)."
},
"existingPropertiesOwned": {
"type": "integer",
"default": 0,
"maximum": 10,
"minimum": 0,
"description": "Number of residential properties already owned."
}
}
}| refinancingCosts |
| number |
| Legal/valuation costs of refinancing, in SGD. |
| originalLoanAmount | number | Original loan principal at drawdown, in SGD. |
| originalTenureYears | integer | Original loan tenure in years. |
| remainingTenureYears | integer | Remaining tenure on the loan, in years. |
| earlyRedemptionPenalty | number | Penalty for redeeming the current loan early, in SGD. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"mode",
"currentRate",
"newRate"
],
"properties": {
"mode": {
"enum": [
"original",
"remaining"
],
"type": "string",
"description": "Use 'original' when supplying the loan's original terms and start date, 'remaining' when supplying the current outstanding balance and tenure left."
},
"newRate": {
"type": "number",
"maximum": 15,
"minimum": 0.1,
"description": "Proposed new annual rate, as a percentage (e.g. 3.2 = 3.2%)."
},
"currentRate": {
"type": "number",
"maximum": 15,
"minimum": 0.1,
"description": "Current annual rate, as a percentage (e.g. 4.0 = 4%)."
},
"loanStartYear": {
"type": "integer",
"maximum": 2100,
"minimum": 1990,
"description": "Year the loan started."
},
"loanStartMonth": {
"type": "integer",
"maximum": 12,
"minimum": 1,
"description": "Month the loan started (1–12)."
},
"newTenureYears": {
"type": "integer",
"maximum": 35,
"minimum": 1,
"description": "New tenure in years; defaults to the remaining tenure if omitted."
},
"remainingAmount": {
"type": "number",
"minimum": 0,
"description": "Current outstanding loan balance, in SGD."
},
"refinancingCosts": {
"type": "number",
"default": 2500,
"minimum": 0,
"description": "Legal/valuation costs of refinancing, in SGD."
},
"originalLoanAmount": {
"type": "number",
"minimum": 0,
"description": "Original loan principal at drawdown, in SGD."
},
"originalTenureYears": {
"type": "integer",
"maximum": 35,
"minimum": 1,
"description": "Original loan tenure in years."
},
"remainingTenureYears": {
"type": "integer",
"maximum": 35,
"minimum": 1,
"description": "Remaining tenure on the loan, in years."
},
"earlyRedemptionPenalty": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Penalty for redeeming the current loan early, in SGD."
}
}
}| stressTestRate |
| number |
| Override the TDSR/MSR stress-test rate (percentage); defaults to the MAS floor. |
| loanTenureYears | integer | Loan tenure in years. |
| existingProperty | boolean | True if the borrower already owns ≥1 property (affects ABSD and LTV). |
| existingPropertiesOwned | integer | Count of residential properties already owned; sets the ABSD tier (0, 1 or 2+). Takes precedence over existingProperty when provided. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"propertyPrice"
],
"properties": {
"age": {
"type": "integer",
"default": 35,
"maximum": 80,
"minimum": 21,
"description": "Age of the (oldest) borrower."
},
"cpfOA": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "CPF Ordinary Account balance usable, in SGD."
},
"cashSavings": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Cash available for downpayment, in SGD."
},
"citizenship": {
"enum": [
"singaporean",
"pr",
"foreigner"
],
"type": "string",
"default": "singaporean",
"description": "Buyer citizenship: singaporean, pr or foreigner. Drives ABSD rate."
},
"monthlyDebt": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Total existing monthly debt commitments, in SGD."
},
"interestRate": {
"type": "number",
"default": 4,
"maximum": 15,
"minimum": 0.1,
"description": "Assumed annual interest rate, as a percentage (e.g. 4.0 = 4%, not 0.04)."
},
"propertyType": {
"enum": [
"hdb",
"ec",
"private"
],
"type": "string",
"default": "hdb",
"description": "Property type: hdb, ec (Executive Condominium) or private."
},
"propertyPrice": {
"type": "number",
"minimum": 0,
"description": "Target property purchase price, in SGD."
},
"stressTestRate": {
"type": "number",
"maximum": 15,
"minimum": 0.1,
"description": "Override the TDSR/MSR stress-test rate (percentage); defaults to the MAS floor."
},
"loanTenureYears": {
"type": "integer",
"default": 25,
"maximum": 35,
"minimum": 1,
"description": "Loan tenure in years."
},
"existingProperty": {
"type": "boolean",
"default": false,
"description": "True if the borrower already owns ≥1 property (affects ABSD and LTV)."
},
"existingPropertiesOwned": {
"type": "integer",
"maximum": 10,
"minimum": 0,
"description": "Count of residential properties already owned; sets the ABSD tier (0, 1 or 2+). Takes precedence over existingProperty when provided."
}
}
}| Outstanding home loan to be redeemed on completion, in SGD. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"sellingPrice",
"owners"
],
"properties": {
"owners": {
"type": "array",
"items": {
"type": "object",
"properties": {
"cpfUtilised": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "CPF principal this owner used for the property (downpayment, instalments, stamp duty), in SGD."
},
"cpfAccruedInterest": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Accrued interest owed to this owner's CPF account on the CPF used, in SGD."
}
}
},
"maxItems": 4,
"minItems": 1,
"description": "CPF usage per owner (1 to 4 owners). Include every owner; use zeros for an owner who used no CPF."
},
"legalFees": {
"type": "number",
"default": 3000,
"minimum": 0,
"description": "Conveyancing/legal fees, in SGD."
},
"sellingPrice": {
"type": "number",
"minimum": 0,
"description": "Sale price of the property, in SGD."
},
"agentFeePercent": {
"type": "number",
"default": 1,
"maximum": 5,
"minimum": 0,
"description": "Agent commission as a percentage of the selling price, before GST (e.g. 1 = 1%, 1.5 = 1.5%, not 0.01). 9% GST is added on top."
},
"miscellaneousFees": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Other selling costs (repairs, valuation, admin), in SGD."
},
"bridgingRatePercent": {
"type": "number",
"maximum": 15,
"minimum": 0,
"description": "Annual bridging-loan rate for the interest-only instalment, as a percentage; defaults to 5."
},
"outstandingLoanAmount": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Outstanding home loan to be redeemed on completion, in SGD."
}
}
}| MAS medium-term stress rate, as a percentage (e.g. 4.0 = 4%, not 0.04). |
| loanTenureYears | integer | Proposed loan tenure in years. |
| combinationSplit | number | For the pledge/show combination: fraction of the income gap covered by pledged funds; the remainder is covered by show funds. Defaults to 0.5. |
| targetLoanAmount | number | Optional target loan amount, in SGD. With targetPurchasePrice it sets the LTV (loan / price, capped at the applicable LTV) and takes precedence over targetLtvCap. Without a price, the price is derived as the loan divided by the applicable LTV. Either way toReachTarget is produced. |
| proposedLoanAmount* | number | The loan amount being tested, in SGD. |
| targetPurchasePrice | number | Optional target purchase price, in SGD. When set, the result includes toReachTarget: the loan needed at the first-loan LTV and the eligible funds to reach it. |
| existingHousingLoans | integer | Number of outstanding housing loans held. Selects the LTV tier (0 -> full, 1 -> second, 2+ -> third). Defaults to 0 when omitted. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"borrowers",
"proposedLoanAmount"
],
"properties": {
"asOfDate": {
"type": "string",
"description": "Reference date (ISO YYYY-MM-DD) for age and IWAA; defaults to today."
},
"borrowers": {
"type": "array",
"items": {
"type": "object",
"properties": {
"age": {
"type": "number",
"maximum": 120,
"minimum": 0,
"description": "Borrower age in whole years. When supplied it is used directly and dateOfBirth is ignored."
},
"annualNoa": {
"type": "number",
"minimum": 0,
"description": "Annual income per the IRAS Notice of Assessment, in SGD. Only used when employedTwoYears is true; recognised variable income is then max(NOA − basic×12, annualVariableIncome)."
},
"dateOfBirth": {
"type": "string",
"description": "Borrower date of birth (ISO date, YYYY-MM-DD). Blank resolves to age 0."
},
"liabilities": {
"type": "array",
"items": {
"type": "object",
"properties": {
"label": {
"type": "string",
"default": "",
"maxLength": 60,
"description": "Free-form liability label (e.g. car loan, personal loan, credit card)."
},
"monthlyAmount": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Monthly repayment for this liability, in SGD."
}
}
},
"default": [],
"maxItems": 3,
"description": "Up to three monthly liability lines."
},
"pledgedFunds": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Eligible financial assets pledged ≥4 years, in SGD. Amortised over 48 months at 100%."
},
"unpledgedFunds": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Eligible financial assets not pledged, in SGD. Amortised over 48 months at 30%."
},
"employedTwoYears": {
"type": "boolean",
"description": "True if the borrower has ≥2 years with the current employer, which permits using the NOA for variable income."
},
"monthlyBasicIncome": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Monthly fixed basic income, in SGD. Recognised at 100%."
},
"monthlyRentalIncome": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Monthly rental income (requires a valid tenancy agreement), in SGD. Recognised at 70%."
},
"annualVariableIncome": {
"type": "number",
"default": 0,
"minimum": 0,
"description": "Annual variable income (bonus/commission/allowances), in SGD. Recognised at 70%, then /12."
}
}
},
"maxItems": 4,
"minItems": 1,
"description": "One Main borrower plus up to three joint borrowers (4 total)."
},
"propertyType": {
"enum": [
"hdb",
"ec",
"private"
],
"type": "string",
"default": "private",
"description": "Property type: hdb, ec or private. Sets the maximum loan tenure (35y private and EC, 30y HDB) and the lower-LTV-tier triggers. EC also carries MSR, like HDB."
},
"targetLtvCap": {
"type": "number",
"maximum": 0.9,
"minimum": 0,
"description": "Optional manual LTV cap for the target loan, as a decimal (e.g. 0.6 = 60%). When set and below the applicable LTV, toReachTarget is sized at this lower LTV so the loan and unlock figures reflect a smaller loan the borrower chooses to take."
},
"ageConvention": {
"enum": [
"birth-date",
"birth-year"
],
"type": "string",
"description": "How borrower age is computed; defaults to birth-date. The tRPC router resolves it per bank from the bank slug."
},
"benchmarkRate": {
"type": "number",
"default": 4,
"maximum": 15,
"minimum": 0.1,
"description": "MAS medium-term stress rate, as a percentage (e.g. 4.0 = 4%, not 0.04)."
},
"loanTenureYears": {
"type": "integer",
"default": 25,
"maximum": 35,
"minimum": 1,
"description": "Proposed loan tenure in years."
},
"combinationSplit": {
"type": "number",
"maximum": 1,
"minimum": 0,
"description": "For the pledge/show combination: fraction of the income gap covered by pledged funds; the remainder is covered by show funds. Defaults to 0.5."
},
"targetLoanAmount": {
"type": "number",
"minimum": 0,
"description": "Optional target loan amount, in SGD. With targetPurchasePrice it sets the LTV (loan / price, capped at the applicable LTV) and takes precedence over targetLtvCap. Without a price, the price is derived as the loan divided by the applicable LTV. Either way toReachTarget is produced."
},
"proposedLoanAmount": {
"type": "number",
"minimum": 0,
"description": "The loan amount being tested, in SGD."
},
"targetPurchasePrice": {
"type": "number",
"minimum": 0,
"description": "Optional target purchase price, in SGD. When set, the result includes toReachTarget: the loan needed at the first-loan LTV and the eligible funds to reach it."
},
"existingHousingLoans": {
"type": "integer",
"maximum": 10,
"minimum": 0,
"description": "Number of outstanding housing loans held. Selects the LTV tier (0 -> full, 1 -> second, 2+ -> third). Defaults to 0 when omitted."
}
}
}| array |
| 2–3 package ids to compare, e.g. from `recommend_packages`. |
| tenureYears | integer | Loan tenure in years. |
| propertyType | string | Shown in the report header when provided. |
| recommendedId | string | The rank-1 package from the prior `recommend` call; its column is highlighted as "Recommended". Only meaningful together with `verdicts`. |
| propertyStatus | string | Shown in the report header when provided. |
| maskBankIdentity | boolean | When true and a bank has maskIdentity set, replace the bank name with "Confidential Rate" and hide the logo in the comparison report. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"packageIds",
"loanAmount"
],
"properties": {
"formats": {
"type": "array",
"items": {
"enum": [
"pdf"
],
"type": "string"
},
"description": "Document formats to generate and upload (currently only \"pdf\"); the download URL is returned under `documents`. Omit/empty for data only."
},
"dealKind": {
"enum": [
"PURCHASE",
"REFINANCE"
],
"type": "string",
"description": "Shown in the report header when provided."
},
"verdicts": {
"type": "array",
"items": {
"type": "object",
"required": [
"packageId",
"text"
],
"properties": {
"text": {
"type": "string",
"maxLength": 2000,
"minLength": 1
},
"packageId": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
}
}
},
"maxItems": 3,
"description": "Per-package verdict texts carried over from a prior `recommend` call (the LLM reasoning). Adds the verdict row to the comparison; omit for a verdict-free report. Do not author these by hand."
},
"loanAmount": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": -9007199254740991,
"description": "Loan principal in SGD (whole dollars)."
},
"packageIds": {
"type": "array",
"items": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"maxItems": 3,
"minItems": 2,
"description": "2–3 package ids to compare, e.g. from `recommend_packages`."
},
"tenureYears": {
"type": "integer",
"default": 25,
"maximum": 35,
"minimum": 1,
"description": "Loan tenure in years."
},
"propertyType": {
"enum": [
"HDB",
"PRIVATE",
"COMMERCIAL"
],
"type": "string",
"description": "Shown in the report header when provided."
},
"recommendedId": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "The rank-1 package from the prior `recommend` call; its column is highlighted as \"Recommended\". Only meaningful together with `verdicts`."
},
"propertyStatus": {
"enum": [
"BTO",
"OWNEROCCUPIED",
"INVESTMENT",
"PERSONAL",
"COMPLETED",
"BUC"
],
"type": "string",
"description": "Shown in the report header when provided."
},
"maskBankIdentity": {
"type": "boolean",
"description": "When true and a bank has maskIdentity set, replace the bank name with \"Confidential Rate\" and hide the logo in the comparison report."
}
}
}| loanAmount* | integer | Loan principal in SGD (whole dollars). |
| tenureYears | integer | Loan tenure in years, used for the comparison document instalments. Defaults to 25. |
| userContext | any | Optional free-text summary of the borrower's priorities, timeline, bank relationships, citizenship, employment, family changes, etc. Only refines the LLM ranking; omit if there are no stated priorities. |
| propertyType* | string | Property type: HDB, PRIVATE, or COMMERCIAL. Determines the valid `propertyStatus` values. |
| currentBankId | string | The borrower's current lender, as a bank UUID from `list_banks`. Required for REFINANCE deals (excludes that bank from results); ignored for PURCHASE. |
| candidateLimit | integer | Upper bound on the packages offered inside one slot of the LLM ranker, itself capped at 8. Rarely worth setting. |
| propertyStatus* | string | Property status. Must pair with `propertyType`: HDB → COMPLETED (resale) | BTO (new); PRIVATE → COMPLETED (ready-built) | BUC (under construction); COMMERCIAL → OWNEROCCUPIED | INVESTMENT | PERSONAL. |
| lockInPeriodYrs | integer | Optional filter: maximum acceptable lock-in period in years (0–5). |
| fixedDurationYrs | any | Optional filter: required fixed-rate duration in years (1–5). Pass an array to match any of several durations. |
{
"type": "object",
"$schema": "http://json-schema.org/draft-07/schema#",
"required": [
"propertyType",
"propertyStatus",
"dealKind",
"loanAmount"
],
"properties": {
"formats": {
"type": "array",
"items": {
"enum": [
"pdf"
],
"type": "string"
},
"description": "Document formats to generate for the recommended set (currently only \"pdf\"); the download URL is returned under `documents`. Omit/empty for data only."
},
"dealKind": {
"enum": [
"PURCHASE",
"REFINANCE"
],
"type": "string",
"description": "PURCHASE (new loan) or REFINANCE (switching lender)."
},
"ratesType": {
"enum": [
"FIXED",
"FLOATING"
],
"type": "string",
"description": "Optional rate type: FIXED or FLOATING. This is a hard filter on the candidate pool, not a ranking hint. Omit it when the borrower stated no preference, so a mix of rate types stays eligible."
},
"loanAmount": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": 0,
"description": "Loan principal in SGD (whole dollars)."
},
"tenureYears": {
"type": "integer",
"maximum": 35,
"minimum": 1,
"description": "Loan tenure in years, used for the comparison document instalments. Defaults to 25."
},
"userContext": {
"anyOf": [
{
"type": "string",
"maxLength": 2000
},
{
"type": "null"
}
],
"default": null,
"description": "Optional free-text summary of the borrower's priorities, timeline, bank relationships, citizenship, employment, family changes, etc. Only refines the LLM ranking; omit if there are no stated priorities."
},
"propertyType": {
"enum": [
"HDB",
"PRIVATE",
"COMMERCIAL"
],
"type": "string",
"description": "Property type: HDB, PRIVATE, or COMMERCIAL. Determines the valid `propertyStatus` values."
},
"currentBankId": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"description": "The borrower's current lender, as a bank UUID from `list_banks`. Required for REFINANCE deals (excludes that bank from results); ignored for PURCHASE."
},
"candidateLimit": {
"type": "integer",
"default": 60,
"maximum": 60,
"minimum": 1,
"description": "Upper bound on the packages offered inside one slot of the LLM ranker, itself capped at 8. Rarely worth setting."
},
"propertyStatus": {
"enum": [
"BTO",
"OWNEROCCUPIED",
"INVESTMENT",
"PERSONAL",
"COMPLETED",
"BUC"
],
"type": "string",
"description": "Property status. Must pair with `propertyType`: HDB → COMPLETED (resale) | BTO (new); PRIVATE → COMPLETED (ready-built) | BUC (under construction); COMMERCIAL → OWNEROCCUPIED | INVESTMENT | PERSONAL."
},
"lockInPeriodYrs": {
"type": "integer",
"maximum": 5,
"minimum": 0,
"description": "Optional filter: maximum acceptable lock-in period in years (0–5)."
},
"fixedDurationYrs": {
"anyOf": [
{
"type": "integer",
"maximum": 5,
"minimum": 1
},
{
"type": "array",
"items": {
"type": "integer",
"maximum": 5,
"minimum": 1
},
"minItems": 1
}
],
"description": "Optional filter: required fixed-rate duration in years (1–5). Pass an array to match any of several durations."
}
}
}