Files
fin-tracker/openapi/openapi.json
T
Dmitry 3727419506 feat(api): аналитика инвестиций, события и карточка инструмента
/analytics/{scopes,summary,value-series,holdings,returns,allocation}, /events с
фильтрами и /instruments/{id}. На запрос ничего не считается — это чтение metric_*,
благодаря чему экраны читаются быстро и показывают одно и то же число.

scope (all | account:<id> | portfolio:<id>) резолвится через ту же функцию, что его
построила, valuation.account_scopes: scope, для которого метрик нет, отдаёт 404, а не
пустой график, который читался бы как пустой портфель.

asset_class уходит наружу строкой, а не енумом. Одно из его значений — index, а
Dart-енум не может назвать член index: он конфликтует с Enum.index, и сгенерированный
клиент перестаёт компилироваться. flutter analyze это пропускает, flutter test ловит.

Фильтр /events?external_flow=false сравнивает meta через is_not_distinct_from, а не
через равенство: у события без meta сравнение даёт NULL, NOT NULL это тоже NULL, и
равенство выбрасывало бы такие события из ОБЕИХ половин фильтра.
2026-09-18 14:20:50 +03:00

4917 lines
118 KiB
JSON

{
"components": {
"schemas": {
"AccountBalance": {
"properties": {
"account_id": {
"title": "Account Id",
"type": "integer"
},
"balance": {
"description": "decimal as string",
"title": "Balance",
"type": "string"
},
"balance_rub": {
"anyOf": [
{
"description": "decimal as string",
"type": "string"
},
{
"type": "null"
}
],
"title": "Balance Rub"
},
"currency": {
"title": "Currency",
"type": "string"
},
"name": {
"title": "Name",
"type": "string"
},
"role": {
"$ref": "#/components/schemas/AccountRole"
}
},
"required": [
"account_id",
"name",
"currency",
"role",
"balance",
"balance_rub"
],
"title": "AccountBalance",
"type": "object"
},
"AccountKind": {
"enum": [
"zm_cash",
"zm_card",
"zm_checking",
"zm_deposit",
"zm_loan",
"zm_emoney",
"zm_debt",
"broker",
"manual_asset"
],
"title": "AccountKind",
"type": "string"
},
"AccountOut": {
"properties": {
"archived": {
"title": "Archived",
"type": "boolean"
},
"balance": {
"anyOf": [
{
"description": "decimal as string",
"type": "string"
},
{
"type": "null"
}
],
"title": "Balance"
},
"balance_as_of": {
"anyOf": [
{
"format": "date-time",
"type": "string"
},
{
"type": "null"
}
],
"title": "Balance As Of"
},
"broker": {
"anyOf": [
{
"$ref": "#/components/schemas/Broker"
},
{
"type": "null"
}
]
},
"credit_limit": {
"anyOf": [
{
"description": "decimal as string",
"type": "string"
},
{
"type": "null"
}
],
"title": "Credit Limit"
},
"currency": {
"title": "Currency",
"type": "string"
},
"id": {
"title": "Id",
"type": "integer"
},
"include_in_net_worth": {
"title": "Include In Net Worth",
"type": "boolean"
},
"kind": {
"$ref": "#/components/schemas/AccountKind"
},
"mirror_of_account_id": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Mirror Of Account Id"
},
"name": {
"title": "Name",
"type": "string"
},
"opened_at": {
"anyOf": [
{
"format": "date",
"type": "string"
},
{
"type": "null"
}
],
"title": "Opened At"
},
"primary_event_source": {
"anyOf": [
{
"$ref": "#/components/schemas/EventSource"
},
{
"type": "null"
}
]
},
"role": {
"$ref": "#/components/schemas/AccountRole"
},
"source": {
"title": "Source",
"type": "string"
},
"source_id": {
"title": "Source Id",
"type": "string"
},
"start_balance": {
"anyOf": [
{
"description": "decimal as string",
"type": "string"
},
{
"type": "null"
}
],
"title": "Start Balance"
}
},
"required": [
"id",
"kind",
"source",
"source_id",
"broker",
"name",
"currency",
"role",
"include_in_net_worth",
"mirror_of_account_id",
"primary_event_source",
"archived",
"opened_at",
"balance",
"balance_as_of",
"start_balance",
"credit_limit"
],
"title": "AccountOut",
"type": "object"
},
"AccountPatch": {
"additionalProperties": false,
"description": "Only the fields the user owns: everything else comes from the source on each sync.\n\nUnset fields are left alone; an explicit `null` clears the column.",
"properties": {
"include_in_net_worth": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
],
"title": "Include In Net Worth"
},
"mirror_of_account_id": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Mirror Of Account Id"
},
"name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Name"
},
"primary_event_source": {
"anyOf": [
{
"$ref": "#/components/schemas/EventSource"
},
{
"type": "null"
}
]
},
"role": {
"anyOf": [
{
"$ref": "#/components/schemas/AccountRole"
},
{
"type": "null"
}
]
}
},
"title": "AccountPatch",
"type": "object"
},
"AccountRole": {
"enum": [
"liquid",
"savings",
"investment",
"debt"
],
"title": "AccountRole",
"type": "string"
},
"AllocationBucket": {
"properties": {
"bucket": {
"title": "Bucket",
"type": "string"
},
"dimension": {
"$ref": "#/components/schemas/AllocationDimension"
},
"holding_count": {
"title": "Holding Count",
"type": "integer"
},
"value_rub": {
"description": "decimal as string",
"title": "Value Rub",
"type": "string"
},
"weight": {
"description": "decimal as string",
"title": "Weight",
"type": "string"
}
},
"required": [
"dimension",
"bucket",
"value_rub",
"weight",
"holding_count"
],
"title": "AllocationBucket",
"type": "object"
},
"AllocationDimension": {
"description": "How a portfolio can be sliced. Every dimension covers the SAME total — securities\nplus cash — so the weights of any one of them add up to 1 and the charts agree.",
"enum": [
"asset_class",
"sector",
"country",
"currency"
],
"title": "AllocationDimension",
"type": "string"
},
"Broker": {
"enum": [
"tinvest",
"sber",
"vtb",
"other"
],
"title": "Broker",
"type": "string"
},
"CashFlowMonth": {
"properties": {
"baseline_rub": {
"description": "decimal as string",
"title": "Baseline Rub",
"type": "string"
},
"expense_rub": {
"description": "decimal as string",
"title": "Expense Rub",
"type": "string"
},
"income_rub": {
"description": "decimal as string",
"title": "Income Rub",
"type": "string"
},
"month": {
"format": "date",
"title": "Month",
"type": "string"
},
"one_off_rub": {
"description": "decimal as string",
"title": "One Off Rub",
"type": "string"
},
"savings_rate": {
"anyOf": [
{
"description": "decimal as string",
"type": "string"
},
{
"type": "null"
}
],
"title": "Savings Rate"
},
"savings_transfer_rub": {
"description": "decimal as string",
"title": "Savings Transfer Rub",
"type": "string"
},
"txn_count": {
"title": "Txn Count",
"type": "integer"
}
},
"required": [
"month",
"income_rub",
"expense_rub",
"baseline_rub",
"one_off_rub",
"savings_transfer_rub",
"savings_rate",
"txn_count"
],
"title": "CashFlowMonth",
"type": "object"
},
"CategoryOut": {
"description": "Flat list; the client builds the tree from `parent_id` (ZenMoney nests one level).",
"properties": {
"archived": {
"title": "Archived",
"type": "boolean"
},
"color": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Color"
},
"icon": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Icon"
},
"id": {
"title": "Id",
"type": "integer"
},
"name": {
"title": "Name",
"type": "string"
},
"parent_id": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Parent Id"
},
"show_income": {
"title": "Show Income",
"type": "boolean"
},
"show_outcome": {
"title": "Show Outcome",
"type": "boolean"
}
},
"required": [
"id",
"parent_id",
"name",
"icon",
"color",
"show_income",
"show_outcome",
"archived"
],
"title": "CategoryOut",
"type": "object"
},
"DataQualityRow": {
"properties": {
"check_name": {
"title": "Check Name",
"type": "string"
},
"computed_at": {
"format": "date-time",
"title": "Computed At",
"type": "string"
},
"count": {
"title": "Count",
"type": "integer"
},
"detail": {
"title": "Detail",
"type": "string"
},
"id": {
"title": "Id",
"type": "integer"
},
"ref": {
"anyOf": [
{
"additionalProperties": true,
"type": "object"
},
{
"type": "null"
}
],
"title": "Ref"
},
"severity": {
"title": "Severity",
"type": "string"
}
},
"required": [
"id",
"check_name",
"severity",
"detail",
"count",
"ref",
"computed_at"
],
"title": "DataQualityRow",
"type": "object"
},
"EventKind": {
"enum": [
"buy",
"sell",
"dividend",
"coupon",
"interest",
"tax",
"tax_refund",
"commission",
"deposit",
"withdrawal",
"transfer_in",
"transfer_out",
"split",
"amortization",
"repayment",
"fx_exchange",
"other"
],
"title": "EventKind",
"type": "string"
},
"EventOut": {
"properties": {
"account_id": {
"title": "Account Id",
"type": "integer"
},
"accrued_interest": {
"anyOf": [
{
"description": "decimal as string",
"type": "string"
},
{
"type": "null"
}
],
"title": "Accrued Interest"
},
"amount": {
"description": "decimal as string",
"title": "Amount",
"type": "string"
},
"amount_rub": {
"anyOf": [
{
"description": "decimal as string",
"type": "string"
},
{
"type": "null"
}
],
"title": "Amount Rub"
},
"currency": {
"title": "Currency",
"type": "string"
},
"description": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Description"
},
"external_flow": {
"title": "External Flow",
"type": "boolean"
},
"fee": {
"anyOf": [
{
"description": "decimal as string",
"type": "string"
},
{
"type": "null"
}
],
"title": "Fee"
},
"id": {
"title": "Id",
"type": "integer"
},
"instrument_id": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Instrument Id"
},
"kind": {
"$ref": "#/components/schemas/EventKind"
},
"price": {
"anyOf": [
{
"description": "decimal as string",
"type": "string"
},
{
"type": "null"
}
],
"title": "Price"
},
"price_currency": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Price Currency"
},
"quantity": {
"anyOf": [
{
"description": "decimal as string",
"type": "string"
},
{
"type": "null"
}
],
"title": "Quantity"
},
"status": {
"$ref": "#/components/schemas/EventStatus"
},
"tax": {
"anyOf": [
{
"description": "decimal as string",
"type": "string"
},
{
"type": "null"
}
],
"title": "Tax"
},
"ticker": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Ticker"
},
"trade_date": {
"format": "date",
"title": "Trade Date",
"type": "string"
},
"ts": {
"format": "date-time",
"title": "Ts",
"type": "string"
}
},
"required": [
"id",
"account_id",
"instrument_id",
"ticker",
"kind",
"status",
"trade_date",
"ts",
"quantity",
"price",
"price_currency",
"amount",
"amount_rub",
"currency",
"fee",
"tax",
"accrued_interest",
"description",
"external_flow"
],
"title": "EventOut",
"type": "object"
},
"EventPage": {
"properties": {
"items": {
"items": {
"$ref": "#/components/schemas/EventOut"
},
"title": "Items",
"type": "array"
},
"page": {
"title": "Page",
"type": "integer"
},
"page_size": {
"title": "Page Size",
"type": "integer"
},
"total": {
"title": "Total",
"type": "integer"
}
},
"required": [
"items",
"total",
"page",
"page_size"
],
"title": "EventPage",
"type": "object"
},
"EventSource": {
"description": "Which feed is the truth for an account's ledger; others become `shadow` events.",
"enum": [
"tinvest_api",
"report_sber",
"report_vtb",
"manual"
],
"title": "EventSource",
"type": "string"
},
"EventStatus": {
"enum": [
"confirmed",
"pending",
"shadow",
"ignored"
],
"title": "EventStatus",
"type": "string"
},
"FlowType": {
"enum": [
"income",
"expense",
"internal_transfer",
"savings_transfer",
"broker_external_flow",
"deleted",
"other"
],
"title": "FlowType",
"type": "string"
},
"Health": {
"properties": {
"database": {
"title": "Database",
"type": "string"
},
"status": {
"title": "Status",
"type": "string"
},
"version": {
"title": "Version",
"type": "string"
}
},
"required": [
"status",
"version",
"database"
],
"title": "Health",
"type": "object"
},
"HoldingOut": {
"properties": {
"accrued_interest_rub": {
"anyOf": [
{
"description": "decimal as string",
"type": "string"
},
{
"type": "null"
}
],
"title": "Accrued Interest Rub"
},
"asset_class": {
"title": "Asset Class",
"type": "string"
},
"avg_cost": {
"anyOf": [
{
"description": "decimal as string",
"type": "string"
},
{
"type": "null"
}
],
"title": "Avg Cost"
},
"board": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Board"
},
"cost_currency": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Cost Currency"
},
"cost_total_rub": {
"anyOf": [
{
"description": "decimal as string",
"type": "string"
},
{
"type": "null"
}
],
"title": "Cost Total Rub"
},
"currency": {
"title": "Currency",
"type": "string"
},
"days_held": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Days Held"
},
"first_buy_date": {
"anyOf": [
{
"format": "date",
"type": "string"
},
{
"type": "null"
}
],
"title": "First Buy Date"
},
"income_rub": {
"anyOf": [
{
"description": "decimal as string",
"type": "string"
},
{
"type": "null"
}
],
"title": "Income Rub"
},
"instrument_id": {
"title": "Instrument Id",
"type": "integer"
},
"ldv_eligible_qty": {
"description": "decimal as string",
"title": "Ldv Eligible Qty",
"type": "string"
},
"market_price": {
"anyOf": [
{
"description": "decimal as string",
"type": "string"
},
{
"type": "null"
}
],
"title": "Market Price"
},
"name": {
"title": "Name",
"type": "string"
},
"price_currency": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Price Currency"
},
"price_date": {
"anyOf": [
{
"format": "date",
"type": "string"
},
{
"type": "null"
}
],
"title": "Price Date"
},
"price_status": {
"title": "Price Status",
"type": "string"
},
"qty": {
"description": "decimal as string",
"title": "Qty",
"type": "string"
},
"realized_pnl_rub": {
"anyOf": [
{
"description": "decimal as string",
"type": "string"
},
{
"type": "null"
}
],
"title": "Realized Pnl Rub"
},
"ticker": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Ticker"
},
"unrealized_pnl_native": {
"anyOf": [
{
"description": "decimal as string",
"type": "string"
},
{
"type": "null"
}
],
"title": "Unrealized Pnl Native"
},
"unrealized_pnl_rub": {
"anyOf": [
{
"description": "decimal as string",
"type": "string"
},
{
"type": "null"
}
],
"title": "Unrealized Pnl Rub"
},
"value_native": {
"anyOf": [
{
"description": "decimal as string",
"type": "string"
},
{
"type": "null"
}
],
"title": "Value Native"
},
"value_rub": {
"anyOf": [
{
"description": "decimal as string",
"type": "string"
},
{
"type": "null"
}
],
"title": "Value Rub"
},
"weight": {
"anyOf": [
{
"description": "decimal as string",
"type": "string"
},
{
"type": "null"
}
],
"title": "Weight"
},
"xirr": {
"anyOf": [
{
"description": "decimal as string",
"type": "string"
},
{
"type": "null"
}
],
"title": "Xirr"
}
},
"required": [
"instrument_id",
"ticker",
"name",
"asset_class",
"board",
"currency",
"qty",
"avg_cost",
"cost_currency",
"cost_total_rub",
"market_price",
"price_currency",
"price_date",
"price_status",
"value_native",
"value_rub",
"accrued_interest_rub",
"unrealized_pnl_native",
"unrealized_pnl_rub",
"realized_pnl_rub",
"income_rub",
"weight",
"xirr",
"first_buy_date",
"days_held",
"ldv_eligible_qty"
],
"title": "HoldingOut",
"type": "object"
},
"InstrumentDetail": {
"properties": {
"events": {
"items": {
"$ref": "#/components/schemas/EventOut"
},
"title": "Events",
"type": "array"
},
"holding": {
"anyOf": [
{
"$ref": "#/components/schemas/HoldingOut"
},
{
"type": "null"
}
]
},
"instrument": {
"$ref": "#/components/schemas/InstrumentOut"
},
"lots": {
"items": {
"$ref": "#/components/schemas/LotOut"
},
"title": "Lots",
"type": "array"
},
"prices": {
"items": {
"$ref": "#/components/schemas/PricePoint"
},
"title": "Prices",
"type": "array"
}
},
"required": [
"instrument",
"holding",
"lots",
"events",
"prices"
],
"title": "InstrumentDetail",
"type": "object"
},
"InstrumentOut": {
"properties": {
"asset_class": {
"title": "Asset Class",
"type": "string"
},
"board": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Board"
},
"country": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Country"
},
"currency": {
"title": "Currency",
"type": "string"
},
"exchange": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Exchange"
},
"figi": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Figi"
},
"id": {
"title": "Id",
"type": "integer"
},
"is_active": {
"title": "Is Active",
"type": "boolean"
},
"isin": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Isin"
},
"issuer": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Issuer"
},
"lot": {
"title": "Lot",
"type": "integer"
},
"maturity_date": {
"anyOf": [
{
"format": "date",
"type": "string"
},
{
"type": "null"
}
],
"title": "Maturity Date"
},
"name": {
"title": "Name",
"type": "string"
},
"nominal": {
"anyOf": [
{
"description": "decimal as string",
"type": "string"
},
{
"type": "null"
}
],
"title": "Nominal"
},
"nominal_currency": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Nominal Currency"
},
"sector": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Sector"
},
"ticker": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Ticker"
}
},
"required": [
"id",
"asset_class",
"isin",
"figi",
"ticker",
"board",
"exchange",
"name",
"issuer",
"currency",
"lot",
"nominal",
"nominal_currency",
"maturity_date",
"sector",
"country",
"is_active"
],
"title": "InstrumentOut",
"type": "object"
},
"JobStatus": {
"enum": [
"queued",
"running",
"done",
"error"
],
"title": "JobStatus",
"type": "string"
},
"LoginRequest": {
"properties": {
"email": {
"format": "email",
"title": "Email",
"type": "string"
},
"password": {
"maxLength": 1024,
"minLength": 1,
"title": "Password",
"type": "string"
}
},
"required": [
"email",
"password"
],
"title": "LoginRequest",
"type": "object"
},
"LotOut": {
"properties": {
"account_id": {
"title": "Account Id",
"type": "integer"
},
"closed_at": {
"anyOf": [
{
"format": "date",
"type": "string"
},
{
"type": "null"
}
],
"title": "Closed At"
},
"cost_currency": {
"title": "Cost Currency",
"type": "string"
},
"cost_per_unit": {
"description": "decimal as string",
"title": "Cost Per Unit",
"type": "string"
},
"cost_total_rub": {
"anyOf": [
{
"description": "decimal as string",
"type": "string"
},
{
"type": "null"
}
],
"title": "Cost Total Rub"
},
"id": {
"title": "Id",
"type": "integer"
},
"open_date": {
"format": "date",
"title": "Open Date",
"type": "string"
},
"qty_open": {
"description": "decimal as string",
"title": "Qty Open",
"type": "string"
},
"qty_remaining": {
"description": "decimal as string",
"title": "Qty Remaining",
"type": "string"
}
},
"required": [
"id",
"account_id",
"open_date",
"qty_open",
"qty_remaining",
"cost_per_unit",
"cost_currency",
"cost_total_rub",
"closed_at"
],
"title": "LotOut",
"type": "object"
},
"NetWorthBreakdown": {
"properties": {
"accounts": {
"items": {
"$ref": "#/components/schemas/AccountBalance"
},
"title": "Accounts",
"type": "array"
},
"by_currency": {
"additionalProperties": {
"type": "string"
},
"title": "By Currency",
"type": "object"
},
"d": {
"anyOf": [
{
"format": "date",
"type": "string"
},
{
"type": "null"
}
],
"title": "D"
},
"debt_rub": {
"description": "decimal as string",
"title": "Debt Rub",
"type": "string"
},
"investment_rub": {
"description": "decimal as string",
"title": "Investment Rub",
"type": "string"
},
"liquid_rub": {
"description": "decimal as string",
"title": "Liquid Rub",
"type": "string"
},
"missing_fx_count": {
"title": "Missing Fx Count",
"type": "integer"
},
"savings_rub": {
"description": "decimal as string",
"title": "Savings Rub",
"type": "string"
},
"total_rub": {
"description": "decimal as string",
"title": "Total Rub",
"type": "string"
}
},
"required": [
"d",
"total_rub",
"liquid_rub",
"savings_rub",
"investment_rub",
"debt_rub",
"by_currency",
"missing_fx_count",
"accounts"
],
"title": "NetWorthBreakdown",
"type": "object"
},
"NetWorthDay": {
"properties": {
"by_currency": {
"additionalProperties": {
"type": "string"
},
"title": "By Currency",
"type": "object"
},
"d": {
"format": "date",
"title": "D",
"type": "string"
},
"debt_rub": {
"description": "decimal as string",
"title": "Debt Rub",
"type": "string"
},
"investment_rub": {
"description": "decimal as string",
"title": "Investment Rub",
"type": "string"
},
"liquid_rub": {
"description": "decimal as string",
"title": "Liquid Rub",
"type": "string"
},
"missing_fx_count": {
"title": "Missing Fx Count",
"type": "integer"
},
"savings_rub": {
"description": "decimal as string",
"title": "Savings Rub",
"type": "string"
},
"total_rub": {
"description": "decimal as string",
"title": "Total Rub",
"type": "string"
}
},
"required": [
"d",
"total_rub",
"liquid_rub",
"savings_rub",
"investment_rub",
"debt_rub",
"by_currency",
"missing_fx_count"
],
"title": "NetWorthDay",
"type": "object"
},
"PricePoint": {
"properties": {
"accrued_interest": {
"anyOf": [
{
"description": "decimal as string",
"type": "string"
},
{
"type": "null"
}
],
"title": "Accrued Interest"
},
"close": {
"description": "decimal as string",
"title": "Close",
"type": "string"
},
"currency": {
"title": "Currency",
"type": "string"
},
"d": {
"format": "date",
"title": "D",
"type": "string"
}
},
"required": [
"d",
"close",
"currency",
"accrued_interest"
],
"title": "PricePoint",
"type": "object"
},
"Problem": {
"description": "RFC 7807 error body (application/problem+json)",
"properties": {
"detail": {
"type": "string"
},
"errors": {
"items": {},
"type": "array"
},
"status": {
"type": "integer"
},
"title": {
"type": "string"
}
},
"required": [
"status",
"title"
],
"title": "Problem",
"type": "object"
},
"RefreshLogOut": {
"properties": {
"error": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Error"
},
"finished_at": {
"anyOf": [
{
"format": "date-time",
"type": "string"
},
{
"type": "null"
}
],
"title": "Finished At"
},
"id": {
"title": "Id",
"type": "integer"
},
"started_at": {
"format": "date-time",
"title": "Started At",
"type": "string"
},
"trigger": {
"title": "Trigger",
"type": "string"
}
},
"required": [
"id",
"started_at",
"finished_at",
"trigger",
"error"
],
"title": "RefreshLogOut",
"type": "object"
},
"RefreshRequest": {
"properties": {
"refresh_token": {
"minLength": 1,
"title": "Refresh Token",
"type": "string"
}
},
"required": [
"refresh_token"
],
"title": "RefreshRequest",
"type": "object"
},
"ReturnsOut": {
"properties": {
"abs_pnl_rub": {
"description": "decimal as string",
"title": "Abs Pnl Rub",
"type": "string"
},
"date_from": {
"format": "date",
"title": "Date From",
"type": "string"
},
"date_to": {
"format": "date",
"title": "Date To",
"type": "string"
},
"external_flow_rub": {
"description": "decimal as string",
"title": "External Flow Rub",
"type": "string"
},
"period": {
"title": "Period",
"type": "string"
},
"twr": {
"anyOf": [
{
"description": "decimal as string",
"type": "string"
},
{
"type": "null"
}
],
"title": "Twr"
},
"twr_annualized": {
"anyOf": [
{
"description": "decimal as string",
"type": "string"
},
{
"type": "null"
}
],
"title": "Twr Annualized"
},
"twr_days_skipped": {
"title": "Twr Days Skipped",
"type": "integer"
},
"value_end_rub": {
"description": "decimal as string",
"title": "Value End Rub",
"type": "string"
},
"value_start_rub": {
"description": "decimal as string",
"title": "Value Start Rub",
"type": "string"
},
"xirr": {
"anyOf": [
{
"description": "decimal as string",
"type": "string"
},
{
"type": "null"
}
],
"title": "Xirr"
}
},
"required": [
"period",
"date_from",
"date_to",
"value_start_rub",
"value_end_rub",
"external_flow_rub",
"abs_pnl_rub",
"xirr",
"twr",
"twr_annualized",
"twr_days_skipped"
],
"title": "ReturnsOut",
"type": "object"
},
"RuleCreate": {
"additionalProperties": false,
"properties": {
"enabled": {
"default": true,
"title": "Enabled",
"type": "boolean"
},
"kind": {
"$ref": "#/components/schemas/RuleKind"
},
"match_type": {
"$ref": "#/components/schemas/RuleMatchType"
},
"note": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Note"
},
"pattern": {
"maxLength": 512,
"minLength": 1,
"title": "Pattern",
"type": "string"
},
"priority": {
"default": 100,
"title": "Priority",
"type": "integer"
},
"value": {
"anyOf": [
{
"maxLength": 512,
"type": "string"
},
{
"type": "null"
}
],
"title": "Value"
}
},
"required": [
"kind",
"match_type",
"pattern"
],
"title": "RuleCreate",
"type": "object"
},
"RuleKind": {
"enum": [
"savings",
"one_off",
"category",
"payee",
"broker_target",
"ignore"
],
"title": "RuleKind",
"type": "string"
},
"RuleMatchType": {
"enum": [
"id",
"payee",
"comment",
"category",
"mcc",
"account"
],
"title": "RuleMatchType",
"type": "string"
},
"RuleOut": {
"properties": {
"enabled": {
"title": "Enabled",
"type": "boolean"
},
"id": {
"title": "Id",
"type": "integer"
},
"kind": {
"$ref": "#/components/schemas/RuleKind"
},
"last_matched_at": {
"anyOf": [
{
"format": "date-time",
"type": "string"
},
{
"type": "null"
}
],
"title": "Last Matched At"
},
"match_count": {
"title": "Match Count",
"type": "integer"
},
"match_type": {
"$ref": "#/components/schemas/RuleMatchType"
},
"note": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Note"
},
"pattern": {
"title": "Pattern",
"type": "string"
},
"priority": {
"title": "Priority",
"type": "integer"
},
"value": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Value"
}
},
"required": [
"id",
"kind",
"match_type",
"pattern",
"value",
"note",
"enabled",
"priority",
"last_matched_at",
"match_count"
],
"title": "RuleOut",
"type": "object"
},
"RulePatch": {
"additionalProperties": false,
"properties": {
"enabled": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
],
"title": "Enabled"
},
"kind": {
"anyOf": [
{
"$ref": "#/components/schemas/RuleKind"
},
{
"type": "null"
}
]
},
"match_type": {
"anyOf": [
{
"$ref": "#/components/schemas/RuleMatchType"
},
{
"type": "null"
}
]
},
"note": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Note"
},
"pattern": {
"anyOf": [
{
"maxLength": 512,
"minLength": 1,
"type": "string"
},
{
"type": "null"
}
],
"title": "Pattern"
},
"priority": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Priority"
},
"value": {
"anyOf": [
{
"maxLength": 512,
"type": "string"
},
{
"type": "null"
}
],
"title": "Value"
}
},
"title": "RulePatch",
"type": "object"
},
"RunStatus": {
"enum": [
"running",
"ok",
"error"
],
"title": "RunStatus",
"type": "string"
},
"RunwayOut": {
"properties": {
"as_of": {
"format": "date",
"title": "As Of",
"type": "string"
},
"avg_baseline_3m_rub": {
"description": "decimal as string",
"title": "Avg Baseline 3M Rub",
"type": "string"
},
"liquid_reserve_rub": {
"description": "decimal as string",
"title": "Liquid Reserve Rub",
"type": "string"
},
"runway_months": {
"anyOf": [
{
"description": "decimal as string",
"type": "string"
},
{
"type": "null"
}
],
"title": "Runway Months"
}
},
"required": [
"as_of",
"liquid_reserve_rub",
"avg_baseline_3m_rub",
"runway_months"
],
"title": "RunwayOut",
"type": "object"
},
"ScopeOut": {
"description": "One reportable set of accounts: `all`, `account:<id>` or `portfolio:<id>`.",
"properties": {
"account_ids": {
"items": {
"type": "integer"
},
"title": "Account Ids",
"type": "array"
},
"name": {
"title": "Name",
"type": "string"
},
"scope": {
"title": "Scope",
"type": "string"
}
},
"required": [
"scope",
"name",
"account_ids"
],
"title": "ScopeOut",
"type": "object"
},
"SourceStatus": {
"properties": {
"cursor": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Cursor"
},
"last_run_at": {
"anyOf": [
{
"format": "date-time",
"type": "string"
},
{
"type": "null"
}
],
"title": "Last Run At"
},
"last_run_status": {
"anyOf": [
{
"$ref": "#/components/schemas/RunStatus"
},
{
"type": "null"
}
]
},
"last_success_at": {
"anyOf": [
{
"format": "date-time",
"type": "string"
},
{
"type": "null"
}
],
"title": "Last Success At"
},
"queued": {
"title": "Queued",
"type": "boolean"
},
"source": {
"title": "Source",
"type": "string"
}
},
"required": [
"source",
"cursor",
"last_run_at",
"last_success_at",
"last_run_status",
"queued"
],
"title": "SourceStatus",
"type": "object"
},
"SpendingRow": {
"properties": {
"amount_rub": {
"description": "decimal as string",
"title": "Amount Rub",
"type": "string"
},
"category_id": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Category Id"
},
"category_name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Category Name"
},
"month": {
"format": "date",
"title": "Month",
"type": "string"
},
"root_category_id": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Root Category Id"
},
"root_category_name": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Root Category Name"
},
"txn_count": {
"title": "Txn Count",
"type": "integer"
}
},
"required": [
"month",
"category_id",
"category_name",
"root_category_id",
"root_category_name",
"amount_rub",
"txn_count"
],
"title": "SpendingRow",
"type": "object"
},
"SummaryOut": {
"description": "The dashboard's one-screen answer for a scope.",
"properties": {
"as_of": {
"anyOf": [
{
"format": "date",
"type": "string"
},
{
"type": "null"
}
],
"title": "As Of"
},
"cash_rub": {
"description": "decimal as string",
"title": "Cash Rub",
"type": "string"
},
"computed_at": {
"anyOf": [
{
"format": "date-time",
"type": "string"
},
{
"type": "null"
}
],
"title": "Computed At"
},
"holding_count": {
"title": "Holding Count",
"type": "integer"
},
"income_rub": {
"description": "decimal as string",
"title": "Income Rub",
"type": "string"
},
"invested_net_rub": {
"description": "decimal as string",
"title": "Invested Net Rub",
"type": "string"
},
"market_value_rub": {
"description": "decimal as string",
"title": "Market Value Rub",
"type": "string"
},
"pnl_total_rub": {
"anyOf": [
{
"description": "decimal as string",
"type": "string"
},
{
"type": "null"
}
],
"title": "Pnl Total Rub"
},
"realized_pnl_rub": {
"description": "decimal as string",
"title": "Realized Pnl Rub",
"type": "string"
},
"returns": {
"items": {
"$ref": "#/components/schemas/ReturnsOut"
},
"title": "Returns",
"type": "array"
},
"scope": {
"title": "Scope",
"type": "string"
},
"stale_count": {
"title": "Stale Count",
"type": "integer"
},
"total_rub": {
"description": "decimal as string",
"title": "Total Rub",
"type": "string"
},
"unpriced_count": {
"title": "Unpriced Count",
"type": "integer"
}
},
"required": [
"scope",
"as_of",
"computed_at",
"market_value_rub",
"cash_rub",
"total_rub",
"invested_net_rub",
"pnl_total_rub",
"realized_pnl_rub",
"income_rub",
"holding_count",
"unpriced_count",
"stale_count",
"returns"
],
"title": "SummaryOut",
"type": "object"
},
"SyncJobOut": {
"properties": {
"id": {
"format": "uuid",
"title": "Id",
"type": "string"
},
"requested_at": {
"format": "date-time",
"title": "Requested At",
"type": "string"
},
"source": {
"title": "Source",
"type": "string"
},
"status": {
"$ref": "#/components/schemas/JobStatus"
}
},
"required": [
"id",
"source",
"status",
"requested_at"
],
"title": "SyncJobOut",
"type": "object"
},
"SyncRunOut": {
"properties": {
"counts": {
"anyOf": [
{
"additionalProperties": true,
"type": "object"
},
{
"type": "null"
}
],
"title": "Counts"
},
"cursor_after": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Cursor After"
},
"cursor_before": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Cursor Before"
},
"error": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Error"
},
"finished_at": {
"anyOf": [
{
"format": "date-time",
"type": "string"
},
{
"type": "null"
}
],
"title": "Finished At"
},
"id": {
"format": "uuid",
"title": "Id",
"type": "string"
},
"source": {
"title": "Source",
"type": "string"
},
"started_at": {
"format": "date-time",
"title": "Started At",
"type": "string"
},
"status": {
"$ref": "#/components/schemas/RunStatus"
},
"triggered_by": {
"title": "Triggered By",
"type": "string"
},
"warnings": {
"anyOf": [
{
"items": {},
"type": "array"
},
{
"type": "null"
}
],
"title": "Warnings"
}
},
"required": [
"id",
"source",
"status",
"triggered_by",
"started_at",
"finished_at",
"cursor_before",
"cursor_after",
"counts",
"warnings",
"error"
],
"title": "SyncRunOut",
"type": "object"
},
"TokenPair": {
"properties": {
"access_token": {
"title": "Access Token",
"type": "string"
},
"expires_in": {
"title": "Expires In",
"type": "integer"
},
"refresh_token": {
"title": "Refresh Token",
"type": "string"
},
"token_type": {
"default": "bearer",
"title": "Token Type",
"type": "string"
}
},
"required": [
"access_token",
"refresh_token",
"expires_in"
],
"title": "TokenPair",
"type": "object"
},
"TransactionOut": {
"properties": {
"category_id": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Category Id"
},
"comment": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Comment"
},
"date": {
"format": "date",
"title": "Date",
"type": "string"
},
"deleted": {
"title": "Deleted",
"type": "boolean"
},
"flow_type": {
"$ref": "#/components/schemas/FlowType"
},
"hold": {
"title": "Hold",
"type": "boolean"
},
"id": {
"title": "Id",
"type": "integer"
},
"income": {
"description": "decimal as string",
"title": "Income",
"type": "string"
},
"income_account_id": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Income Account Id"
},
"income_currency": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Income Currency"
},
"income_rub": {
"anyOf": [
{
"description": "decimal as string",
"type": "string"
},
{
"type": "null"
}
],
"title": "Income Rub"
},
"is_one_off": {
"title": "Is One Off",
"type": "boolean"
},
"mcc": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Mcc"
},
"outcome": {
"description": "decimal as string",
"title": "Outcome",
"type": "string"
},
"outcome_account_id": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Outcome Account Id"
},
"outcome_currency": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Outcome Currency"
},
"outcome_rub": {
"anyOf": [
{
"description": "decimal as string",
"type": "string"
},
{
"type": "null"
}
],
"title": "Outcome Rub"
},
"payee": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Payee"
},
"payee_canonical": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Payee Canonical"
},
"source_id": {
"title": "Source Id",
"type": "string"
},
"tags": {
"items": {
"type": "integer"
},
"title": "Tags",
"type": "array"
},
"trip_id": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Trip Id"
},
"ts": {
"format": "date-time",
"title": "Ts",
"type": "string"
}
},
"required": [
"id",
"source_id",
"ts",
"date",
"income",
"income_currency",
"income_account_id",
"income_rub",
"outcome",
"outcome_currency",
"outcome_account_id",
"outcome_rub",
"payee",
"payee_canonical",
"comment",
"mcc",
"hold",
"deleted",
"flow_type",
"category_id",
"is_one_off",
"trip_id",
"tags"
],
"title": "TransactionOut",
"type": "object"
},
"TransactionPage": {
"properties": {
"items": {
"items": {
"$ref": "#/components/schemas/TransactionOut"
},
"title": "Items",
"type": "array"
},
"page": {
"title": "Page",
"type": "integer"
},
"page_size": {
"title": "Page Size",
"type": "integer"
},
"total": {
"title": "Total",
"type": "integer"
}
},
"required": [
"items",
"total",
"page",
"page_size"
],
"title": "TransactionPage",
"type": "object"
},
"UserOut": {
"properties": {
"email": {
"title": "Email",
"type": "string"
},
"id": {
"title": "Id",
"type": "integer"
}
},
"required": [
"id",
"email"
],
"title": "UserOut",
"type": "object"
},
"ValueDay": {
"properties": {
"accrued_interest_rub": {
"description": "decimal as string",
"title": "Accrued Interest Rub",
"type": "string"
},
"cash_rub": {
"description": "decimal as string",
"title": "Cash Rub",
"type": "string"
},
"d": {
"format": "date",
"title": "D",
"type": "string"
},
"external_flow_rub": {
"description": "decimal as string",
"title": "External Flow Rub",
"type": "string"
},
"invested_net_rub": {
"description": "decimal as string",
"title": "Invested Net Rub",
"type": "string"
},
"market_value_rub": {
"description": "decimal as string",
"title": "Market Value Rub",
"type": "string"
},
"missing_fx_count": {
"title": "Missing Fx Count",
"type": "integer"
},
"missing_price_count": {
"title": "Missing Price Count",
"type": "integer"
},
"pnl_total_rub": {
"anyOf": [
{
"description": "decimal as string",
"type": "string"
},
{
"type": "null"
}
],
"title": "Pnl Total Rub"
},
"stale_price_count": {
"title": "Stale Price Count",
"type": "integer"
},
"total_rub": {
"description": "decimal as string",
"title": "Total Rub",
"type": "string"
}
},
"required": [
"d",
"market_value_rub",
"accrued_interest_rub",
"cash_rub",
"total_rub",
"external_flow_rub",
"invested_net_rub",
"pnl_total_rub",
"stale_price_count",
"missing_price_count",
"missing_fx_count"
],
"title": "ValueDay",
"type": "object"
}
},
"securitySchemes": {
"HTTPBearer": {
"scheme": "bearer",
"type": "http"
}
}
},
"info": {
"title": "fin-tracker",
"version": "0.1.0"
},
"openapi": "3.1.0",
"paths": {
"/api/v1/accounts": {
"get": {
"description": "Every account, archived ones included — the client decides what to show.",
"operationId": "accounts_list",
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"items": {
"$ref": "#/components/schemas/AccountOut"
},
"title": "Response Accounts List",
"type": "array"
}
}
},
"description": "Successful Response"
},
"default": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Error"
}
},
"security": [
{
"HTTPBearer": []
}
],
"summary": "List",
"tags": [
"accounts"
]
}
},
"/api/v1/accounts/{account_id}": {
"patch": {
"description": "Update the user-owned fields. Everything else is overwritten by the next sync.",
"operationId": "accounts_patch",
"parameters": [
{
"in": "path",
"name": "account_id",
"required": true,
"schema": {
"title": "Account Id",
"type": "integer"
}
}
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/AccountPatch"
}
}
},
"required": true
},
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/AccountOut"
}
}
},
"description": "Successful Response"
},
"default": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Error"
}
},
"security": [
{
"HTTPBearer": []
}
],
"summary": "Patch",
"tags": [
"accounts"
]
}
},
"/api/v1/analytics/allocation": {
"get": {
"description": "Buckets of one dimension (or all four), largest first.",
"operationId": "analytics_allocation",
"parameters": [
{
"description": "all | account:<id> | portfolio:<id>",
"in": "query",
"name": "scope",
"required": false,
"schema": {
"default": "all",
"description": "all | account:<id> | portfolio:<id>",
"title": "Scope",
"type": "string"
}
},
{
"in": "query",
"name": "dimension",
"required": false,
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/AllocationDimension"
},
{
"type": "null"
}
],
"title": "Dimension"
}
}
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"items": {
"$ref": "#/components/schemas/AllocationBucket"
},
"title": "Response Analytics Allocation",
"type": "array"
}
}
},
"description": "Successful Response"
},
"default": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Error"
}
},
"security": [
{
"HTTPBearer": []
}
],
"summary": "Allocation",
"tags": [
"analytics"
]
}
},
"/api/v1/analytics/holdings": {
"get": {
"description": "Open positions, most valuable first; unpriced ones last with null values.",
"operationId": "analytics_holdings",
"parameters": [
{
"description": "all | account:<id> | portfolio:<id>",
"in": "query",
"name": "scope",
"required": false,
"schema": {
"default": "all",
"description": "all | account:<id> | portfolio:<id>",
"title": "Scope",
"type": "string"
}
}
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"items": {
"$ref": "#/components/schemas/HoldingOut"
},
"title": "Response Analytics Holdings",
"type": "array"
}
}
},
"description": "Successful Response"
},
"default": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Error"
}
},
"security": [
{
"HTTPBearer": []
}
],
"summary": "Holdings",
"tags": [
"analytics"
]
}
},
"/api/v1/analytics/returns": {
"get": {
"description": "XIRR and TWR per period, shortest first.",
"operationId": "analytics_returns",
"parameters": [
{
"description": "all | account:<id> | portfolio:<id>",
"in": "query",
"name": "scope",
"required": false,
"schema": {
"default": "all",
"description": "all | account:<id> | portfolio:<id>",
"title": "Scope",
"type": "string"
}
}
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"items": {
"$ref": "#/components/schemas/ReturnsOut"
},
"title": "Response Analytics Returns",
"type": "array"
}
}
},
"description": "Successful Response"
},
"default": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Error"
}
},
"security": [
{
"HTTPBearer": []
}
],
"summary": "Returns",
"tags": [
"analytics"
]
}
},
"/api/v1/analytics/scopes": {
"get": {
"description": "Every set of accounts the metrics were built for.",
"operationId": "analytics_scopes",
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"items": {
"$ref": "#/components/schemas/ScopeOut"
},
"title": "Response Analytics Scopes",
"type": "array"
}
}
},
"description": "Successful Response"
},
"default": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Error"
}
},
"security": [
{
"HTTPBearer": []
}
],
"summary": "Scopes",
"tags": [
"analytics"
]
}
},
"/api/v1/analytics/summary": {
"get": {
"description": "One screen's worth: the latest day, the totals it adds up to, and the returns.",
"operationId": "analytics_summary",
"parameters": [
{
"description": "all | account:<id> | portfolio:<id>",
"in": "query",
"name": "scope",
"required": false,
"schema": {
"default": "all",
"description": "all | account:<id> | portfolio:<id>",
"title": "Scope",
"type": "string"
}
}
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SummaryOut"
}
}
},
"description": "Successful Response"
},
"default": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Error"
}
},
"security": [
{
"HTTPBearer": []
}
],
"summary": "Summary",
"tags": [
"analytics"
]
}
},
"/api/v1/analytics/value-series": {
"get": {
"description": "Daily portfolio value; defaults to the last 365 days.",
"operationId": "analytics_value_series",
"parameters": [
{
"description": "all | account:<id> | portfolio:<id>",
"in": "query",
"name": "scope",
"required": false,
"schema": {
"default": "all",
"description": "all | account:<id> | portfolio:<id>",
"title": "Scope",
"type": "string"
}
},
{
"in": "query",
"name": "from",
"required": false,
"schema": {
"anyOf": [
{
"format": "date",
"type": "string"
},
{
"type": "null"
}
],
"title": "From"
}
},
{
"in": "query",
"name": "to",
"required": false,
"schema": {
"anyOf": [
{
"format": "date",
"type": "string"
},
{
"type": "null"
}
],
"title": "To"
}
}
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"items": {
"$ref": "#/components/schemas/ValueDay"
},
"title": "Response Analytics Value Series",
"type": "array"
}
}
},
"description": "Successful Response"
},
"default": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Error"
}
},
"security": [
{
"HTTPBearer": []
}
],
"summary": "Value Series",
"tags": [
"analytics"
]
}
},
"/api/v1/auth/login": {
"post": {
"operationId": "auth_login",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/LoginRequest"
}
}
},
"required": true
},
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/TokenPair"
}
}
},
"description": "Successful Response"
},
"default": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Error"
}
},
"summary": "Login",
"tags": [
"auth"
]
}
},
"/api/v1/auth/logout": {
"post": {
"operationId": "auth_logout",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/RefreshRequest"
}
}
},
"required": true
},
"responses": {
"204": {
"description": "Successful Response"
},
"default": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Error"
}
},
"summary": "Logout",
"tags": [
"auth"
]
}
},
"/api/v1/auth/me": {
"get": {
"operationId": "auth_me",
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/UserOut"
}
}
},
"description": "Successful Response"
},
"default": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Error"
}
},
"security": [
{
"HTTPBearer": []
}
],
"summary": "Me",
"tags": [
"auth"
]
}
},
"/api/v1/auth/refresh": {
"post": {
"operationId": "auth_refresh",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/RefreshRequest"
}
}
},
"required": true
},
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/TokenPair"
}
}
},
"description": "Successful Response"
},
"default": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Error"
}
},
"summary": "Refresh",
"tags": [
"auth"
]
}
},
"/api/v1/cashflow/monthly": {
"get": {
"description": "The last `months` months, oldest first.",
"operationId": "cashflow_monthly",
"parameters": [
{
"in": "query",
"name": "months",
"required": false,
"schema": {
"default": 12,
"maximum": 120,
"minimum": 1,
"title": "Months",
"type": "integer"
}
}
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"items": {
"$ref": "#/components/schemas/CashFlowMonth"
},
"title": "Response Cashflow Monthly",
"type": "array"
}
}
},
"description": "Successful Response"
},
"default": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Error"
}
},
"security": [
{
"HTTPBearer": []
}
],
"summary": "Monthly",
"tags": [
"cashflow"
]
}
},
"/api/v1/categories": {
"get": {
"description": "Flat list of the ZenMoney tag tree; the client nests it by `parent_id`.",
"operationId": "categories_list",
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"items": {
"$ref": "#/components/schemas/CategoryOut"
},
"title": "Response Categories List",
"type": "array"
}
}
},
"description": "Successful Response"
},
"default": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Error"
}
},
"security": [
{
"HTTPBearer": []
}
],
"summary": "List",
"tags": [
"categories"
]
}
},
"/api/v1/data-quality": {
"get": {
"description": "Findings of the latest refresh, most serious first.",
"operationId": "metrics_data_quality",
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"items": {
"$ref": "#/components/schemas/DataQualityRow"
},
"title": "Response Metrics Data Quality",
"type": "array"
}
}
},
"description": "Successful Response"
},
"default": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Error"
}
},
"security": [
{
"HTTPBearer": []
}
],
"summary": "Data Quality",
"tags": [
"metrics"
]
}
},
"/api/v1/events": {
"get": {
"description": "One page of ledger events, newest first, with RUB amounts at each own date's rate.",
"operationId": "events_list",
"parameters": [
{
"in": "query",
"name": "from",
"required": false,
"schema": {
"anyOf": [
{
"format": "date",
"type": "string"
},
{
"type": "null"
}
],
"title": "From"
}
},
{
"in": "query",
"name": "to",
"required": false,
"schema": {
"anyOf": [
{
"format": "date",
"type": "string"
},
{
"type": "null"
}
],
"title": "To"
}
},
{
"in": "query",
"name": "account_id",
"required": false,
"schema": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Account Id"
}
},
{
"in": "query",
"name": "instrument_id",
"required": false,
"schema": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Instrument Id"
}
},
{
"in": "query",
"name": "kind",
"required": false,
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/EventKind"
},
{
"type": "null"
}
],
"title": "Kind"
}
},
{
"in": "query",
"name": "status",
"required": false,
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/EventStatus"
},
{
"type": "null"
}
],
"title": "Status"
}
},
{
"description": "only the events XIRR reads as boundary flows",
"in": "query",
"name": "external_flow",
"required": false,
"schema": {
"anyOf": [
{
"type": "boolean"
},
{
"type": "null"
}
],
"description": "only the events XIRR reads as boundary flows",
"title": "External Flow"
}
},
{
"description": "substring of description or ticker",
"in": "query",
"name": "q",
"required": false,
"schema": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "substring of description or ticker",
"title": "Q"
}
},
{
"in": "query",
"name": "page",
"required": false,
"schema": {
"default": 1,
"minimum": 1,
"title": "Page",
"type": "integer"
}
},
{
"in": "query",
"name": "page_size",
"required": false,
"schema": {
"default": 50,
"maximum": 500,
"minimum": 1,
"title": "Page Size",
"type": "integer"
}
}
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/EventPage"
}
}
},
"description": "Successful Response"
},
"default": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Error"
}
},
"security": [
{
"HTTPBearer": []
}
],
"summary": "List",
"tags": [
"events"
]
}
},
"/api/v1/health": {
"get": {
"operationId": "health_check",
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Health"
}
}
},
"description": "Successful Response"
},
"default": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Error"
}
},
"summary": "Check",
"tags": [
"health"
]
}
},
"/api/v1/instruments": {
"get": {
"operationId": "instruments_list",
"parameters": [
{
"description": "substring of ticker, name or ISIN",
"in": "query",
"name": "q",
"required": false,
"schema": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "substring of ticker, name or ISIN",
"title": "Q"
}
},
{
"description": "share | bond | etf | fund | currency | index | …",
"in": "query",
"name": "asset_class",
"required": false,
"schema": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "share | bond | etf | fund | currency | index | …",
"title": "Asset Class"
}
},
{
"description": "only instruments with an open lot",
"in": "query",
"name": "held_only",
"required": false,
"schema": {
"default": false,
"description": "only instruments with an open lot",
"title": "Held Only",
"type": "boolean"
}
},
{
"in": "query",
"name": "limit",
"required": false,
"schema": {
"default": 100,
"maximum": 500,
"minimum": 1,
"title": "Limit",
"type": "integer"
}
}
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"items": {
"$ref": "#/components/schemas/InstrumentOut"
},
"title": "Response Instruments List",
"type": "array"
}
}
},
"description": "Successful Response"
},
"default": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Error"
}
},
"security": [
{
"HTTPBearer": []
}
],
"summary": "List",
"tags": [
"instruments"
]
}
},
"/api/v1/instruments/{instrument_id}": {
"get": {
"description": "One instrument with its position, open lots, events and price history.",
"operationId": "instruments_get",
"parameters": [
{
"in": "path",
"name": "instrument_id",
"required": true,
"schema": {
"title": "Instrument Id",
"type": "integer"
}
},
{
"description": "all | account:<id> | portfolio:<id>",
"in": "query",
"name": "scope",
"required": false,
"schema": {
"default": "all",
"description": "all | account:<id> | portfolio:<id>",
"title": "Scope",
"type": "string"
}
},
{
"in": "query",
"name": "prices_from",
"required": false,
"schema": {
"anyOf": [
{
"format": "date",
"type": "string"
},
{
"type": "null"
}
],
"title": "Prices From"
}
}
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/InstrumentDetail"
}
}
},
"description": "Successful Response"
},
"default": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Error"
}
},
"security": [
{
"HTTPBearer": []
}
],
"summary": "Get",
"tags": [
"instruments"
]
}
},
"/api/v1/metrics/refresh": {
"post": {
"description": "Rebuild every metric_* table inline (seconds at personal volumes).",
"operationId": "metrics_refresh",
"responses": {
"202": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/RefreshLogOut"
}
}
},
"description": "Successful Response"
},
"default": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Error"
}
},
"security": [
{
"HTTPBearer": []
}
],
"summary": "Refresh",
"tags": [
"metrics"
]
}
},
"/api/v1/metrics/status": {
"get": {
"description": "When the metric tables were last rebuilt, and whether it failed.",
"operationId": "metrics_status",
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/RefreshLogOut"
},
{
"type": "null"
}
],
"title": "Response Metrics Status"
}
}
},
"description": "Successful Response"
},
"default": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Error"
}
},
"security": [
{
"HTTPBearer": []
}
],
"summary": "Status",
"tags": [
"metrics"
]
}
},
"/api/v1/networth/breakdown": {
"get": {
"description": "The latest day's buckets, plus every account's current balance native and in RUB.",
"operationId": "networth_breakdown",
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/NetWorthBreakdown"
}
}
},
"description": "Successful Response"
},
"default": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Error"
}
},
"security": [
{
"HTTPBearer": []
}
],
"summary": "Breakdown",
"tags": [
"networth"
]
}
},
"/api/v1/networth/series": {
"get": {
"description": "Daily net worth; defaults to the last 365 days.",
"operationId": "networth_series",
"parameters": [
{
"in": "query",
"name": "from",
"required": false,
"schema": {
"anyOf": [
{
"format": "date",
"type": "string"
},
{
"type": "null"
}
],
"title": "From"
}
},
{
"in": "query",
"name": "to",
"required": false,
"schema": {
"anyOf": [
{
"format": "date",
"type": "string"
},
{
"type": "null"
}
],
"title": "To"
}
}
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"items": {
"$ref": "#/components/schemas/NetWorthDay"
},
"title": "Response Networth Series",
"type": "array"
}
}
},
"description": "Successful Response"
},
"default": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Error"
}
},
"security": [
{
"HTTPBearer": []
}
],
"summary": "Series",
"tags": [
"networth"
]
}
},
"/api/v1/rules": {
"get": {
"operationId": "rules_list",
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"items": {
"$ref": "#/components/schemas/RuleOut"
},
"title": "Response Rules List",
"type": "array"
}
}
},
"description": "Successful Response"
},
"default": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Error"
}
},
"security": [
{
"HTTPBearer": []
}
],
"summary": "List",
"tags": [
"rules"
]
},
"post": {
"operationId": "rules_create",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/RuleCreate"
}
}
},
"required": true
},
"responses": {
"201": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/RuleOut"
}
}
},
"description": "Successful Response"
},
"default": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Error"
}
},
"security": [
{
"HTTPBearer": []
}
],
"summary": "Create",
"tags": [
"rules"
]
}
},
"/api/v1/rules/apply": {
"post": {
"description": "Re-run the whole metric refresh so edited rules take effect everywhere at once.",
"operationId": "rules_apply",
"responses": {
"202": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/RefreshLogOut"
}
}
},
"description": "Successful Response"
},
"default": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Error"
}
},
"security": [
{
"HTTPBearer": []
}
],
"summary": "Apply",
"tags": [
"rules"
]
}
},
"/api/v1/rules/stale": {
"get": {
"description": "Enabled rules that matched nothing in the latest refresh: the payee was renamed,\nor the transaction was re-categorised in ZenMoney.",
"operationId": "rules_stale",
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"items": {
"$ref": "#/components/schemas/RuleOut"
},
"title": "Response Rules Stale",
"type": "array"
}
}
},
"description": "Successful Response"
},
"default": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Error"
}
},
"security": [
{
"HTTPBearer": []
}
],
"summary": "Stale",
"tags": [
"rules"
]
}
},
"/api/v1/rules/{rule_id}": {
"delete": {
"operationId": "rules_delete",
"parameters": [
{
"in": "path",
"name": "rule_id",
"required": true,
"schema": {
"title": "Rule Id",
"type": "integer"
}
}
],
"responses": {
"204": {
"description": "Successful Response"
},
"default": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Error"
}
},
"security": [
{
"HTTPBearer": []
}
],
"summary": "Delete",
"tags": [
"rules"
]
},
"patch": {
"operationId": "rules_patch",
"parameters": [
{
"in": "path",
"name": "rule_id",
"required": true,
"schema": {
"title": "Rule Id",
"type": "integer"
}
}
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/RulePatch"
}
}
},
"required": true
},
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/RuleOut"
}
}
},
"description": "Successful Response"
},
"default": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Error"
}
},
"security": [
{
"HTTPBearer": []
}
],
"summary": "Patch",
"tags": [
"rules"
]
}
},
"/api/v1/runway": {
"get": {
"description": "How many months the liquid reserve covers; null before the first refresh.",
"operationId": "cashflow_runway",
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/RunwayOut"
},
{
"type": "null"
}
],
"title": "Response Cashflow Runway"
}
}
},
"description": "Successful Response"
},
"default": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Error"
}
},
"security": [
{
"HTTPBearer": []
}
],
"summary": "Runway",
"tags": [
"cashflow"
]
}
},
"/api/v1/spending/categories": {
"get": {
"description": "Expenses of one month by category, largest first.",
"operationId": "cashflow_spending",
"parameters": [
{
"description": "YYYY-MM; defaults to the latest month",
"in": "query",
"name": "month",
"required": false,
"schema": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "YYYY-MM; defaults to the latest month",
"title": "Month"
}
}
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"items": {
"$ref": "#/components/schemas/SpendingRow"
},
"title": "Response Cashflow Spending",
"type": "array"
}
}
},
"description": "Successful Response"
},
"default": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Error"
}
},
"security": [
{
"HTTPBearer": []
}
],
"summary": "Spending",
"tags": [
"cashflow"
]
}
},
"/api/v1/sync/runs": {
"get": {
"operationId": "sync_runs",
"parameters": [
{
"in": "query",
"name": "source",
"required": false,
"schema": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Source"
}
},
{
"in": "query",
"name": "limit",
"required": false,
"schema": {
"default": 20,
"maximum": 200,
"minimum": 1,
"title": "Limit",
"type": "integer"
}
}
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"items": {
"$ref": "#/components/schemas/SyncRunOut"
},
"title": "Response Sync Runs",
"type": "array"
}
}
},
"description": "Successful Response"
},
"default": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Error"
}
},
"security": [
{
"HTTPBearer": []
}
],
"summary": "Runs",
"tags": [
"sync"
]
}
},
"/api/v1/sync/status": {
"get": {
"operationId": "sync_status",
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"items": {
"$ref": "#/components/schemas/SourceStatus"
},
"title": "Response Sync Status",
"type": "array"
}
}
},
"description": "Successful Response"
},
"default": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Error"
}
},
"security": [
{
"HTTPBearer": []
}
],
"summary": "Status",
"tags": [
"sync"
]
}
},
"/api/v1/sync/{source}": {
"post": {
"operationId": "sync_trigger",
"parameters": [
{
"in": "path",
"name": "source",
"required": true,
"schema": {
"title": "Source",
"type": "string"
}
}
],
"responses": {
"202": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SyncJobOut"
}
}
},
"description": "Successful Response"
},
"default": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Error"
}
},
"security": [
{
"HTTPBearer": []
}
],
"summary": "Trigger",
"tags": [
"sync"
]
}
},
"/api/v1/transactions": {
"get": {
"description": "One page of transactions, newest first, with RUB amounts at each own date's rate.",
"operationId": "transactions_list",
"parameters": [
{
"in": "query",
"name": "from",
"required": false,
"schema": {
"anyOf": [
{
"format": "date",
"type": "string"
},
{
"type": "null"
}
],
"title": "From"
}
},
{
"in": "query",
"name": "to",
"required": false,
"schema": {
"anyOf": [
{
"format": "date",
"type": "string"
},
{
"type": "null"
}
],
"title": "To"
}
},
{
"in": "query",
"name": "account_id",
"required": false,
"schema": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Account Id"
}
},
{
"in": "query",
"name": "category_id",
"required": false,
"schema": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Category Id"
}
},
{
"in": "query",
"name": "flow_type",
"required": false,
"schema": {
"anyOf": [
{
"$ref": "#/components/schemas/FlowType"
},
{
"type": "null"
}
],
"title": "Flow Type"
}
},
{
"description": "substring of payee or comment",
"in": "query",
"name": "q",
"required": false,
"schema": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"description": "substring of payee or comment",
"title": "Q"
}
},
{
"in": "query",
"name": "include_deleted",
"required": false,
"schema": {
"default": false,
"title": "Include Deleted",
"type": "boolean"
}
},
{
"in": "query",
"name": "page",
"required": false,
"schema": {
"default": 1,
"minimum": 1,
"title": "Page",
"type": "integer"
}
},
{
"in": "query",
"name": "page_size",
"required": false,
"schema": {
"default": 50,
"maximum": 500,
"minimum": 1,
"title": "Page Size",
"type": "integer"
}
}
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/TransactionPage"
}
}
},
"description": "Successful Response"
},
"default": {
"content": {
"application/problem+json": {
"schema": {
"$ref": "#/components/schemas/Problem"
}
}
},
"description": "Error"
}
},
"security": [
{
"HTTPBearer": []
}
],
"summary": "List",
"tags": [
"transactions"
]
}
}
},
"servers": [
{
"url": "/"
}
]
}