Calculation
The calculation endpoints run the tool’s live version on the server. The two endpoints behave the same; one addresses by key, the other by toolId (key is recommended for integrations):
POST /v1/public/tools/by-key/{key}/calculatePOST /v1/public/tools/{toolId}/calculateRequest
Section titled “Request”{ "inputs": { "amount": "1000", "mode": "exclusive", "vatRateOption": "standard" }, "asOf": "2026-01-15", "version": 1}| Field | Required | Description |
|---|---|---|
inputs |
yes | Field name → value. The keys are the names in inputSchema.fields. |
asOf |
no | The date the rates are in force, YYYY-MM-DD. Defaults to today (Europe/Istanbul). |
version |
no | The versionNumber you read from the definition. 409 if it differs from the live version. |
The request body can be at most 16 KB; unknown top-level fields are rejected.
Value rules
Section titled “Value rules”- Decimals and dates are strings, enum values are strings, booleans are JSON
true/false. - A JSON number (
1000) is rejected:422 VALIDATION_ERROR, with the relevant path indetails.issues[]. - If
required: falsefields are not sent,defaultis used. - A field that is not in the definition →
details.fields[].reason = "UNKNOWN_INPUT". - Range and scale checks:
OUT_OF_RANGE,SCALE_EXCEEDED; a missing required field isMISSING_INPUT, anullvalue isNULL_INPUT.
Response
Section titled “Response”{ "data": { "result": { "net": "1000.00", "vat": "200.00", "gross": "1200.00" }, "breakdown": [ { "key": "net", "value": "1000.00", "format": "money", "scale": 2, "currency": "TRY" }, { "key": "vat", "value": "200.00", "format": "money", "scale": 2, "currency": "TRY" }, { "key": "gross", "value": "1200.00", "format": "money", "scale": 2, "currency": "TRY" } ] }, "meta": { "toolId": "8410ffe8-825a-41a1-9488-078f3a7c7f42", "toolVersion": 1, "asOf": "2026-10-11", "engineVersion": "1.2.0", "dslVersion": 1, "rates": [ { "binding": "vatRate", "key": "tr.vat.standard", "rateId": "31fb806c-…", "valueId": "65a8771b-…", "version": 1, "value": "20", "effectiveFrom": "2023-07-10", "effectiveTo": null } ] }}result: result key → string value. Read it directly by key in your code (data.result.gross).breakdown: an ordered list of the same results with format information (format,scale,currency,unit). Use this if you are drawing a results table; the order is the one the tool defines.
Formatting
Section titled “Formatting”format |
Meaning | Display suggestion |
|---|---|---|
money |
Money (currency) |
Intl.NumberFormat('tr-TR', { style: 'currency', currency }) |
percent |
Percentage value ("20" = 20%) |
20 % |
number |
Number | As many decimals as scale |
date |
Date YYYY-MM-DD |
Local date format |
To format the string value without converting it to floating point, use a decimal library (decimal.js, Python Decimal, .NET decimal); or display the string as it is.
toolVersion,asOf: the tool version and the rate date used in the calculation.rates[]: every rate used:key,value,version,effectiveFrom/To. Store this field to document what the calculation is based on.engineVersion,dslVersion: calculation engine versions (informational).
Historical calculation
Section titled “Historical calculation”With asOf, calculate with the rates in force on that date (e.g. a past VAT rate):
curl -X POST "https://api.mirac-ozdemir.com/v1/public/tools/by-key/kdv/calculate" \ -H "Content-Type: application/json" \ -d '{"asOf":"2023-01-15","inputs":{"amount":"1000","mode":"exclusive","vatRateOption":"standard"}}'If no rate was in force on that date, 422 RATE_UNAVAILABLE is returned (details.rates, details.asOf). For example, a very old date for which a rate is not defined gives this error.
Verify freshness with version
Section titled “Verify freshness with version”If you cached the definition and generated your form from it, send version in the calculation. If the tool has been updated in the meantime, the calculation is not performed:
{ "error": { "code": "CONFLICT", "message": "…", "details": { "reason": "VERSION_OUTDATED", "currentVersion": 2 } }, "requestId": "…" }In this case, fetch the definition again (getToolByKey), update your form and repeat the request. If you always want fresh data, you can omit version and calculate with the live version every time.
Errors and limits
Section titled “Errors and limits”- Calculation endpoints are limited to 60 requests per minute per IP; the limit of the two endpoints is shared.
- Calculation responses come with
no-store; they must not be cached. - A tool under maintenance returns
503 MAINTENANCE.
All error codes and handling suggestions: Errors.