apply_clusters
ChatGPTApply a MERCHANT to whole UNKNOWN-merchant clusters by cluster_key — WITHOUT ever listing transaction ids. Cleanup is applied by key: each assignment carries a cluster_key (verbatim from cluster_unknown_transactions) plus the merchant, and the server re-derives that cluster's transactions and bulk-updates them. A whole confirmed wave fits in ONE call, so no long id list is ever serialized. Clusters apply MERCHANTS ONLY — categories are not set here. assignments: list of objects, each {cluster_key + exactly ONE merchant target}: - cluster_key: the cluster's opaque ASCII handle from cluster_unknown_transactions, verbatim. It is the match key — an ASCII token, so unlike the description it can't be mangled by accents/encoding on the round-trip. (The human "description" may also be included; it is only a fallback match when cluster_key is omitted, and it can miss on accented text, which the ASCII cluster_key cannot.) - merchant_classifier_id: link to a LIBRARY merchant (preferred for a library match — the backend attaches a per-space merchant with logo/mapping). - merchant_account_id: the account id (a UUID) of an existing/local CUSTOMER merchant — a create_merchant result, or the id resolved for the internal- transfer merchant via search_merchants(system_preset="INTERNAL_TRANSFER_MERCHANT"). The value is an account UUID: a system-preset NAME (the literal "INTERNAL_TRANSFER_MERCHANT") in this field is rejected with a 400. Mutually exclusive with merchant_classifier_id. Provide exactly one merchant target per assignment. As a special case, an assignment {all_transfers: true, merchant_account_id: <unknown_transfers.merchant_account_id from cluster_unknown_transactions>} sweeps EVERY unknown TRANSFER onto the internal-transfer merchant in one bulk update (by movement type, not by cluster_key); it belongs in one call per cleanup, not in every wave. Returns {results:[{cluster_key, description, matched (transactions currently found for that cluster), updated (how many the backend actually CHANGED)}]}. The counts are NOT pass/fail: updated skips transactions already in that state, so updated < matched (even 0) just means "already set", not a failure; matched: 0 (with a note) means nothing still-unknown matched — already merchant-assigned and gone, or a stale key — a fresh cluster scan carries the current cluster_key. Progress is readable from a fresh cluster scan, where assigned merchants drop out; these counts do not carry it.
archive_account
ChatGPTArchive (archived=True, the default) or unarchive (archived=False) an account. Use it to hide an account the user has stopped using without deleting its history, or to bring one back. Read-modify-write, so nothing else on the account changes. Synced accounts can't be archived.
bulk_update_transactions
ChatGPTApply the SAME change to many transactions at once (one atomic batch). Main use: relabelling a whole group of "Unknown merchant" transactions to one merchant, or recategorizing them, as one call rather than one call per row. Pass transaction_ids (from search_transactions) plus exactly the field(s) to change: - merchant_account_id: set merchant by account id (from search_merchants merchant_account_id, or create_merchant). - merchant_classifier_id: set merchant from the library (classifier id). (merchant_account_id and merchant_classifier_id are mutually exclusive — pass at most one.) - category_id: set the category on every transaction — an EXPENSE or INCOME category from list_categories ONLY. A DEBT/REWARD counter-account is rejected here ("Account is not category"); those are settable one transaction at a time through update_transaction. - tag_id: add a tag (from search_tags / create_tag). - mark_reviewed: true to clear the "Needs review" flag on all of them. - mark_as_family: true marks a transaction as shared family spending, false un-marks it. Only when the space is in a family: otherwise 400 FAMILY_NO_ACTIVE_FAMILY_EXISTS. WARNING: un-marking removes the copy from the partner's space, and their category, split or debt allocation on that copy goes with it. Note: category and tag changes skip split transactions, so the returned count can be less than len(transaction_ids); merchant changes apply to all. A family change is skipped the same way on a split, and also on a transaction with no expense/income leg (a plain transfer or top-up), which cannot carry the status — so the call answers 200 having changed nothing. The count says how many transactions changed, not which field changed on them; a family change is readable on the transaction itself, which reports family and the per-leg family_status. Returns {"updated": <count>}.
change_account_type
ChatGPTChange an existing account's type. new_type is a finerd AccountType. The server only allows changes among the asset types BANK_ACCOUNT, CASH, STOCKS_AND_CRYPTO, REAL_ESTATE, VEHICLE, FURNITURE, ELECTRONICS, VALUABLE_ITEMS, OTHER_PROPERTY — plus TO DEBT (which requires the account hold a SINGLE currency). System, synced, and deleted accounts can't be retyped; nor can the space's LAST cash account, nor an account with money-on-the-way links (typical after transfers — ACCOUNT_HAS_RELATED_MONEY_ON_THE_WAY). A forbidden change raises with the reason and will not succeed on retry.
cluster_unknown_transactions
ChatGPTGroup the space's UNKNOWN_MERCHANT transactions into ranked description clusters — server-side, in ONE call — so you can bulk-assign merchants and categories without paging and grouping thousands of rows yourself. Use it for any space with a large "Unknown merchant" backlog — after a file import, or whenever synced transactions arrived without a matched payee. It resolves the UNKNOWN_MERCHANT itself, scans all its transactions, and groups them by a conservatively normalized description (lowercased, punctuation and standalone number/date tokens stripped — so "ATB 1234" and "ATB 5678" land together; digits inside a word are kept). - min_count: ignore clusters smaller than this (default 3 — clusters of 1-2 transactions aren't worth the round-trip, so the tail is left for the final best-effort sweep). - top: return at most this many clusters, largest first (default 30). Returns {total_unknown, scanned, truncated (true if the scan hit its page cap — more may remain), unclusterable (null/opaque/all-number descriptions that can't be grouped by name), progress {still_unknown, still_unknown_amount, significant_remaining, transfers_remaining, amount_bar, done}, unknown_transfers {count, merchant_account_id} (ALL unknown TRANSFERs take this one merchant — sweep them with apply_clusters([{all_transfers:true, merchant_account_id}]); no naming), clusters_total, clusters:[{cluster_key (an opaque ASCII handle; apply_clusters matches on it, not on the description), description (a representative raw form to read), count, amount, max_txn, type, category (a hint to sanity-check that a same-name merchant is actually the right business)}], and — ONLY once done — a compact tail:[{cluster_key, description, count}] of sub-significant one-offs for a single best-effort naming sweep}. clusters holds only SIGNIFICANT business groups (count >= min_count OR a single txn >= amount_bar). progress.done DRIVES the loop: true only when no significant business cluster remains AND transfers are swept. Clusters carry NO transaction ids; none are ever serialized. Each cluster includes a cluster_key identifying the transaction group. Clustering does not modify transactions or return transaction id lists. Grouping is deliberately conservative — it may over-split one merchant into two near-identical clusters rather than risk merging two different payees, and two near-identical clusters can share one target in a single apply_clusters call.
create_bank_account
ChatGPTCreate a manual BANK_ACCOUNT (checking / savings / etc., not bank-synced). - name: display name (e.g. "Chase Checking", "Monobank UAH"). - asset_id: money currency, lowercase ISO. - initial_amount: opening balance. This creates a manual bank account. To connect a bank-synced account (Plaid / Mono), users must use the in-app bank-connect flow — the MCP does not initiate external bank linking.
create_bill
ChatGPTStep 2 of adding a receipt from a file: starts recognition of the files uploaded under request_id (from get_bill_upload_urls, after the PUTs succeeded). Returns immediately; recognition runs in the background. request_id is the bill id. The result appears as a bill in get_bill after ~10-30 s: merchant, date, total, line items with categories, and a transaction_id when a matching transaction was found or one was created for it. Until then get_bill reports the bill as not found. A bill that is still missing after ~90 s means the files were not recognized as a receipt (or were never uploaded): nothing is created, and there is no other signal. Starting recognition for a request_id whose files were not uploaded produces the same outcome.
create_budget
ChatGPTCreate the budget for one month — one budget per (space, month). Use it when the user wants a monthly spending or income plan, or wants to re-enter one they keep elsewhere. - month: budget month, "YYYY-MM" or "YYYY-MM-DD" (normalized to the 1st). - total_expense / total_income: overall monthly limits (>= 0). - expense_categories / income_categories: optional per-category allocations, each {"category_id": <id from list_categories>, "amount": <positive number>}. The per-side sum may be <= the matching total; the remainder is the unbudgeted ("Other") envelope. Allocations are keyed by finerd category id, not by category name. Returns the saved budget (month, kind, budgeted/actual totals, category counts).
create_cash_account
ChatGPTCreate a manual CASH account (wallet / cash on hand). - name: display name (e.g. "Wallet USD", "Cash UAH"). - asset_id: money currency, lowercase ISO ("usd", "uah", "eur"). - initial_amount: opening balance (default 0). The server records this as a verified balance at creation so the account has a starting point. - off_balance: True hides the account from net-worth (rare; default False). Holds ordinary money that is not bank-synced and does not track points.
create_category
ChatGPTCreate a new expense/income category, for a category the space does not have yet — one the user names to categorize a transaction, set a budget, or clear uncategorized items. Categories are not deduplicated server-side: a synonym of an existing category of the same type is created as a second, separate category, and both then appear in the tree and in budgets. - category_type: "EXPENSE" or "INCOME". Must match the parent's type when a parent is given. - parent_id: optional id of a TOP-LEVEL category to nest under — that category must itself have no parent_id, since the tree is two levels deep. Omit for a new top-level category. Returns the created category (id, name, type, parent_id).
create_complex_transaction
ChatGPTCreate a complex (split) transaction with multiple FROM and/or TO journals. Use this for splits between several categories or multi-account flows. Each entry in from_journals / to_journals is: {"account_id": str|null, "amount": number, "accrual_month": str|null (optional), "tags": list[str]|null (optional)} Total of FROM amounts must equal total of TO amounts. SINGLE-ASSET, with one exception. Every leg is in the transaction's asset_id; a journal entry has no asset of its own. The exception: one FROM leg and one TO leg carrying different asset_ids — buying 18 shares for 1080 CAD, an FX exchange, a points transfer. That is a transfer, and it is booked as one for you; no need to switch tools. That shortcut needs the plain shape — exactly one leg each way, no merchant, no per-leg tags or accrual_month. Anything else with mixed assets (a multi-leg split across currencies) cannot be created in one call: this tool takes a single asset for the whole transaction, and a per-leg asset_id is only accepted by a restructure. Patterns: - Split expense: from = [{account_id: <bank>, amount: 100}], to = [{account_id: <category A>, amount: 60}, {account_id: <category B>, amount: 40}] - Split income: from = [{account_id: <category A>, amount: 700}, {account_id: <category B>, amount: 300}], to = [{account_id: <bank>, amount: 1000}] - Transfer with fee: from = [{account_id: <src>, amount: 1010}], to = [{account_id: <dst>, amount: 1000}, {account_id: null, amount: 10}] - SPREAD ACROSS MONTHS (any of: "розбий цю витрату по місяцях", "rozkydai pidpysku na kvartal", "split this payment over Jan/Feb/Mar", "amortize this annual fee across 12 months", "разнеси по месяцам", "spread this prepayment over 6 months"): from = [{account_id: <bank>, amount: 300}], to = [ {account_id: <Subscriptions>, amount: 100, accrual_month: "2026-01"}, {account_id: <Subscriptions>, amount: 100, accrual_month: "2026-02"}, {account_id: <Subscriptions>, amount: 100, accrual_month: "2026-03"}, ] The cash leaves the bank once but the expense is recognized in three different months. Same trick works on FROM legs for spreading recognized income across months. When account_id is null, the server fills in the space's default "Uncategorized Expense" or "Uncategorized Income" account, depending on whether the journal is on the FROM or TO side of a non-money flow. There is no way to leave a journal completely uncategorised — pass an explicit category/account if you need a specific bucket. Per-entry optional fields: - accrual_month: per-leg booking month for accrual reports — the month this leg conceptually belongs to, independent of the transaction date. "YYYY-MM" / "YYYY-MM-01" / "" (clear). - tags: list of tag IDs from search_tags applied to that leg. asset_id is the ISO currency code (e.g. "uah", "usd"). date is optional ISO datetime; server uses now() if omitted. For the merchant pass exactly one of: - merchant_classifier_id: from search_merchants result (preferred) - merchant_account_id: from search_merchants result (use only when merchant_classifier_id is null in the search result)
create_debt_account
ChatGPTCreate a DEBT account (credit card, mortgage, IOU between people). Direction (required, default "owed_to_me"): - "owed_to_me" — someone owes the USER (friend who borrowed, unpaid invoice from a customer). UI: "Owes me <name>", green. - "i_owe" — the USER owes (credit card, mortgage, loan, friend who lent to user). UI: "I owe <name>", orange. initial_amount is the outstanding balance, always a positive magnitude. The MCP flips the stored sign internally based on direction. A negative value is rejected. merchant_classifier_id links the debt to a business merchant (find via search_merchants — e.g. Chase library merchant id for a credit card). Independent of direction. For human counterparties leave it empty and use email / phone_number / link for contact info. - name: display name (e.g. "Chase Sapphire Reserve", "Andriy IOU"). - asset_id: money currency, lowercase ISO ("usd", "uah", "eur").
create_merchant
ChatGPTCreate a per-space (customer) merchant for a payee that has no global-library entry. A merchant created here carries no balance. Passing merchant_classifier_id links it to a library entry, which brings that entry's logo and mapping with it; without one the merchant stands alone. When the library already holds an entry under the same name and no classifier id was given, the response carries a library_note naming it: an unlinked duplicate splits that payee's history, while a name shared by a different business is a legitimate standalone merchant. - name: merchant display name. - merchant_classifier_id: library classifier id, as returned by a merchant search. Required when a library entry for this business exists. - logo_url: a logo image URL (Icon type LINK; per-space, adds NOTHING to the library). It is validated server-side: a dead link or a non-image is dropped and reported in logo_note, so a bad URL cannot produce a broken icon. A site's own logo, its favicon or a Wikimedia image works; aggregator endpoints are gated and get dropped. - website: the business's official site URL (stored as debtProperties.link). Per-space only; adds NOTHING to the shared library. A guessed "<brand>" domain often resolves to a parked or for-sale page. A name-only merchant shows a blank placeholder until a logo is supplied. The returned merchant is assignable immediately: its id is the merchant_account_id that transaction tools accept.
create_monthly_points_accrual
ChatGPTCreate ONE monthly accrual transaction split across N reward categories. The shape a points-activity CSV's earnings import takes: earnings collapse to one row per (month, item), and those items become the line items of a single monthly transaction — like a receipt. - lines: a list of {"category_id": <REWARD_CATEGORY id>, "amount": <positive points>} objects. Map each CSV earning description to a reward category (list_categories type_filter="REWARD_CATEGORY") and sum identical ones. - asset_id: the reward account's point asset id, verbatim. - kind: "INCOME" for earnings (default) or "EXPENSE". - date: ISO YYYY-MM-DD — use the row's date (last day of the month). - accrual_month: "YYYY-MM" — set to the accrual month for reports. Returns the saved transaction with its split journals.
create_reward_account
ChatGPTCreate a loyalty/reward (points or miles) account for a program. A reward account holds a point currency, not money. It must be created from a loyalty-program library merchant — find it with search_merchants and use a result that has reward_asset_ids (e.g. World of Hyatt → "HYT"). - asset_id: the program's point asset id (from reward_asset_ids), verbatim. - merchant_classifier_id: the program's library merchant id (preferred). - initial_amount: opening point balance. With no prior data it is the program's current balance; where history is imported afterwards, a verified balance recorded later is what reconciles the account. A space usually holds at most one account per loyalty program.
create_reward_pnl_transaction
ChatGPTCreate a single-category points earning or expense on a reward account. Records one point movement against one reward category. A whole month of earnings spread across several categories is a different shape and is not what this tool records. - amount: positive point magnitude (sign is conveyed by kind). - asset_id: the reward account's point asset id, verbatim. - reward_category_id: a REWARD_CATEGORY id (from list_categories with type_filter="REWARD_CATEGORY"). - kind: "INCOME" (points earned) or "EXPENSE" (points spent/lost). - date: ISO YYYY-MM-DD booking date. - accrual_month: "YYYY-MM" booking month for reports (optional).
create_reward_redemption
ChatGPTRecord a points redemption — one per booking (e.g. an award hotel stay). Points leave the reward account in exchange for value (the cash-equivalent of what was redeemed). - points_amount / points_asset_id: points spent (positive magnitude). - value_amount / value_asset_id: the cash value of the redemption, in a money currency (asset id lowercase ISO, e.g. "usd"). When it is unknown, a cents-per-point valuation yields an estimate rather than a recorded figure. - value_category_id: EXPENSE category id for the cash-value side (e.g. "Travel", "Lodging", "Hotels"). Find via list_categories with type_filter="EXPENSE". If omitted, the server records it against "Uncategorized Expense". - merchant_id / merchant_classifier_id: where it was redeemed (the hotel) — find via search_merchants. Each redemption usually has its own merchant. - other_account_id / other_amount / other_asset_id: optional cash co-pay leg, for "points + cash" bookings (e.g. 50k pts + $100). - date: ISO YYYY-MM-DD — the real booking date.
create_tag
ChatGPTCreate a new tag for tagging transactions. Tags are not deduplicated server-side: a near-duplicate of an existing tag is created as a second, separate tag. Tag search matches by PREFIX only, so "no results" is not evidence that a tag does not already exist under another wording.
create_transaction
ChatGPTCreate a simple expense or income transaction. transaction_type: EXPENSE, INCOME, EXPENSE_REFUND, DEBT_PAY, or DEBT_RECEIVE. amount: positive number. asset_id: ISO currency code (e.g. "usd", "eur", "uah") — lowercase. account_id: bank/cash account ID from list_accounts (optional). category_id: category ID from list_categories (optional; ignored for DEBT_PAY / DEBT_RECEIVE). DEBT_PAY / DEBT_RECEIVE: the counterparty is the MERCHANT — pass the debtor's merchant_account_id (from search_merchants or list_accounts' DEBT account). Without a merchant the call is rejected. date: posting date "YYYY-MM-DD" (optional; defaults to today). The amount is valued in the primary currency at that day's rate. accrual_month: booking month "YYYY-MM" (optional). For the merchant pass exactly one of: - merchant_classifier_id: from search_merchants result (preferred) - merchant_account_id: from search_merchants result (use only when merchant_classifier_id is null in the search result)
create_transaction_from_bill
ChatGPTCreate a NEW transaction from a recognized receipt (bill) and attach the receipt to it. For a bill in list_bills(exclude_assigned=true) that has no matching transaction — a cash purchase, a receipt from an account that is not synced. When a matching transaction already exists (same amount, same day, typically a bank import), this tool produces a duplicate of it; attaching with update_transaction(bill_id=...) is the non-duplicating path, and search_transactions shows whether such a transaction exists. The server builds the transaction from the bill: date and total from the receipt, one expense leg per category found in its items (item↔leg links kept), merchant from the receipt. The money leg is the account the server guesses — the account last used with this merchant, else the wallet — so pass account_id (from list_accounts) whenever the user says where the money came from. The response includes source_account, identifying the account used for the transaction. - bill_id: from list_bills / get_bill. Refused if the bill is already on a transaction (the message names it) or has no items (not recognized). - account_id: the money account the receipt was paid from. Replaces the money leg only; the category split stays as recognized. - debt: true books the bill against its merchant as a DEBT counterparty (someone paid for you / you owe them) instead of a money account. Only works when the receipt's merchant is a known person/merchant; with an unknown merchant the server falls back to a money account — check source_account in the response. - date: ISO date or datetime, overrides the receipt date; the amount is valued at that day's rate. - comment: optional note.
create_transfer
ChatGPTCreate a transfer between two of the user's accounts. It records the movement in the finerd ledger only: finerd holds no funds and initiates no payment, so nothing moves at any bank, broker or exchange. Same-asset (the common case): just pass amount + asset_id; the receiving leg mirrors the sending leg. Cross-asset transfer: also pass to_amount + to_asset_id. Use this for - BUYING OR SELLING A SECURITY OR CRYPTO — money account out, investment account in, the two legs in different assets and different amounts. "bought 18 shares of EBIT.TO for 1080 CAD" is account_from=<bank>, amount=1080, asset_id="cad", account_to=<investment>, to_amount=18, to_asset_id="ebit.to_xtse". Selling is the same the other way round. This is NOT a complex transaction — that one is single-asset and will reject the two amounts; - point-program transfers between reward accounts (e.g. Bilt → Hyatt: asset_id="bilt_pts", to_asset_id="hyt_pts"; usually 1:1 ratio so to_amount = amount, but some partners have other ratios); - currency-FX between money accounts in different ISO currencies. Each leg is debited/credited in its own asset; the server-side TransferTransactionFactory accepts independent assetIds per leg. account_from / account_to: account IDs from list_accounts. amount, to_amount: positive numbers. asset_id, to_asset_id: asset ids — lowercase ISO for money (e.g. "usd"), program-specific for points (e.g. "hyt_pts", "bilt_pts"); discover via reward_asset_ids on search_merchants or asset_id on get_account_balances. An uppercase code is normalised automatically. date: ISO date or datetime of the transfer, defaults to now. Pass it for anything historical — importing past trades, backfilling. A cross-asset transfer is valued at the rate OF THAT DAY, so a trade booked with the wrong date is also valued at the wrong rate. Passing it here also saves a follow-up update_transaction per row.
delete_transaction
ChatGPTDelete a transaction permanently. Get transaction_id from search_transactions or get_transaction. Refused (400) when any leg is bank-posted ("synced": true in get_transaction — SYNCED_TRANSACTION_CANNOT_BE_DELETED): a bank import is the bank's record. Also refused on the partner-owned copy of a family transaction (FAMILY_SYNCED_TRANSACTION_CANNOT_BE_DELETED). A pending (not yet posted) bank row can be deleted.
get_account_balances
ChatGPTList the user's accounts together with their current balances. search: case-insensitive SUBSTRING match on the account name. Archived accounts are not returned. Each account includes amount and currency for each balance entry. Use this when the user asks about balances, totals, or net worth. Pagination: returns up to page_size accounts (default 500, max 1000) plus "has_more". If "has_more" is true, call again with page incremented (page=1, 2, …) and combine the results to cover every account.
get_asset_rates
ChatGPTGet Finerd's canonical conversion rate from each asset to a base money currency, on a given date (defaults to today). For reward assets this is the cents-per-point estimate (multiplied by the base unit) Finerd uses internally — e.g. hyt_pts → 0.019 means 1 Hyatt point ≈ $0.019 ≈ 1.9 cpp. Use this for the points-import workflow when computing a redemption's value_amount = points * rate. Also works for money↔money FX: asset_ids=["uah"], base_asset_id="usd" returns the current UAH→USD rate — use it to convert amounts between currencies (e.g. a budget expressed in one accounting currency into another). - asset_ids: list of asset ids (e.g. ["hyt_pts", "ap_pts"], or money codes like ["uah"]). - base_asset_id: lowercase ISO money asset (default "usd"). - date: ISO YYYY-MM-DD; defaults to today. Returns one entry per asset_id with {asset_id, rate, date}.
get_bill
ChatGPTGet full details for a single bill, including line items. Each item has a name, quantity, unit price, and total price. Get the bill_id from list_bills results.
get_bill_upload_urls
ChatGPTStep 1 of adding a receipt from a file: reserves a bill id (request_id) and returns one presigned upload URL per file. The file bytes are then PUT to each url with the returned headers (raw body, no multipart, no auth) — by whoever holds the file: a shell (curl), a script, an agent runtime with network access. Files pasted into a chat cannot be forwarded here; the upload has to come from an environment that can read the file. URLs are valid until expires_at (about an hour). Step 2 is create_bill with the same request_id. file_names: 1-10 names with a png, jpg, jpeg, pdf or heic extension, e.g. ["receipt.jpg"]; several pages of one receipt go in one call. Names are returned in uploads[].file_name so each file goes to its own url.
get_budget
ChatGPTGet the budget for one month — planned vs actual execution. - month: "YYYY-MM" or "YYYY-MM-DD" (normalized to the 1st of the month). Returns: kind (EXPLICIT = user-set, INHERITED = carried from a prior month, HISTORICAL = a past month with no explicit plan); overall expense and income, each as {budgeted, effective (after one-off budget events), actual (realized this month)}; and per-category rows (category, budgeted, effective, actual). If no budget is set for that month the result is {"month": ..., "budget": null} — that means no budget exists, not a failure. Answers "how am I doing against my budget", and reads the current plan before editing. When a budget exists, the response carries currency — the space's primary currency, which every figure here is denominated in; that unit applies to every amount in the response and to no amount outside it.
get_cashflow_report
ChatGPTCash flow report (inflows vs outflows) for a date range. Money that MOVED, both sides of every transfer — not the same thing as the expense/income reports, which count P&L only. Use it for "where did the money come from and go to", debt and investment movements included. date_from/date_to are ISO dates (YYYY-MM-DD). granularity: MONTH, QUARTER, YEAR, CUSTOM, DAY. DAY is the only day-level figure Finerd has, and it answers what people usually mean by "spending per day" / "витрати по днях": how much money left the money accounts each day, outflow_by_type split into CATEGORIES / DEBTS / INVESTMENTS, inflow likewise, transfers between the user's own accounts excluded. It is cash movement, not the expense (P&L) report — that one books by accrual month and cannot be cut finer than a month, and the two differ when a purchase is booked to another month, an expense is accrued but unpaid, or a debt repayment leaves the account (cash out, not an expense). The response states this in basis so the difference can be passed on. Up to ~3 months per call; two calls give a month-over-month day comparison. No expand/include_non_cash at DAY. Each side is broken down by flow type, and each flow type lists its own categories or accounts under children. The five flow types, named after what sat on the other side of the movement: - CATEGORIES — ordinary spending and income - DEBTS — debts, loans and mortgages (borrowing, lending, repayments) - INTERNAL_TRANSFERS — the user's own money accounts: cash, bank, money on the way. Only the leg that left the selected accounts shows up here. - INVESTMENTS — real estate, vehicles, stocks and crypto, electronics, furniture, valuables, other property - OTHER — equity A flow type with no movement in the period is simply absent. expand: one of those five names, to break that one bucket down to its leaf categories as well, e.g. expand="CATEGORIES". This is the fullest of the three levels and the largest response. by_interval (per-period totals) appears when the range spans more than one period at the chosen granularity. include_non_cash: movements between income/expense and debt or investment accounts that never touch a money account — accrued loan interest, reinvested income. Counted by default, same as the app. Pass False for "how much money actually moved", which is what a user comparing the report against their bank statement means. This is the app's "Non-cash movements: Show / Hide" filter. The response carries currency — the space's primary currency, which every figure here is denominated in; that unit applies to every amount in the response and to no amount outside it.
get_expense_report
ChatGPTAggregated expense report for a date range, grouped by dimension. USE THIS for any question about a period — a month, a week, a few days — because it is the only source of a correct total: it aggregates server-side and converts to one currency. A page of transaction rows is not a total: those rows are per-currency and do not sum to this figure. MONTHLY RESOLUTION: date_from/date_to are ISO dates (YYYY-MM-DD) but the range is WIDENED to whole months — that is the only period this report exists for, and the app's own picker offers months and nothing finer. Ask for 24-31 July and you are answered for the whole of July — the period the response covers can be wider than the one asked for. Day-level figures exist only as cash movement: the cash-flow report at granularity DAY gives per-day outflows from the money accounts (what "spending per day" usually means), which is cash out rather than P&L and is labelled as such. group_by: CATEGORY, MERCHANT, PURPOSE, PARTNER. PURPOSE splits FAMILY (shared family spending) vs PERSONAL; PARTNER splits ME vs PARTNER — WHO PAID in a family, not a business counterparty. Family spending by category or merchant is a query_report question (family != NOT_FAMILY, group category). Optional filters (all act as INCLUDE allowlists): - category_ids: from list_categories - merchant_ids: merchant_classifier_id or merchant_account_id from search_merchants - tag_ids: from search_tags Returns total spending and breakdown per dimension with prior-period comparison. NOTE: when group_by=CATEGORY the breakdown rolls up to top-level parent categories (e.g. "Food & Essentials" instead of the leaf "Coffee & Snacks"). Leaf-category detail is not in this breakdown; grouping by MERCHANT, or a transaction search filtered to the leaf category, carries it. The response carries currency — the space's primary currency, which every figure here is denominated in; that unit applies to every amount in the response and to no amount outside it.
get_income_report
ChatGPTAggregated income report for a date range, grouped by dimension. USE THIS for any question about a period — a month, a week, a few days — because it is the only source of a correct total: it aggregates server-side and converts to one currency. A page of transaction rows is not a total: those rows are per-currency and do not sum to this figure. MONTHLY RESOLUTION: date_from/date_to are ISO dates (YYYY-MM-DD) but the range is WIDENED to whole months — that is the only period this report exists for, and the app's own picker offers months and nothing finer. Ask for 24-31 July and you are answered for the whole of July — the period the response covers can be wider than the one asked for. Day-level figures exist only as cash movement: the cash-flow report at granularity DAY gives per-day outflows from the money accounts (what "spending per day" usually means), which is cash out rather than P&L and is labelled as such. group_by: CATEGORY, MERCHANT, PURPOSE, PARTNER. PURPOSE splits FAMILY (shared family spending) vs PERSONAL; PARTNER splits ME vs PARTNER — WHO PAID in a family, not a business counterparty. Family spending by category or merchant is a query_report question (family != NOT_FAMILY, group category). Optional filters (all act as INCLUDE allowlists): - category_ids: from list_categories - merchant_ids: merchant_classifier_id or merchant_account_id from search_merchants - tag_ids: from search_tags Returns total income and breakdown per dimension with prior-period comparison. NOTE: when group_by=CATEGORY the breakdown rolls up to top-level parent categories. Leaf-category numbers are not in this breakdown; a transaction search filtered to the leaf category carries them. The response carries currency — the space's primary currency, which every figure here is denominated in; that unit applies to every amount in the response and to no amount outside it.
get_networth_report
ChatGPTNet worth report over time (assets, liabilities, equity). date_from/date_to are ISO dates (YYYY-MM-DD), both optional — omit them for the current net worth and they default to the last 12 months through today. currency: ISO code (e.g., USD, EUR, UAH) — values are converted to this. Omit it and the report is denominated in the space's own primary currency — the same currency the expense, income and cash-flow reports are already denominated in server-side; pass one only when the user asked to see their net worth in a different currency. This response echoes the currency it was answered in; that unit applies to every amount in it. granularity: MONTH, QUARTER, or YEAR (CUSTOM is not supported here).
get_recurrence
ChatGPTRead one recurring payment by UUID, including amount, currency, frequency and status. Includes next expected date and linked-payment statistics. Expected dates are forecasts.
get_recurrence_calendar
ChatGPTRead recurring payment cycles for an inclusive YYYY-MM-DD range of at most one year. Includes past and future cycles for DETECTED and CONFIRMED recurrences. expected_date is a forecast; paid means a linked transaction matched this cycle. paid=false alone does not mean overdue. Amounts retain their own currency.
get_session_info
ChatGPTReport this MCP session's capabilities. Returns {role, can_write}. role is MCP_READ (read-only) or MCP_READ_WRITE; can_write is true when write tools (create/update/bulk) are available this session. A read-only session reaches every data tool but has no write tool available to it; write access is chosen when the connector is authorised.
get_space_primary_currency
ChatGPTThe space's declared primary (base) currency — the currency that reports, net worth and all cross-currency totals are denominated in. Returns {currency, asset_id}, e.g. {"currency": "USD", "asset_id": "usd"}. Useful when an amount's currency is otherwise unlabeled: income/expense reports return bare numbers in this currency. Two spaces can declare different primary currencies, so a figure is only comparable across spaces once both are read.
get_transaction
ChatGPTGet full details for a single transaction, including journal entries. Journals show the FROM/TO accounts with amounts, currencies, categories, and tags. Use this when you need detailed breakdowns (e.g., split transactions, multi-currency, or per-account amounts). Get the transaction_id from search_transactions results.
get_turnover_report
ChatGPTTurnover (statement summary) for ONE account over a date range: opening balance, total inflow, total outflow, net movement, closing balance. Use it for "how did account X change between two dates", "what was the balance on date D" (ask for a range ending on D and read closing), and for reconciling against a bank statement. It is day-accurate for any dates — unlike the expense/income reports, which only resolve whole months. account_id: exactly one account, as returned by the account listing — the report covers a single account per call. Bank, cash, debt, investment and reward accounts all work; categories are not accounts. date_from/date_to are ISO dates (YYYY-MM-DD), inclusive. asset_id: optional filter to one asset of a multi-currency account, lowercase ("usd", "uah"). by_asset has one row per currency the account holds: figures in that currency, plus *_primary mirrors converted to the space's primary currency (the currency field) when available. inflow/outflow are gross, net is their difference and equals closing - opening. rows_count is how many transactions moved the account; this report carries the totals only, not the rows themselves.
list_accounts
ChatGPTList the user's financial accounts (bank accounts, wallets, etc.). Use search for a case-insensitive SUBSTRING match on the account name. Archived accounts are not returned. Returns account IDs, names, and types. Use the IDs to filter search_transactions by account. Pagination: returns up to page_size accounts (default 500, max 1000) plus "has_more". If "has_more" is true, call again with page incremented (page=1, 2, …) and combine the results to get every account.
list_asset_rate_providers
ChatGPTList every rate provider available for an asset, their current rate, and which one is selected for this space. Providers: THE_POINTS_GUY, AWARD_WALLET, NERD_WALLET, BANK_RATE, FREQUENT_MILER, ONE_MILE_AT_A_TIME, UPGRADED_POINTS, FINERD_AVERAGE (aggregate), CUSTOM (user-set). The set of valuations that exist for the asset, which is what a change of preferred provider or a custom rate is chosen from.
list_bills
ChatGPTList the user's bills (receipts, invoices). Use date_from/date_to for date ranges (ISO format: YYYY-MM-DD). Use search_string for a case-insensitive SUBSTRING match over the merchant name and the bill's own text: line-item names, address, order id and the whole-number total — so "молоко", an order number or "1234" all find a receipt. Set exclude_assigned=True to show only bills not yet linked to a transaction. Returns bill IDs, dates, amounts, merchants, and line items.
list_categories
ChatGPTList the user's transaction categories. Use type_filter to filter by type: "EXPENSE", "INCOME", or "REWARD_CATEGORY". Reward categories are the dedicated buckets for loyalty points (e.g. base points, bonuses, redemptions) — use them as reward_category_id when creating reward/points transactions. Use search for a case-insensitive SUBSTRING match on the category name as the user named it — often in their own language ("Ресторани", not "restaurant"); omit it to list them all. Archived categories are not returned, so a name missing from a search may still exist archived. Returns category IDs, names, and parent_id when the category is nested under a top-level parent (parent_id absent = top-level). Use the IDs to filter search_transactions; use parent_id to resolve a category's parent and to find same-type top-level parents when creating a new category with create_category. Also includes system_preset for finerd's built-in system categories (e.g. UNCATEGORIZED_EXPENSE / UNCATEGORIZED_INCOME); absent for ordinary categories — use it to spot and exclude system buckets. The same ids are what update_categories takes to rename categories; system categories cannot be renamed.
list_connected_institutions
ChatGPTList the banks already connected to this space, with how many accounts each contributed and the connection's sync state. This reflects imported accounts, so it stays empty until the user has finished choosing which accounts to import in the browser — an empty result means the connection is unfinished, not that it failed. One row per connection: the same bank connected twice appears twice.
list_spaces
ChatGPTList the user's available spaces. Every other tool takes a space_id, and these are the ids it accepts. Returns space IDs, names, and whether each is the personal space.
list_verified_balances
ChatGPTList recorded verified balances for an account (most recent first). Useful to see the last reconciled balance/date for a reward account before re-syncing. Each entry has date, amount, currency, type, correction_amount.
mark_transaction_reviewed
ChatGPTClear the "Needs review" flag on a transaction. Use this when the user says things like "позначити цю транзакцію як переглянуту", "прибери needs review", "mark this as reviewed", "clear the review flag". Pairs with search_transactions(needs_review=True) which lists the transactions currently in the "Needs review" inbox. Get transaction_id from search_transactions or get_transaction. CAVEAT — the flag may not actually clear if the transaction still has unresolved review reasons. The endpoint returns 200 either way (no body), and whether it cleared is readable on the transaction itself: it carries needs_review (present only while still flagged) and needs_review_reason. Each condition below returns 200 and leaves the transaction in the inbox: - Uncategorized journal (reason UNCATEGORIZED): the same save call that flips the flag also re-runs the needs-review processor, which detects the uncategorized leg and re-sets the flag within the same request. Net effect on the inbox: nothing changed. The journal needs a category before the flag can clear. - Old pending journal (>7 days, reason PENDING_TOO_LONG) or pending deleted by the bank (PENDING_TO_DELETED): the server refuses to clear until the user resolves the underlying pending journal (typically by deleting the transaction). - LOADED_OR_IMPORTED (the common "freshly synced" case) clears cleanly and stays cleared.
query_report
ChatGPTCustom spending/income reports by exact dates, card, category and other dimensions. query: pipe QL, not SQL. Resolved UUIDs come from account/category/tag tools; merchant takes merchant_account_id, not merchant_classifier_id. APPROVED default. Examples: where type = EXPENSE and counter = <card UUID> and date between 2024-03-01 and 2024-03-31 | group category | sum(primary_amount) as total | sort -total | limit 5000 where type = EXPENSE and direction = TO and date between 2024-03-01 and 2024-03-31 | sum(primary_amount) as total, count_distinct(transaction) as purchases account/category is the own expense category; counter is the paying card. type=EXPENSE with account=<card UUID> is contradictory. parent includes children. Fields: date, accrual, account/category, parent, type, report_type, counter, counter_type, merchant, tag, currency, family, status, direction, tax_deductible, is_synthetic, transaction, amount, primary_amount. Enum values (exact, upper-case) — type/counter_type: BANK_ACCOUNT, CASH, DEBT, REWARD, EXPENSE, INCOME, EQUITY, MONEY_ON_THE_WAY, STOCKS_AND_CRYPTO, LOAN_AND_MORTGAGE, REIMBURSEMENT; report_type: ASSETS, LIABILITIES, EQUITY, PNL; status: PLANNED, DRAFT, APPROVED, REJECTED; direction: FROM, TO; family: FAMILY_I_PAID, FAMILY_OTHER_PAID, NOT_FAMILY (shared family spending = family != NOT_FAMILY; there is no true/SHARED). Filters: AND; = != > >= < <=, in/not in (...), between a and b, is [not] null; tag has ID / has all (IDs); tag not in (ID) includes untagged. Literal values only: account != counter is invalid. No OR, HAVING, field comparisons or arithmetic. Group: field[, field]; date/accrual by day|week|month|quarter|year fill as period. fill needs bounded dates and supplies empty periods. Sort uses existing column aliases (e.g. sort period requires as period). Limit 1..5000, default 500. Aggregates: sum, sum_abs, avg, min, max, count(), count_distinct(field). date is transaction date; accrual is per-leg recognition month (fallback date). Dates YYYY-MM-DD inclusive. Period subtotals use only requested dimensions: category+merchant means group category, merchant, with no date/day dimension. One side (EXPENSE/INCOME) avoids cancellation. Net expenses use signed SUM; refund exclusion requires direction=TO before grouping; refund-only FROM then negate SUM. Net income is negated SUM, not sum_abs. primary_amount is in primary_currency; amount requires currency grouping/filtering. Different currencies and overlapping tag groups cannot be added as an overall total. Facts are split legs, not purchases: average purchase = total/purchases from example 2, undefined at zero. Top purchases: TO, group transaction, SUM, sort -total. Top merchants require merchant is not null before limit. Purchase thresholds apply after transaction grouping. Percent change=(new-old)/old*100, undefined at old=0. Empty SUM/COUNT periods mean zero; grouped queries may return no rows. Sorting does not guarantee qualifying purchases fit within the row limit. truncated=true invalidates a full total even when sorted by amount; complete ranges combine by sum/count, not averages. Per-group distinct counts may overlap. Transfers: money that LEFT an account = direction=FROM, type in (BANK_ACCOUNT,CASH), counter_type in (BANK_ACCOUNT,CASH,MONEY_ON_THE_WAY), group account, sum. Money that ARRIVED = the same with direction=TO. A->B pairs are exact only for manual transfers (counter_type in (BANK_ACCOUNT,CASH)); synced transfers carry counter_type= MONEY_ON_THE_WAY and the other account is not in the facts: report the outflow and say the destination is unknown here. Count transfers as UNIQUE transaction IDs, never rows. Balances/net worth, bank settled/POSTED and receipt items are outside QL. QUERY_INVALID includes a correction reason. Fixed report tools remain available.
search_institutions
ChatGPTSearch the banks and financial institutions finerd can connect to. Covers bank accounts only. country_code ranks that country's institutions first; it does not restrict the results, so a match from another country is a normal outcome; each row carries its own "region". Only root institutions are searchable. A row with "children" is a group of regional branches, all sharing the group's name: the branch is what gets connected, not the group, and the branches differ by "region". A row's "connect_url" is the link to hand to the user; a row without one cannot be connected directly. "connection_started" means a browser connection exists — it flips at the provider handshake, BEFORE any account is imported, and on a group it means merely that one child has one. It is not a signal that the bank is finished — imported accounts are what a connected-institutions listing reports.
search_merchants
ChatGPTSearch for merchants by name. search is a case-insensitive substring / trigram match on the merchant NAME as stored, which is usually Latin (Rozetka, Glovo): a Cyrillic spelling of a Latin name matches nothing, so retry with the Latin spelling or a short root. An un-recognized transaction has no merchant of its own — search its descriptor with search_transactions instead. Returns each merchant with: - merchant_account_id: finance account id — pass as merchant_account_id when creating/updating transactions - merchant_classifier_id: library/global merchant id — pass as merchant_classifier_id when no merchant_account_id is available - type: LIBRARY or CUSTOMER - system_preset: set for finerd system merchants (UNKNOWN_MERCHANT, INTERNAL_TRANSFER_MERCHANT); null for ordinary merchants - reward_asset_ids: present only for loyalty programs (e.g. World of Hyatt). These are the point currency ids (e.g. "HYT"). When present, a reward account can be created from this merchant via create_reward_account, and the asset id is what reward/points transactions and balances use. Useful for finding stores or services the user transacts with, and for finding the loyalty program + point asset id behind a reward account. Pass system_preset to find finerd's built-in system merchants instead of by name — system_preset="UNKNOWN_MERCHANT" returns the placeholder merchant carried by migrated/un-recognized transactions; its merchant_account_id is the merchant filter matching every transaction still on that placeholder. Only UNKNOWN_MERCHANT and INTERNAL_TRANSFER_MERCHANT are valid here.
search_merchants_batch
ChatGPTLook up MANY merchant names against the library at once — ONE call instead of one search_merchants per name. Main use: merchant cleanup over many payees at once. Each name comes back with its candidates; a candidate carrying a merchant_classifier_id is a library match, and a name with no candidate is not in the library. Resolving a whole list in one round-trip is much faster than one search per name. - names: merchant names to look up (deduped server-side; capped at 50). Returns {results: [{query, merchants: [{name, type, merchant_classifier_id, merchant_account_id, system_preset, ...}]}]}. An empty merchants list = no library/customer match for that name.
search_recurrences
ChatGPTSearch recurring payments and subscriptions by merchant name and status. statuses: DETECTED, CONFIRMED, REJECTED, CANCELLED, PRICE_CHANGED. Omitted statuses includes all statuses. DETECTED and CONFIRMED are active. Pages are zero-based, page_size is 1–100. Returns results, page and has_more. Amounts are in each recurrence's asset; regular payments are not necessarily subscriptions.
search_tags
ChatGPTSearch the user's transaction tags. Returns tag IDs and names. search matches by PREFIX only, case-insensitively, and archived tags are never returned — so a tag worded differently from what the user said ("спорт: волейбол" for "волей") looks like it does not exist. Omit search to list them all.
search_transactions
ChatGPTSearch the user's transactions with filters. Use date_from/date_to for date ranges (ISO format: YYYY-MM-DD). Use category_ids to filter by category (get IDs from list_categories). Use account_ids to filter by account (get IDs from list_accounts). Use merchant_ids to filter by merchant account id (from search_merchants merchant_account_id) — e.g. the UNKNOWN_MERCHANT placeholder's id to list all un-recognized transactions. Use tag_ids to filter by tag (get IDs from search_tags). search_string is a case-insensitive SUBSTRING match on the merchant name and the bank descriptor, NOT full-text or fuzzy: a Cyrillic word never matches a Latin descriptor and vice versa, and bank descriptors are usually Latin (LIQPAY*..., ROZETKA). On a miss, retry with the Latin spelling and a SHORT root ("rozetka", "yoshi"), or list recent transactions with no search_string and read the page. A category or kind of purchase ("flowers", "квіти") is not text — use category_ids from list_categories. Set needs_review=True to return only transactions flagged as needing user review (the app's "Needs review" inbox). Set hide_internal_transfers=True to exclude internal transfers (money moved between the user's own accounts); by default all transactions are returned, transfers included. Returns a page of transactions with date, amount, currency, merchant, tags, and journal entries (FROM/TO accounts).
set_asset_rate_preference
ChatGPTSet this space's preferred rate for an asset — either by selecting one of Finerd's known providers, or by giving a CUSTOM rate. - asset_id: e.g. "hyt_pts" (as returned by a merchant search's reward_asset_ids, or an account balance's asset_id). - provider: one of the providers list_asset_rate_providers returns for THIS asset (points programs use THE_POINTS_GUY, AWARD_WALLET, NERD_WALLET, BANK_RATE, FREQUENT_MILER, ONE_MILE_AT_A_TIME, UPGRADED_POINTS, FINERD_AVERAGE; crypto and stocks have their own), or CUSTOM. - cpp / usd_per_point: the CUSTOM rate, in whichever unit it is known. They are the same rate a hundred apart — "1.9 cents per point" is cpp=1.9 and usd_per_point=0.019. Exactly one of the two is required when provider="CUSTOM", and passing both is rejected. - custom_rate_usd_per_point: the former name of usd_per_point, still accepted. Affects every rate lookup and redemption value estimate for the asset afterwards.
set_verified_balance
ChatGPTRecord an account's verified (true) balance for an asset at a date. Use after importing points activity: pass the program's CURRENT balance so the account reconciles. The server auto-creates a balance-correction transaction for any delta between this verified balance and the computed balance. Idempotent per (account, asset, date) — re-recording replaces. - asset_id: verbatim (the point asset id for reward accounts; lowercase ISO for money accounts). - balance: the verified amount. - date: ISO YYYY-MM-DD (or a full YYYY-MM-DDTHH:MM:SS). Returns the verified balance including correction_amount (the delta booked).
update_categories
ChatGPTRename one or more existing categories in one call — for example, translate the whole default set into the user's language. Only the name changes. Nesting, icon and colour stay as they are, and transactions, budgets and reports stay attached to the same category ids. A category shared with a family partner gets the new name for the partner too, as a rename in the app does. This renames the category itself, everywhere it appears. Moving ONE transaction to another category is a different operation — update_transaction with category_id — and leaves every category name as it is. When the category a user names does not exist, the usual fit is an existing category with a similar meaning or a new one from create_category; renaming some other category into that name would relabel all of its transactions. - updates: 1–100 items {"category_id": <id from list_categories>, "name": <new name>}. Names are trimmed, 1–100 characters; an id may appear once. - dry_run: true reads and checks every category and returns the planned changes (category_id, old_name → name) without writing anything. A bulk rename is normally shown to the user this way and confirmed before the real call. An id that is not an EXPENSE / INCOME / REWARD_CATEGORY account fails the whole call before anything is written. System categories (system_preset) and archived ones are returned in skipped with a reason, names that already match in unchanged; neither is written. Returns updated (count), changes, unchanged, skipped, and for a real call failed and not_attempted. Once writing has started the result is always this summary, never an error, so renames that landed are never hidden: a failed item carries errorCode, traceId and outcome_unknown (true when the server's answer was lost and the rename may or may not have been applied); not_attempted items were not written because the call ran out of time. Repeating the same call is safe: categories already renamed come back in unchanged.
update_merchant
ChatGPTUpdate an EXISTING local (customer) merchant's name / logo / website. Use this to attach a logo or site to a merchant you already created (or to rename it) — create_merchant only sets those at creation, so this is how you fix one after the fact. Read-modify-write under the hood: nothing else on the merchant is changed. - merchant_account_id: the merchant's account id (from create_merchant or search_merchants). - logo_url / website: same as create_merchant — pass your best-effort value; logo_url is server-side validated and a dead link / non-image is dropped (a logo_note is returned). A guessed domain often resolves to a parked page. Omit a field to leave it unchanged. - name: optional rename. Per-space appearance only; nothing touches the shared library.
update_tag
ChatGPTRename or archive an existing tag. Get tag_id from search_tags. Pass name to rename, archived=true to archive.
update_transaction
ChatGPTEdit an existing transaction. Two modes — patch and restructure. PATCH MODE (default — when from_journals/to_journals are NOT provided): Field-level overrides applied on top of the existing journal layout. - date: ISO datetime; a new day re-values the amount at that day's rate - merchant_classifier_id / merchant_account_id: pass exactly one from a search_merchants result (merchant_classifier_id takes precedence) - comment: text comment. Set to a string to update. clear_comment=true clears it; comment="" does not, because empty-string sentinels are unreliable through tool-use serialization. Omitting the argument preserves the current comment unchanged. - category_id: what one journal FACES — its counter-account. A category id from list_categories, or any other counter-account id from list_accounts: a person's DEBT account when that share is theirs (that is how a shared expense becomes a debt they owe), a REWARD account for points. The parameter name is historical; the value is written to the journal's account whatever its type. So "put this on X" / "assign it to X" is answered out of BOTH lists — a name that is not among the categories may still be an account. - tags: list of tag IDs from search_tags — replaces tags on the same journal that category_id targets. To clear all tags on that journal, set clear_tags=true. Omitting the argument preserves current tags. Ids only: a tag NAME is rejected, and a tag that does not exist yet has no id. Same for category_id — that is an account id, as returned by the category and account listings, not a name. - accrual_month: per-journal booking month — the month this leg should be attributed to in reports / accruals, independent of the transaction date. ISO date "YYYY-MM-01" or shorthand "YYYY-MM". To clear and fall back to the server default, set clear_accrual_month=true. Targets the same journal as category_id / tags (auto-picked single category leg, or the leg pinned by journal_id). Use this when the user says things like "this Jan invoice is actually for February", "віднеси цю витрату до березня", "book this to Q1", "this is the March rent paid in April" — i.e. when the spending/earning conceptually belongs to a different month than the cash movement. For "split a single expense ACROSS several months" use restructure mode below (one leg per month, with per-leg accrual_month). - A leg with "synced": true in get_transaction is the bank's record and cannot be changed. To re-point a synced transfer or expense, pass the journal_id of the OTHER (not synced) leg — e.g. a transfer's money-on-the-way TO leg can take a category_id. - journal_id: required for splits when targeting category_id / tags / accrual_month (or their clear_* counterparts) to a specific leg. Get journal_id from get_transaction.journals. - accounting_status: APPROVED or PLANNED on a manual transaction; APPROVED or REJECTED on a bank-imported one (REJECTED excludes it from reports); DRAFT is never settable. Anything else is a 400 - clear_comment / clear_tags / clear_accrual_month: explicit boolean flags to clear the corresponding field. Use these instead of empty string / empty list sentinels — those are unreliable. RECEIPT (BILL) ATTACH / DETACH — works in both modes, alone or together with other changes: - bill_id: attach this receipt (an id from list_bills, e.g. with exclude_assigned=true, or get_bill) to the transaction. A bill can be attached to ONE transaction and a transaction holds ONE bill; a conflict is refused with a message naming the other side. Attaching here is a plain link: the transaction keeps its own category split and the bill takes the transaction's merchant. Bills with no items (not recognized) cannot be attached. The link is readable afterwards from either side: the transaction carries bill_id, and the bill carries transaction_id. - detach_bill: true removes the attached receipt from this transaction. The bill itself survives as unassigned (it comes back in list_bills exclude_assigned=true) and can be attached elsewhere. Omitting both keeps the attached receipt through the edit. A receipt is ne…