lodgeit labs

Deterministic tax and accounting calculators, over HTTP

A small fleet of Australian tax and accounting calculators with a single public front door. No key, no signup, no client library. Built so an agent can call them without a human in the loop.

Each calculator is a deterministic engine — Prolog or Python, no model in the path — behind a gateway that validates your request, refuses the ones it cannot answer, and tells you which statutory rate tables it consumed to produce the number.

What's live

CalculatorStatusWhat it does
Depreciation available Single-asset carrying amount under AASB 116. Prime cost or diminishing value, at a date or over an arbitrary range.
Division 7A available Minimum yearly repayment, shortfall and deemed dividend under ITAA 1936 Div 7A.
Fringe Benefits Tax not yet Twenty-one benefit categories. Reachable, but with a known defect that makes cross-category totals wrong — see below.
FBT is not ready and we would rather say so. The engine returns correct taxable values, but only three of nineteen benefit calculators currently emit the grossed-up figure and the FBT payable. The rest return null for those fields. Anything summing fbt_payable across benefit types will silently undercount. We are fixing it at the dispatch boundary rather than one calculator at a time. Until then, treat FBT as unavailable.

Working with an agent?

Give it this and stop reading:

https://lodgeit.org/llms.txt

That file is the entire API surface in plain text — every endpoint, every field, the day-count semantics, the error contract, the known limitations, and how the numbers are represented. An agent can read it once and call these calculators correctly without parsing a single HTML page.

It is written to be read by a machine and understood by a person. If you are evaluating whether this is worth your time, that file will tell you faster than these pages will.

Base URL

https://fbt-calculator-api-qkp3j5bjnq-ts.a.run.app

The service name is historical — it fronts every calculator, not just FBT.

This host will change. A stable custom domain is coming; until then you are calling the underlying Cloud Run URL directly. Put the base URL in configuration rather than in your code, and re-check this page before assuming it still resolves.

Thirty seconds

Written-down value of a $10,000 asset with a five-year life, one month after purchase:

curl -s -X POST \
  "https://fbt-calculator-api-qkp3j5bjnq-ts.a.run.app/v1/calculators/depreciation/at/urn:sbrm:period:depreciation:unscoped" \
  -H "Content-Type: application/json" \
  -d '{
    "basis": "accounting",
    "asset": {
      "cost": 10000,
      "acquisition_date": "2023-07-01",
      "accounting_useful_life_years": 5,
      "accounting_method": "prime_cost"
    },
    "at_date": "2023-07-31"
  }'
{
  "basis": "accounting",
  "at_date": "2023-07-31",
  "wdv_at": "9830.60",
  "period_dep_at": "169.40",
  "day_count": "actual/actual",
  "schedule_summary": { "opening_balance": "10000.00", "closing_balance_at": "9830.60", ... },
  "manifest": { "rate_table_uris": [] },
  "advisory": { ... }
}

Discovery

The gateway describes itself. Point an agent at these before hardcoding anything:

GET /v1/calculators
Every calculator, its URN, its input schema reference, and the period URNs it accepts.
GET /openapi.json
Full OpenAPI 3 document for every route.
GET /v1/rates/{period_uri}
The statutory rate tables registered for a period, each with a content hash.
GET /healthz
Liveness.

How the responses are shaped

Every successful response carries two blocks beyond the calculation itself.

manifest.rate_table_uris lists the statutory rate tables the calculation actually consumed, each with a SHA-256 of its contents. An empty list means the figure was derived arithmetically from your inputs alone. This is emitted by the computation, not attached by hand — if the list changes, the arithmetic changed.

advisory states what kind of figure you have been given and what it is not. It varies by calculator: an AASB 116 carrying amount carries a warning against using it in a tax return; a Division 7A figure carries the tax-agent framing. Read it — the distinction is the point.

Errors

Status codes mean what they say. We treat this as a contract:

CodeMeaning
422Your request is malformed or incomplete. The body names the field. Do not retry unchanged.
400Your request is well-formed but outside what this calculator will answer. The body carries a refusal_class.
404Unknown calculator URN or a period URN this calculator does not accept. The body names what it does accept.
5xxOur fault. Retrying is reasonable.

A 4xx from the engine behind the gateway is never reported to you as a 5xx. If you get a 5xx, something is genuinely wrong on our side.

Refusals are a feature

These calculators refuse rather than guess. A Division 7A loan with a zero or negative balance is not a loan, so it returns 400 instead of a compliance verdict. A pooled depreciating asset is out of scope, so it returns 400 with pool_asset_out_of_t6_scope instead of a number computed under the wrong rules.

If you are used to APIs that return something for everything, this will occasionally be inconvenient. It is deliberate. A refusal you can see beats a plausible number you cannot check.

What this is not

Rate limits, cost, keys

None, none, and none. The services are capped at three instances each, so sustained heavy load will queue. If you need more than that, or you are building something that would break when we change a field name, get in touch first.

Telling us it's wrong

The most useful thing you can send us is a request and the response it produced, with the number you expected and where that expectation comes from. Two independent implementations disagreeing is how the last four defects in these calculators were found — including one where our engine was right and our own reasoning was wrong.

github.com/lodgeit-labs