AI JSON Contract -> Backend -> Frontend¶
This document defines the official JSON format accepted by the system to interpret natural language instructions and convert them into financial actions.
This schema is the integration contract between backend and frontend. Any change must update this document and be versioned.
1) Accepted JSON schema¶
{
"action": "add_income | add_expense | report | data_analysis | none",
"amount": 1000.0,
"description": "Salary",
"category": "salary",
"account": "General",
"base_currency": "USD",
"exchange_rate": 1.0,
"converted_amount": 1000.0,
"report_type": "expenses | incomes | balance | cashflow | summary | null",
"period": {
"preset": "this_month | last_month | last_3_months | this_year | custom",
"from": "2025-01-01",
"to": "2025-03-31"
},
"filters": {
"categories": ["restaurant"],
"accounts": ["General"],
"min_amount": null,
"max_amount": null,
"text": null
},
"message": "Income recorded: 1000.00 USD (base USD) in General"
}
2) Field definitions¶
action(string, required)-
Allowed values:
add_income: record incomeadd_expense: record expensereport: request a reportdata_analysis: request user data analysisnone: no executable action
-
amount(number, optional depending on action) - Original amount detected in the prompt.
- Required for
add_incomeandadd_expense. -
Since
0.0.1a2, the backend normalizes monetary numbers as exact two-decimal money values before persistence. -
description(string, optional) -
Free operation description, such as
SalaryorCoffee. -
category(string, optional) - Semantic category such as
salary,food, ortransport.
Special rule for savings:
- If the AI or deterministic parser interprets a savings phrase, the action still remains
add_expense. - In that case
categorycan resolve to a technical savings category such assavingsor its internal canonical equivalent. -
Categories internally marked as savings update goals but are excluded from real expense in reports, analysis, and budgets.
-
account(string, optional) -
Destination or source account such as
General,Savings, orCash. -
base_currency(string, required for monetary flows) -
System base currency used to consolidate the ledger.
-
exchange_rate(number, required for monetary flows) - Exchange rate used to convert
amountintobase_currency. - Rule:
converted_amount = amount * exchange_rate. -
One action supports only one FX leg into
base_currency. If a real flow crosses more than two currencies, the caller must provide the final effective rate intobase_currencyor split the flow into multiple operations. -
converted_amount(number, required for monetary flows) - Amount converted into the base currency.
- It must match the conversion formula after applying the canonical rounding rule.
Fields specific to report and data_analysis¶
report_type(string, required whenaction=report)-
Allowed values:
expenses,incomes,balance,cashflow,summary. -
period(object, required whenaction=reportoraction=data_analysis) preset:this_month,last_month,last_3_months,this_year,customfrom: required whenpreset=custom-
to: required whenpreset=custom -
filters(object, optional whenaction=reportoraction=data_analysis) categories: included categoriesaccounts: included accountsmin_amount: minimum thresholdmax_amount: maximum threshold-
text: free-text search -
message(string, optional but recommended) - Friendly message for UI or logs.
3) Validation rules¶
- If
actionisadd_incomeoradd_expense,amount,base_currency,exchange_rate, andconverted_amountmust be present. - If
actionisreport,report_typeandperiodare mandatory. - If
actionisdata_analysis,periodis mandatory andreport_typemust benull. - If
actionisnone, monetary and report fields may be omitted andmessageshould explain why no action is executed. exchange_ratemust be greater than zero for monetary operations.converted_amountmust respect the conversion formula.- Monetary values (
amount,converted_amount,filters.min_amount,filters.max_amount) are normalized to two decimals usingROUND_HALF_UP. - If
period.preset = custom,period.fromandperiod.toare mandatory. - If
action = add_expenseand the category is internally marked as savings, the backend must treat the operation as a technical outflow for goals and exclude it from reportable real expense.
4) Monetary semantics¶
- JSON still carries money as JSON numbers.
- At validation time, the backend converts monetary values to exact decimal semantics with two decimals.
- SQLite persistence stores the resulting values as integer cents.
- UI, charts, and some export adapters may render decimal strings or display floats, but arithmetic and persistence do not rely on binary-floating calculations.
- Canonical rounding rule:
ROUND_HALF_UPto two decimals.
5) Valid examples¶
Example A: Income in base currency¶
{
"action": "add_income",
"amount": 1000.0,
"description": "Salary",
"category": "salary",
"account": "General",
"base_currency": "USD",
"exchange_rate": 1.0,
"converted_amount": 1000.0,
"report_type": null,
"period": null,
"filters": null,
"message": "Income recorded: 1000.00 USD (base USD) in General"
}
Example B: Expense in a different currency¶
{
"action": "add_expense",
"amount": 500.0,
"description": "Food expense",
"category": "food",
"account": "General",
"base_currency": "USD",
"exchange_rate": 0.058,
"converted_amount": 29.0,
"report_type": null,
"period": null,
"filters": null,
"message": "Expense recorded: 500.00 (converted to 29.00 USD) in General"
}
Example C: Report with type, period, and filter¶
{
"action": "report",
"amount": null,
"description": "Expense report",
"category": null,
"account": null,
"base_currency": "USD",
"exchange_rate": null,
"converted_amount": null,
"report_type": "expenses",
"period": {
"preset": "last_3_months",
"from": null,
"to": null
},
"filters": {
"categories": ["restaurant"],
"accounts": null,
"min_amount": null,
"max_amount": null,
"text": null
},
"message": "Requested expenses report for restaurants in last 3 months"
}
Example D: Data analysis¶
{
"action": "data_analysis",
"amount": null,
"description": null,
"category": "education",
"account": null,
"base_currency": "USD",
"exchange_rate": null,
"converted_amount": null,
"report_type": null,
"period": {
"preset": "last_3_months",
"from": null,
"to": null
},
"filters": {
"categories": ["education"],
"accounts": null,
"min_amount": null,
"max_amount": null,
"text": null
},
"message": null
}
6) Recommended system prompt¶
You are a financial parser. Convert the user's prompt into valid JSON.
Reply with JSON only and include these keys:
action, amount, description, category, account, base_currency, exchange_rate, converted_amount, report_type, period, filters, message.
Rules:
- action in {add_income, add_expense, report, data_analysis, none}
- For add_income/add_expense:
- amount, base_currency, exchange_rate, and converted_amount are required
- converted_amount = amount * exchange_rate rounded to 2 decimals with ROUND_HALF_UP
- report_type, period, and filters must be null
- For report:
- report_type required in {expenses, incomes, balance, cashflow, summary}
- period required with preset in {this_month,last_month,last_3_months,this_year,custom}
- if period.preset=custom, from and to are required
- filters optional
- For data_analysis:
- period required
- filters optional
- report_type must be null
- For none:
- monetary and report fields may be null
- Do not add any text outside the JSON
7) Compatibility¶
- This document replaces the previous format that did not include multi-currency fields, explicit report structure, or the
data_analysisaction. - Backend and frontend must validate the presence and consistency of
base_currency,exchange_rate,converted_amount,report_type,period, andfilters. - Since
0.0.1a2, developer-facing integrations must also respect the exact-money contract: - money fields are normalized to two-decimal exact values
converted_amountis validated against the rounded conversion formula- multi-leg currency conversions are not represented in a single action