Skip to main content

Quickstart

Make your first serviceability call in three steps: exchange your credentials for an access token, POST a scenario to the serviceability endpoint, then read the indicative result. This is the first call of the pre-submission stage of the loan lifecycle (pre-submission, submission, post-submission). Every request and response on this page is copy-pasteable; substitute your own credentials where marked.

Responses are indicative only and never constitute a credit approval.

Before you start

You need a Client ID and secret scoped to the PreSubmissionPartner Frontegg machine-to-machine credential. These are separate from the human login you use to read this portal. Contact your Slate integration manager if you do not have them yet. They also provide your Frontegg tenant host (see Environments); substitute it for <your-tenant> below.

The examples use curl and jq (to pull fields out of JSON responses). Any HTTP client works.

Step 1: Exchange credentials for an access token

Slate uses the Frontegg OAuth 2.0 client-credentials flow. Exchange your Client ID and secret for a short-lived access token:

curl -sS -X POST "https://<your-tenant>.frontegg.com/identity/resources/auth/v2/api-token" \
-H "Content-Type: application/json" \
-d '{
"clientId": "YOUR_CLIENT_ID",
"secret": "YOUR_CLIENT_SECRET"
}'

The token is returned in the accessToken field:

{
"accessToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6...",
"tokenType": "Bearer",
"expiresIn": 3600
}

Capture it for the next step:

ACCESS_TOKEN=$(curl -sS -X POST "https://<your-tenant>.frontegg.com/identity/resources/auth/v2/api-token" \
-H "Content-Type: application/json" \
-d '{"clientId":"YOUR_CLIENT_ID","secret":"YOUR_CLIENT_SECRET"}' \
| jq -r '.accessToken')

Tokens expire after one hour (expiresIn, in seconds). Refresh before expiry rather than waiting for a 401.

Step 2: Check serviceability

POST a scenario to the serviceability endpoint with the token as a Bearer credential. You do not pass a product code: the loan.purpose plus your allowlist select the product. Send a single proposed loan plus the security's estimatedValue; Slate may service the loan as an internal split (primary plus deposit boost), but the response presents one unified loan with the split as accessory detail.

curl -sS -X POST "https://api.slateos.ai/api/v1/pre-submission/serviceability" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"brokerDetails": {
"brokerName": "Jane Smith",
"brokerId": "BRK-100234",
"aclNumber": "389328",
"acrNumber": "461220",
"brokerEmail": "jane.smith@brokerage.com.au"
},
"partnerScenarioReference": "FIN-SCENARIO-9281",
"scenario": {
"applicants": 2,
"dependants": 2,
"postcode": "3000",
"incomes": [
{ "type": "PAYG", "annualAmount": 12000000, "frequency": "ANNUAL" }
],
"expenses": 320000,
"liabilities": [
{ "type": "CREDIT_CARD", "balance": 850000, "limit": 1500000, "monthlyRepayment": 25000 }
],
"loan": { "amount": 60800000, "termYears": 30, "purpose": "OwnerOccupied" },
"security": { "estimatedValue": 64000000 }
}
}'

Monetary amounts are integer cents (60800000 is $608,000). The security.estimatedValue is the property value Slate divides the loan amount by to derive the LVR (60800000 / 64000000 is 95%). See Serviceability Request Fields for a field-by-field breakdown, or LIXI CAL to send a LIXI package instead of JSON.

How routing works

Slate selects the product from loan.purpose and the products on your allowlist, and returns it as selectedProductCode. If the scenario does not resolve to an available product, the call returns scenario_matches_no_product. See Products for how the routed product is named (Boost Owner Occupier P&I [LVR band]), how the per-1%-LVR-increment rate model works, and where the fees come from. Omit scenario.expenses to assess against the HEM benchmark (Household Expenditure Measure); supply a figure and Slate assesses on the higher of declared and HEM.

Step 3: Read the response

A serviceable call returns 200 with status: "Completed" and a populated result block:

{
"preSubmissionRequestUid": "psr_01ARZ3NDEKTSV4RRFFQ69G5FAV",
"status": "Completed",
"partnerScenarioReference": "FIN-SCENARIO-9281",
"selectedProductCode": "OHDBOO",
"indicativeOutcome": "APPROVED",
"product": {
"code": "OHDBOO",
"displayName": "Boost Owner Occupier P&I 82-83%",
"type": "OwnerOccupied",
"blendedRatePercent": 6.74,
"comparisonRatePercent": 8.25,
"displayFees": [
{ "name": "Application Fee", "amount": 39900, "frequency": "OneOff" },
{ "name": "Ongoing Fee", "amount": 39500, "frequency": "Annual" },
{ "name": "Lender Legals", "amount": 27500, "frequency": "OneOff" },
{ "name": "Low Deposit Premium", "amount": 1450000, "frequency": "OneOff" }
]
},
"result": {
"netMonthlySurplus": 247453,
"maxBorrowingCapacity": 19600000,
"maxCapacityLvrPercent": 90.0,
"dti": 3.8,
"dtiOutcome": "PASS",
"nmsOutcome": "PASS",
"assessedMonthlyExpenses": 308763,
"existingLiabilities": {
"assessedMonthly": 38000,
"breakdown": [
{
"type": "Credit Card",
"method": "PERCENT_OF_LIMIT",
"assessedRepayment": 38000,
"outstandingBalance": 850000
}
]
},
"proposedLoan": {
"amount": 60800000,
"lvrPercent": 95.0,
"blendedRatePercent": 6.74,
"assessedRepaymentsMonthly": 514300,
"actualRepaymentsMonthly": 403600,
"repaymentType": "PRINCIPAL_AND_INTEREST",
"repaymentPhases": [
{ "fromYear": 1, "toYear": 15, "actualMonthly": 403600, "assessedMonthly": 514300 },
{ "fromYear": 15, "toYear": 30, "actualMonthly": 300700, "assessedMonthly": 399100 }
],
"splits": [
{
"loanType": "PRIMARY_MORTGAGE",
"amount": 40000000,
"termYears": 30,
"productRatePercent": 6.14,
"assessmentRatePercent": 8.65,
"monthlyRepayment": 311800,
"monthlyRepaymentActual": 243400,
"repaymentType": "PRINCIPAL_AND_INTEREST"
},
{
"loanType": "PRIMARY_MORTGAGE",
"amount": 11200000,
"termYears": 30,
"productRatePercent": 6.14,
"assessmentRatePercent": 8.65,
"monthlyRepayment": 87300,
"monthlyRepaymentActual": 57300,
"repaymentType": "INTEREST_ONLY"
},
{
"loanType": "DEPOSIT_BOOST",
"amount": 9600000,
"termYears": 15,
"productRatePercent": 9.95,
"assessmentRatePercent": 12.0,
"monthlyRepayment": 115200,
"monthlyRepaymentActual": 102900,
"repaymentType": "PRINCIPAL_AND_INTEREST"
}
]
}
}
}

The figures above are illustrative example values from the contract, not the computed result of the request in Step 2. Reading the response:

  • indicativeOutcome is Slate's domain outcome: APPROVED, ESCALATE, or DECLINE. Indicative only, never an approval.
  • selectedProductCode is the code of the product Slate routed the scenario to.
  • product is the full detail of that product, resolved for this scenario. Because the blended rate is dynamic (amount-weighted across the splits for the actual LVR), read product.blendedRatePercent and product.displayFees here rather than from the static products catalog. The Low Deposit Premium fee maps to your LMI field.
  • Money fields are integer cents, the same scale as the request (netMonthlySurplus of 247453 is $2,474.53). Rate and LVR fields are 0-100 percentages (blendedRatePercent of 6.74 means 6.74%, lvrPercent of 95.0 means 95%).
  • preSubmissionRequestUid (the psr_ identifier) is the authoritative audit key, persisted on every call including rejections. Pass it to Required Documents to get the evidence list resolved against this scenario, or re-fetch this result later with GET /api/v1/pre-submission/serviceability/{preSubmissionRequestUid}. The optional partnerScenarioReference is echoed back but is not unique.
  • result.proposedLoan is one unified loan: amount, lvrPercent, blendedRatePercent (the amount-weighted product rate), and the assessed and actual monthly repayment totals. Read these as the headline figures. lvrPercent is present only when you sent security.estimatedValue.
  • result.proposedLoan.repaymentPhases shows the step-down across the life of the loan. The headline actualRepaymentsMonthly and assessedRepaymentsMonthly are the initial figures (year 1 to 15); the figure drops around year 15 as the deposit-boost split is repaid. For the full month-by-month schedule, call GET /api/v1/pre-submission/serviceability/{preSubmissionRequestUid}/repayment-schedule.
  • result.proposedLoan.splits is accessory detail: where Slate services the request as more than one component, this carries a line per split, each with its own amount, termYears, rates, repayments, and repaymentType. Splits may run different terms, and the primary mortgage itself can be structured as more than one facility; here it is a principal-and-interest account plus an interest-only account, alongside the deposit boost. The unified figures above are what the borrower sees; the splits are the internal structure. existingLiabilities.breakdown carries one line per declared liability, with the assessed figures Slate used.

When a call is rejected

A rejected call does not return a 200 with status: "Rejected". Rejections come back as an HTTP 4xx with a structured error envelope, while an audit record is still written server-side against a preSubmissionRequestUid. For example, omitting brokerEmail returns 400:

{
"error": {
"type": "bad_request",
"code": "missing_required_field",
"title": "Missing Required Field",
"message": "Required field 'brokerEmail' is missing from the request.",
"resolution": "Include the 'brokerEmail' field in your request.",
"documentation_url": "https://docs.slateos.ai/errors#missing_required_field",
"request_uid": "req_01ARZ3NDEKTSV4RRFFQ69G5FAV",
"timestamp": "2026-06-24T03:30:00Z",
"details": { "field": "brokerEmail" }
}
}

Branch on error.code, not on the HTTP status or the human-readable message. See the Error Reference for the full list of codes.

Next steps

With a Completed result, fetch the Required Documents for the preSubmissionRequestUid, then Submit the application when the borrower proceeds. To understand the routed product, see Products for the naming scheme, the per-1%-LVR-increment rate model, and the fee sources.