Minimum yearly repayment, shortfall, and deemed dividend for a complying Division 7A loan under ITAA 1936. Benchmark interest rate is read from a period-scoped rate table and cited in every response.
POST /v1/calculators/div7a/at/urn:sbrm:period:div7a:fy2025
POST /v1/calculators/div7a/at/urn:sbrm:period:div7a:fy2026
Unlike depreciation, this calculator is period-scoped — the benchmark interest rate changes each income year, so the period URN selects the rate table. Supplying a year that has no registered rate table returns 404 naming the ones that do.
A $70,000 amalgamated base, seven-year unsecured term, first MYR year, no repayments made:
curl -s -X POST \
"$BASE/v1/calculators/div7a/at/urn:sbrm:period:div7a:fy2025" \
-H "Content-Type: application/json" \
-d '{
"amalgamated_base": 70000,
"loan_term_years": 7,
"loan_origination_date": "2024-06-01",
"income_year_start_date": "2024-07-01",
"is_first_real_myr_year": true,
"repayments": []
}'
{
"period_uri": "urn:sbrm:period:div7a:fy2025",
"amalgamated_base": "70000.0",
"benchmark_rate": "0.0877",
"remaining_term_years": 6,
"statutory_myr": "15497.53",
"total_repayments": "0.00",
"shortfall": "15497.53",
"is_complying": false,
"deemed_dividend": "15497.53",
"interest_accrued": "6122.18",
"manifest": {
"rate_table_uris": [{
"uri": "urn:sbrm:rate:div7a:fy2025:benchmark-interest",
"content_hash": "eb203be2431484215fd5b8ce310fa1645a5638e7dcda2491712faeea6fff4045",
"hash_algorithm": "sha256"
}]
},
"advisory": { ... }
}
A loan balance of zero or less is not a loan, and returning a compliance verdict for one would be worse than returning nothing. Both layers refuse it independently — the gateway on the schema, the engine in its own right:
// at the gateway
422 {"detail": [{"type": "greater_than", "loc": ["body", "amalgamated_base"],
"msg": "Input should be greater than 0", "input": -1000, "ctx": {"gt": 0.0}}]}
// at the engine, for anything that reaches it directly
400 {"detail": {"refusal_class": "non_positive_amalgamated_base", ...}}
200 with
is_complying: true and a negative minimum repayment for a negative balance. If you
tested it before that date, retest.
manifest.rate_table_uris is non-empty here and carries a content hash for the benchmark rate — you can pin against it and detect a rate-table change.:unscoped segment; it will 404./v1/calculators/urn:sbrm:calculator:div7a:at/{period_uri} reaches the same handler.