Real-Time Calculation
Taxbit's inventory engine is built for real-time, per-user tax calculations at retail and institutional scale. These APIs form the core integration surface for Austria Tax Withholding.
| API | Endpoint | Method | Purpose |
|---|---|---|---|
| Transactions | /transactions/{external_id} | POST | Submit a new transaction event. Triggers inventory, gain, and withholding calculation. |
| Inventory | /inventory | GET | Retrieve undisposed tax lots for a specific asset and account. Includes cost basis, acquisition date, pool type. |
| Inventory | /inventory/summaries | GET | Per-asset summary of total quantity and total cost basis across all undisposed lots. |
| Gains | /gains | GET | Detailed gain/loss per disposal event, including the Austria-specific gain_type. |
| Gains | /gains/summary | GET | Running total of gains and losses for a given account and period. |
| Withholding | /v1/inventory/withholding/austria | GET | Dedicated Austria withholding read endpoint. Returns the YTD withholding target and, when polled with a transaction_id, that transaction's contribution. |
| User Cost Basis | /v1/transfer-lots/transactions/{transactionId} | POST | Submit user-entered acquisition data for assets transferred onto the platform without a known cost basis. |
Transactions API
The Transactions API is the entry point for all activity data. Every acquisition, disposal, income event, and transfer must be submitted here. Taxbit uses transaction data to update inventory lots and trigger gain/loss and withholding calculation in near real-time.
- Key endpoint:
POST /transactions/{external_id}. Each transaction is identified by a customer-suppliedexternal_idto ensure idempotency. - The payload includes asset type, quantity, timestamp, transaction type, FMV (in EUR for Austria), and relevant account identifiers.
- Transactions are processed asynchronously — a
200response confirms successful ingestion; inventory, gains, and withholding are calculated in the background.
Inventory API
The Inventory API provides a real-time view of a user's undisposed asset holdings organised into cost basis lots. Each lot carries metadata including acquisition date, quantity, cost basis, and pool classification.
GET /inventory— Returns all undisposed lots for a given asset and account. Supports unrealised gain/loss display.GET /inventory/summaries— Returns a per-asset summary of total quantity and total cost basis for an account.
Austria requires the moving average cost method at the wallet level for all New Stock. Each new acquisition updates the average cost of the entire holdings pool for that asset. When a disposal occurs, the average cost at the moment of disposal determines the cost basis of the disposed units.
Inventory is organised into four distinct pools that are disposed of in the following priority order. This order is enforced server-side by Taxbit's inventory engine and is not currently configurable per account:
| Priority | Pool | Disposition Method | Loss Compensation |
|---|---|---|---|
| 1 (first) | Old Stock (acquired on or before 2/28/2021) | FIFO | N/A — tax-free (all Old Stock has exceeded the 1-year holding period) |
| 2 | New Stock – Missing Cost Basis ("at-missing-basis", 50% rule) | 50% of proceeds assumed as cost | Not allowed |
| 3 | New Stock – User-Entered Cost Basis ("at-user-basis") | Moving Average Cost | Not allowed |
| 4 (last) | New Stock – Known Cost Basis ("at-new-stock") | Moving Average Cost | Allowed |
A disposal that exhausts one pool continues into the next pool in priority order, and a single disposal can therefore draw from several pools at once. Because each pool carries its own loss-compensation treatment, the pool a disposal draws from determines how it contributes to the withholding calculation — see Withholding API below.
Gains API
The Gains API surfaces calculated gain and loss data at multiple levels of granularity.
GET /gains— Full cost basis, proceeds, and gain/loss per disposal event.GET /gains/summary— Total gains/losses for a specified period.
For Austria, GET /gains returns one of five pool-specific gain_type values on each row:
gain_type | Pool | Row shape |
|---|---|---|
at-new-stock | Known Cost Basis (New Stock) | One row per disposition — the pool's average cost collapses multiple lots into a single weighted-average row. |
at-user-basis | User-Basis Stock | One row per disposition, same collapsing behavior as at-new-stock. |
at-missing-basis | Missing Basis (50% rule) | One row per disposition; cost = 50% × proceeds. |
at-old-stock-long-term | Old Stock, held > 1 year | One row per FIFO tranche consumed; concrete acquisition_transaction_id and cost_basis_date. |
at-old-stock-short-term | Old Stock, held ≤ 1 year | Same as above; distinguished for record-keeping even though both are excluded from withholding. |
A single disposition can emit multiple gain rows if it spans multiple pools (e.g. a large sale that exhausts Old Stock and dips into the Known Cost Basis pool). Old Stock rows are still returned — and still show a real gain or loss — for record-keeping purposes even though Old Stock contributes zero to the withholding calculation.
Withholding API
The Withholding API is the authoritative source for KESt withholding amounts. It is a dedicated, Austria-specific read endpoint — the Gains API returns gain and loss data only and does not return a withholding amount.
Key endpoints:
GET /v1/inventory/withholding/austria?account_id={account_id}&transaction_id={transaction_id}
GET /v1/inventory/withholding/austria?account_id={account_id}&tax_year={YYYY}
Request
| Parameter | Required? | Meaning |
|---|---|---|
account_id | Yes | Account ID. Withholding and loss compensation are scoped to a single account. Returns 409 Conflict if the account is not configured with disposition_method AUSTRIA. |
transaction_id | No | Client-supplied external transaction ID. When supplied, the transaction block in the response is point-in-time as of that transaction and is null until that transaction finishes processing — this is the polling target. |
tax_year | No | Defaults to the current calendar year (Europe/Vienna midnight Jan 1 cutover). Specify this parameter to read a prior year's rollup. |
Response
{
"summary": {
"year_to_date_balance": "1247.835",
"latest_transaction_datetime": "2026-04-12T14:32:18Z"
},
"transaction": {
"change_in_balance": "42.675",
"transaction_datetime": "2026-04-12T14:32:18Z",
"transaction_id": "client-tx-9182"
},
"tax_year": 2026,
"fiat_asset": {
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"name": "Euro",
"code": "EUR",
"type": "Fiat"
},
"calculation_status": "complete"
}| Field | Meaning |
|---|---|
summary.year_to_date_balance | The balance the CASP should currently be holding for this account YTD, in EUR, with the 27.5% KESt rate already applied. Authoritative for withheld-balance reconciliation. |
summary.latest_transaction_datetime | Timestamp of the latest processed transaction reflected in the summary. |
transaction | null until transaction_id is supplied and that transaction has finished processing. Once non-null, it will always be present in that response — even for transactions that don't generate withholding (change_in_balance will simply be "0"). |
transaction.change_in_balance | This transaction's signed contribution to summary.year_to_date_balance. Can be negative under loss compensation, when this transaction's loss reduces the running net (see Important Considerations). |
tax_year | The Austrian calendar year (Europe/Vienna) this response covers. |
fiat_asset | Always EUR for Austria; returned explicitly so callers don't have to hardcode it. |
calculation_status | Optional. Present when set; indicates whether background calculation for the account is complete. |
Amounts are returned in full precision as decimal strings (never scientific notation). The CASP applies its own rounding (typically to EUR cents) when reconciling against its actual withheld balance.
Regime formulas
Taxbit resolves each account to one of two formulas: a compensated formula for individual accounts (losses net against gains, clamped at zero), and a non-compensated, gross-only formula for joint, business, and trust accounts. Both amounts below are returned with the 27.5% rate already applied.
| Quantity | Formula |
|---|---|
Compensated — summary.year_to_date_balance | (max(0, ytd_compensable) + ytd_non_compensable) × 0.275 |
Compensated — transaction.change_in_balance | (max(0, ytd_compensable_after) − max(0, ytd_compensable_before) + Δnon_compensable) × 0.275 |
Non-compensated — summary.year_to_date_balance | ytd_gross × 0.275 |
Non-compensated — transaction.change_in_balance | Δgross × 0.275 |
Where each disposition's contribution to compensable / non_compensable / gross is determined by pool:
| Pool | compensable | non_compensable | gross |
|---|---|---|---|
Known Cost Basis (at-new-stock) | gain (can be negative) | 0 | max(0, gain) |
User-Basis Stock (at-user-basis) | 0 | max(0, gain) | max(0, gain) |
Missing Basis (at-missing-basis) | 0 | amount (always ≥ 0 by construction) | amount |
Old Stock (at-old-stock) | 0 | 0 | 0 |
Performance Characteristics
Inventory, gain/loss, and withholding calculations are triggered automatically whenever a transaction is processed. The pipeline is asynchronous — Taxbit's P95 SLA for end-to-end inventory calculation is under 1 second for standard append transactions. This latency window must be factored into the customer's execution flow when determining the timing of fund diversion (see Important Considerations).
Updated about 1 hour ago

