What kinds of thing this document holds — every format declared in it, with its columns, their types, units, bounds and enum members, and the prose the author wrote around them. The columns are declared per document: `start_here` ends with one line per format, enough to write a row, and this is the whole declaration. Name a `format` for its full column list; omit it for the list of what is declared. Reads the file; never modifies it.
輸入參數path必填format
declare_format
Declare a new format
Bring a **new** kind of thing into a document: a format, compiled to a real table with real types and checks. Call `formats` first. Each column has a `snake_case` `name`, a `type` — one of `text`, `integer`, `real`, `date`, `boolean`, `enum`, `money`, `link`, `quantity`, `decimal`, `month`, `instant`, `links`, `percent`, `time`, `enums`, `elsewhere`, `file` — and optionally a `unit` (`g`, `kcal`), `required`, `unique` (no two rows share it — a name a row is known by; text, integer, decimal, date, month or link), `values` for an enum or enums, `minimum`/`maximum`, a `scale` for a `decimal` (6 for a rate) and a `note` saying what it means. **A format declares its claims** — `claims`, equations over its numeric columns with a tolerance, like `4*protein_g + 4*carbs_g + 9*fat_g = kcal` within 2%; they make a row provable and never refuse one. **Ask the user whether any figures relate** before answering (a total and its parts, a price and a quantity); a list — books, contacts — makes none, needs no claims, and still gets every refusal. A figure worked out rather than typed is a `computed` column. **`money` is two columns**, minor units beside a currency, so no figure is a float. **`quantity` is two as well**, a figure beside the unit *that row* is in — only when the unit varies by row; otherwise a `real` with a `unit`. Neither takes a `unit`. A **ratio** has a `unit` and a `per` — `density`, `g` per `ml` — the only way a figure crosses dimensions: a claim writes `amount in g via ingredient.density`. No free-text check, no expression. **Across two columns of one row** is a `constraint`, one of four sentences: `isbn is required when kind is BOOK` (`required only when` adds *and empty otherwise*), `at most one of book, article, video`, `score is at most 10 when kind is QUIZ` (or `at least`), and `one row per course, week` (`when status is DONE` limits it) — a row breaking one is refused. `notes` is prose kept word for word: where the figures come from and what was decided. T
輸入參數path必填notestitleclaimscommitformatcolumnsformatsrollupscomputedrule_setsaggregates另有 1 個
connect_formats
Connect two formats
Join two formats this document **already** declares: a `link` column on `format`, so each of its rows belongs to one row of `target` — each `recipe_line` belongs to one `recipe` — and, optionally, a total on `target` along it (an `aggregate`): `recipe.total_kcal`, the sum of `amount_g / 100 * ingredient.kcal` over the lines that name it. This is how a format gets a total of another declared after it, which `declare_format` cannot do. Name an existing link as `column` to add only the total. **It only adds**: a link may be empty, so rows already there lose nothing; `required: true` on a format that has rows is refused. A connection is removed with `extend_format` `remove` (the link column once its cells are emptied, or the total); re-pointing one is done at `#/connections` in the app. Returns what it would add and writes nothing until `commit: true`.
輸入參數notepath必填columncommitformat必填target必填found_byrequiredaggregate
extend_format
Change a format
Change a format this document **already** declares, as its table does: add `columns`, `claims`, `computed` columns, `unique`, `constraints`, `rollups` and `aggregates` as `declare_format` takes them; `rename`, `change` or `remove` what it has; `order` the columns. A rename keeps its values. Refused, naming the rows: a `required` column on rows, removing a column holding values, a list the rows do not fit, a type or unit change over values. Writes nothing until `commit: true`.
輸入參數path必填orderchangeclaimscommitformat必填removerenameuniquecolumnsrollupscomputed另有 3 個
delete_format
Remove a format and every row in it
Remove a declared format: its declaration, its table and **every row in it**. Refused while another format links to it or folds its rows. The dry run says how many rows go; show that to the user, and call again with `commit: true` only on a plain yes. To remove some rows, use `delete_format_rows` instead.
輸入參數path必填commitformat必填
format_rows
Rows of a declared format
The rows this document holds of one declared format. Ask `formats` first for the column names — they belong to the document. `match` filters on exact values (text and enum members case-insensitively; a list column, the rows holding the one named), `search` a substring in every column as shown (a price as 12.90, a link by its row’s name) but yes/no, and `order` sorts by any column, computed too — by default the one a row is named by, which is its first text column. Narrow `columns` to get a table of just those — a computed column (a total, or one worked out from the row) by its name too; leave it out and each row comes back in full with its empty columns left out, **and with what its claims say about it** — proved, failed, or unchecked when the row has nothing in a column one of them names. Every row carries the id the other tools use to correct or remove it. Reads the file; never modifies it.
輸入參數path必填viewlimitmatchorderformat必填offsetsearchvaluescolumnsdescending
show_format
A declared format’s rows, drawn as the app draws them
Shows the person one declared format as the app draws it — its table, its list (one line per row, a tick, grouped as `arrange_views view:list` says), or its board, roadmap or ready-to-start list when the format has one; or the agenda, every dated format’s rows from today; or a dashboard (no `format`: the document’s) — in the conversation, in a client that draws MCP Apps; the answer’s first line says whether this client said it draws them. Call it when they ask to see a format, or some of its rows: `match` and `search` filter exactly as `format_rows` does, and the view draws only those rows and says it is filtered. The text answer names every row the view shows, with its state or lane. The page is a read-only copy, drawn again as the file changes. Reads the file; never modifies it.
輸入參數path必填viewmatchformatsearchcolumns
format_problems
What looks wrong in declared formats
What the engine can see is wrong in a format’s rows, without a rule anybody wrote: a link empty where most rows fill it, rows the same in every column, claims failed or unchecked. `read_onto_format` says it after every read. Reads only.
輸入參數path必填formats
search_document
Search the whole document
Every row of every format, and the rest of the file, that holds any of `terms`: each hit by format, row and column, columns searched as shown. One call where `format_rows`’ `search` is one a format. Or `denylist`, a `--replace-text` file: the scan before a document is shared — hits only, a term named by its line, and the file’s bytes checked for a deleted value. Reads only.
輸入參數path必填termsdenylist
start_here
What to start from
Call first. Answers the questions this document’s own views say a session starts from — what is ready to start, what is open, the latest entries — each with the `format_rows` call that lists it whole; then what a row of each format looks like, enough to write one; and where new work begins: `formats`, then `read_onto_format` (files or a folder of notes) or `declare_format`. Read-only.
輸入參數path必填
format_rollup
A rollup of a declared format
A rollup a format declares, run: a figure per month, and per linked row. `formats` lists them. A month with a row that cannot answer is `unchecked`, naming it; money stays per currency. Reads only.
輸入參數tofrompath必填limitformat必填rollup
apply_rules
Apply a rule set
Run a rule set over every row it sets — a write applies it by itself. A person’s value is never touched; a tie is named. With `link`, fill that link’s empty cells by its `found_by` instead; a set link is never moved, and a row with no match or two is named. Dry run until `commit: true`.
輸入參數linkpath必填commitformat必填rule_set
record_format_rows
Record rows of a declared format
Write rows into a declared format — **a list of them in one call**. The columns belong to the document — `start_here` lists them, `formats` in full — and a key the format has not got refuses the batch rather than being dropped, because a figure you believe you stored and did not is worse than one you know is missing. A `money` column takes the amount in major units beside its own `<column>_currency` key, and a `quantity` column takes the figure beside a `<column>_unit` key naming a unit this document knows — `amount: 200` with `amount_unit: ml`. A `link` takes the row’s id, its name (first text column) when one row has it — `recipe: "Pancakes"` — or columns that find one: `{name: "Salt", brand: "Fine"}`, or one unique column alone: `course: {notion_id: "a1b2"}`; a row may name another of the same batch. Types, bounds and enum members are checked before anything is written and the whole batch applies or none of it does. Returns the diff and writes nothing until `commit: true`.
輸入參數path必填rows必填viewcommitformat必填
update_format_rows
Correct rows of a declared format
Change columns of rows already stored — **a list of edits in one call**. Each names its row by `id` or by `where` (`{ref: "INV-0042"}`, which survives another writer; a text document renews ids on every write), and the `values` to change. `expect` (`{state: "planned"}`) refuses the batch unless the row still holds that, so two sessions cannot both take one row. Every column not named is left exactly as it was, which is the difference between this and the app: `#/formats/<name>` shows a person every column and writes every column, and this corrects three of thirty across a batch. A required column cannot be emptied. A `link` value may be the name of the row it points at, or a unique column of it (`course: {notion_id: "a1b2"}`), as in `record_format_rows`. Returns the before-and-after and writes nothing until `commit: true`.
輸入參數path必填edits必填commitformat必填
delete_format_rows
Remove rows of a declared format
Remove rows from a declared format by id — **a list of them in one call**. **Refused while any other row still links to one of them** — the dry run names those rows; re-link or clear them first. The app removes one row at a time from `#/formats/<name>`; this is how thirty go at once, after printing every one of them in full. Writes nothing until `commit: true`; a copy of the document is taken first, and it is the only way back.
輸入參數ids必填path必填commitformat必填
In a document that keeps the `development` pack, take units by `ref`: `planned` to `in_progress` with `taken_by`, the git `branch` they are taken on and `started` (default today), the batch refused unless every one is still planned. With `take_over`, take units already `in_progress` from that taker — a session that stopped mid-unit — refused unless each is held by exactly that taker; `started` is reset and the taker replaced is kept on `taken_over_from`. Writes nothing until `commit: true`.
輸入參數path必填units必填branchcommitstartedtaken_by必填take_over
In a document that keeps the `development` pack: a unit’s closing pass in one call, for a list of units by `ref`: their new `decisions` (date, milestone, scope and `state: live` default to the unit’s), the unit to `built` with `landed` (default today), `files` and `decision` (default its first new one), its `review_entries` (`state: open` default), `close_milestone`, its git `commit` (refused without one unless `same_commit: true`) and `branch` (another than the claim’s refused unless `other_branch`), and the `report` of the session that built it. Or `inbox`: ids of a child’s proposals — an inbox row, `state: new`, `refs` its unit, its `body` one ```json block of one unit’s landing (no `close_milestone`), written to the document path the hooks printed — each landed as written and moved to `applied`. Each step is `record_format_rows` or `update_format_rows`, judged as they are, run in that order; all of it applies or none. Diff by format; writes nothing until `commit: true`.
輸入參數path必填inboxunitscommit
dispatch_plan
Dispatch plan
In a document that keeps the `development` pack, before handing units out: for unit refs (every ready unit if left out), which can run at once and which share a path in their `paths` — one per line — and so go one after another, beside any unit in progress; a unit with no paths is unknown, never safe. Then each one’s brief, the units it waits on and the decisions it names. Read-only.
輸入參數path必填units
record_units
Add or correct a unit of measure, or set the currency
Teach this document a unit it does not know — `slice`, `IU`, a tablespoon that is 20 ml in this kitchen. A unit is what a declared column says its figures are in and what a claim's `in` converts into, so a conversion the document cannot make is a conversion this fixes. **The engine converts inside a dimension and never across one**: grams to kilograms is arithmetic, millilitres to grams needs a density, and a density is a column of the format rather than a unit. A symbol the document already has is corrected — its factor and its note, never what it measures. `currency` sets the document’s currency — what a new amount on a screen starts in; a row still names its own. `week_starts_on` is what `this week` counts from (Monday). `formats` lists them. Returns the diff and writes nothing until `commit: true`.
輸入參數path必填unitscommitcurrencyweek_starts_on
read_onto_format
Read a file onto a declared format
Read what the user already keeps onto a declared format: any .csv, .xlsx (or .xls) or .json of rows, a file row per table row; or **a folder of Markdown notes**, a note per row: `title`, `file`, each frontmatter key, `inline tags`, `body links` and `body`, with a link read from `[[name]]` or `[text](file.md)`; a body link naming no row is left out and named, the note still read; a hidden file is not read; links between notes of one folder are filled; `items` reads list items or table rows onto another format, each linked to its note. **Dry-runs by default**: the reader with each key’s header cell and first value, the rows as stored, the rows that would not type and why, and **what the format claims, checked before a row exists**. Writes on `commit: true`. Keys come from `formats`. **The user maps the columns; this only guesses** — show the reader, correct it with `columns`. `fixed` gives a key one value for the file (a currency, a unit, the row every line belongs to). A column fills a `link` only by the exact unique name of its row, and a line naming none is refused; a link is guessed only from a header that names it. `header` writes the file’s total, period or card as one row of another format that every line links to. **A source read again updates its rows** by note file name, and any file by a unique column (so a later export updates what an earlier one wrote): changed, added and gone named; gone kept unless `remove_gone`. `supersede_reading` takes a reading back. **Several files in one call**: `files`, each `{ file, format }` with its own columns — read in the order their links need, so a line names a row from a file above it; one dry run for the lot, written whole or not at all.
輸入參數filepath必填filesfixeditemssheetundercommitformatheadercolumnsrecursive另有 4 個
supersede_reading
Take back what one file wrote
Take back the rows one file wrote onto a declared format — **exactly those rows and no others**. Called with no `reading`, it lists every file read onto the format with when, and how many rows each still has. **Dry-runs by default** and writes nothing until `commit: true`. **The reader is kept** (forgetting one is `readers`), so supersede → correct the file → read it again is one call each. This is how a file read twice stops being two copies; a file is matched by its bytes, never row by row.
輸入參數path必填commitformat必填reading
readers
A format’s readers
List a declared format’s readers — one per kind of file it has read — or name and forget them. Dry-runs until `commit: true`. Forgetting takes back no row.
輸入參數path必填commitforgetformat必填rename
keep_copy
Back up a document now
Backs the document up now: a copy as it is, in Emerald’s folder, named for what it comes before (`work.before-20261006T120512-cleanup.emd`), and says where. A read or batch of 100+ rows keeps one by itself; ask before anything else worth undoing. The last 3 are kept, apart from the rolling copies.
輸入參數path必填before
store_document_as
Store a document as text or as SQLite bytes
Writes the document again as `text` (diffs in git) or as `sqlite` bytes, and every later write keeps that shape — the app’s Save as text. Refused when the text would not carry it — a document keeping the ledger, for one. Writes nothing until `commit: true`.
輸入參數as必填path必填commit
arrange_views
Arrange a format’s views
How the app shows a format: on its entry, a column’s `show_as`, `header` line, `hidden`, `new_entry` and the `order`; a board’s lanes; a table column’s `width`; a column’s `setting` in a list, a roadmap, a ready list or a start-here question; `view: saved`, a `name` and what it keeps (`today` stays that phrase) — on one saved, only what is given changes; `match:{}`, `search:""`, `order:""`, `columns:{}` empty one — or `rename`, or `delete: true`; `kind: input` takes `fields`, `defaults`, `default`; `view: dashboard` `add`s, `size`s, `order`s and `remove`s blocks (no `format`: the document’s). `formats` with a `format` shows the arrangement and each column’s ways. Dry-runs until `commit: true`.
輸入參數path必填commitsettings必填
create_document
Create an Emerald document
Create a new .emd document at a path that does not exist yet; never overwrites. It keeps the `packs` named and no other, and **none when omitted**: a document starts empty and holds what the person declares. Name a pack only when the work is what it is for — `development` for a project’s memory, `ledger` for their finances. With no path the user may be shown a form; otherwise ask them.
輸入參數pathpacks
keep_packs
Keep packs in a document
Keep packs in an existing document (the Packs screen’s *Keep … here*): declares the pack’s formats it lacks, adds missing optional columns to those it holds, renames a column the pack renamed with its values, changes no row; a pack kept already is brought up to date this way, its formats’ and columns’ notes and views’ settings too wherever the document still holds an earlier build’s — one a person changed is kept, and the dry run names it. Refused, naming the column, where a format of the same name is not the pack’s. Dry run until `commit: true`.
輸入參數path必填packs必填commit
document_info
Emerald document info
Describe an Emerald document (a .emd file): whether this plugin will serve it, the formats it declares with how many rows each holds, and — when it keeps the ledger — its accounts, transactions, categories and reporting currency. Call it when a tool refuses a document — every tool refuses one whose schema does not match, and this is the one that says so in a sentence the user can act on. Reads the file; never modifies it.
輸入參數path必填
category_tree
Category tree
The document category taxonomy: every category full path (Living → Utilities → Electricity), its id, its kind and its Plan bucket. Read this before suggesting a category for anything — the ids here are what the write tools take, and two categories may share a short name.
輸入參數path必填include_archived
uncategorized_merchants
Uncategorized merchants
The uncategorized backlog, grouped by merchant: how many rows each merchant has, and what the user has already categorized other rows of the same merchant under. Ordered by the largest single-currency outflow, which is not printed. Start a "help me categorize" conversation here.
輸入參數tofrompath必填limitsearchaccount_idinclude_amounts
A filtered page of the ledger, plus the totals over every matching row — not just the page. Filter by date range, account, category, uncategorized-only, merchant search and amount bounds; sort by date, charge date, merchant, category or amount. Use this to answer "what did I spend at X" and to show the evidence behind any figure.
輸入參數tomaxminfrompath必填sortlimitsearchdirectionaccount_idcategory_iduncategorized另有 1 個
month_summary
Month summary
One month already added up: income, expenses, balance, savings rate; how each compares with last month, the three-month average and the same month last year; the category breakdown rolled up to parents; the fixed-versus-variable split; and where the income came from. Defaults to the current month. Every figure comes from the same call the app own Insights screen makes, so they cannot disagree.
輸入參數path必填month
One year already added up: income, expenses, balance and savings rate; averages over the complete months only; the best and worst month by balance; a month-by-month table where a month with no data reads as nothing rather than as zero; and what the paychecks say about gross, tax and payroll savings. Defaults to the current year.
輸入參數path必填year
The bills the document tracks: what each is expected to cost, its amortized monthly equivalent, its cadence, whether the amount is fixed or variable, what it is matched against in the ledger, and whether the user marked it cuttable. Active ones by default.
輸入參數path必填include_inactive
Every debt with its principal, what has been paid, what is outstanding, how far along it is and when it projects to be paid off — plus the totals and what card installments still commit the user to. Open debts by default.
輸入參數path必填include_closed
Goals and where the money is: net worth — the one figure Overview shows, with its parts (in the bank, in accounts with holdings, locked, pension and hishtalmut funds, owed) — each goal balance broken into the four ways money got there, and the monthly expense figure the emergency-fund target is measured against. Open goals by default.
輸入參數path必填include_closed
goal_projection
Goal projection
What each open goal will hold in N months, by plain addition of the amount Plan puts toward it each month (no interest counted), and what a month would have to be to reach it on its date. The same view as the Goals screen.
輸入參數path必填months
debt_projection
Debt projection
What each open debt will have left in N months, by plain addition of its monthly payment and the extra Plan puts toward it (no interest counted), and when it is paid off with and without the extra. The same view as the Debts screen.
輸入參數path必填months
fixed_expense_candidates
Recurring charges the ledger suggests
What looks like a recurring bill in the imported transactions but has not been confirmed as one: the merchant, the typical charge and its amortized monthly share, the cadence, how many months it was seen in, how much the amount and the day move, and how confident the detector is. Merchants already tracked or previously dismissed are excluded. This is the question `fixed_expenses` answers the other half of — that one lists what is already committed. Reads the document; changes nothing.
輸入參數path必填ending_atwindow_monthsinclude_dismissed
track_fixed_expenses
Track recurring charges as bills
Turn recurring charges into tracked bills — **several in one call**. An entry either names a `merchant` from fixed_expense_candidates, in which case the charge, cadence and category come from the detector unless overridden, or gives a `name` and an `expected_amount` for a bill the ledger cannot see, like an annual premium paid by standing order elsewhere. **Dry-runs by default**: it returns what it would track and writes nothing until called again with commit true. Confirming the same merchant twice updates the row already covering it rather than adding a second. The document is copied to a timestamped .bak before anything is written.
輸入參數path必填commitexpenses必填ending_atwindow_months
dismiss_fixed_expense_candidates
Say no to suggested recurring charges
Record that these merchants are not recurring bills, so the detector stops offering them — to this conversation and to every later one. A dismissal lasts until it is cleared; pass restore to clear it and let the merchant be suggested again. Writes immediately: it changes no transaction, no category and no bill.
輸入參數path必填restoremerchants必填
plans
Plans: bill choices and buckets
The Plan screen as rows: each plan with what every bill does to it (out of net, part of the split, or ignored) and its buckets — what each is for (a goal, a debt, a category, a fund, an account or nothing) and how it is sized. Ids are here for `set_plan`, `set_fixed_expense_roles` and `edit_fixed_expenses`. The worked-out figures for a month are `plan_month`.
輸入參數path必填plan_id
plan_month
Work a plan out for a month, and use it
One plan worked out for one month: net pay, the bills out of net, what is left to split, each bucket’s amount and share, what it actually got lately, when a goal is reached at this rate, money with no job to do — and what *using* the plan for that month would write (a planned deposit into a goal, a planned payment on a debt). **Dry-runs by default**, which is also how you read it; `commit: true` writes those suggestions and gives the month this plan. `months` instead gives a list of months this plan and writes nothing else, and `back_to_default` puts months of this plan back on the default — the Plan screen’s month checkboxes. A month nobody gave a plan is on the default. `only_buckets` writes just those buckets’ suggestions. A fund or an account is pointed at and writes nothing. The document is copied to a timestamped .bak first.
輸入參數path必填monthcommitmonthsplan_idonly_bucketsback_to_default
set_fixed_expense_roles
Say what each bill does to a plan
For each bill, per plan: `net` (comes out of net pay before the split), `split` (is one share of the split, drawn on the bar) or `ignore` (this plan leaves it out). **A list in one call.** A bill the paycheck already took (a pension deduction) is outside the choice and refused. An expense with no choice made is ignored. This is the Plan screen’s switch on every row. **Dry-runs by default**: it shows each change and writes nothing until `commit: true`; the document is copied to a timestamped .bak first. Ids come from `plans`.
輸入參數path必填roles必填commit
set_plan
Create, change or delete plans
What the Plan screen does to a plan — **a list of entries in one call**. Per entry: `plan_id` for an existing plan, or `create: true` with `name` (and `copy_of`, a plan id, to start from its sizes and its choices for the bills); then any of `name`, `make_default`, `basis` ({kind: LAST_PAYCHECK | AVERAGE_LAST_N | SPECIFIC_PAYCHECK | CUSTOM_AMOUNT, amount, months, paycheck_id}) — a bonus month is a plan whose basis is an amount — and `buckets`, **the plan’s whole list of buckets** as the screen’s bar has it: each {bucket_id (existing) or name (new), points_at: {kind: goal | debt | category | fund | account | none, id}, mode: percent | amount | rest, percent (0-100), amount (major units), locked}. A bucket is for one thing, and a bucket sized “rest” takes what is left after the others and the bills that are part of the split; at most one. Or, alone, `delete: true` (the default plan too; another becomes the default), or `remove_buckets` to take buckets out and hand each share to its right. What each bill does to a plan is `set_fixed_expense_roles`. **Dry-runs by default**; `commit: true` writes. The document is copied to a timestamped .bak first. Ids come from `plans`.
輸入參數path必填plans必填commit
edit_fixed_expenses
Edit, end or stop tracking bills
Change bills that exist — **a list in one call**. Per entry: `name`, `expected_amount` (the whole charge, major units — a yearly premium is the year’s figure), `cadence`, `category_id` (null clears), `is_discretionary`, `day_of_month` (null clears), `ended_on` (YYYY-MM-DD or "today": it keeps its history and drops out of every plan); or, alone, `stop_tracking: true`, which removes it and what every plan said about it — its charges stay on Transactions. How it is recognised in the ledger is not changed here. Adding is `track_fixed_expenses`; what it does to a plan is `set_fixed_expense_roles`. **Dry-runs by default**; `commit: true` writes. The document is copied to a timestamped .bak first. Ids come from `plans` or `fixed_expenses`.
輸入參數path必填edits必填commit
Every paycheck recorded in the document — gross, net, the employer’s own contributions, total compensation, the effective tax rate, what payroll put away, and whether each itemization reconciles to its net. Plus the salary months with no paycheck at all, per-year totals, and **the deduction-type vocabulary with its ids**, which is what a line of a new paycheck has to name. Reads the document; changes nothing.
輸入參數path必填limitinclude_lines
record_paychecks
Record paychecks
Write paychecks into the document — **several in one call**, one entry per paycheck. Each needs the salary month, the payment date, gross and net; an itemization is optional, and each of its lines names a deduction type by id from `paychecks`. Read a paycheck PDF or photo yourself and this is where what it says goes. **Dry-runs by default**: it returns what it would record, including whether each paycheck’s itemization reconciles to its net, and writes nothing until called again with commit true. Nothing here deduplicates — a second paycheck for a salary month is allowed, because a 13th salary is exactly that. The document is copied to a timestamped .bak before anything is written.
輸入參數path必填commitpaychecks必填
set_deduction_types
Make, rename, order and remove deduction types, and say what each counts as and pays into
Everything the deduction types screen does, for **several types in one call**: **make** a type (its name, what the paycheck calls it, its kind), **rename** one, change its kind, **remove** one, put the list in **order**, and say what each type **counts as** (out of net, saving — the two flags every paycheck figure turns on) and what it **pays into** — a fund or a goal for a saving line, a debt (a payroll loan) for a loan repayment. **This link is how a fund finds its paycheck lines**: a pension line is a deposit into a fund only because its type names the fund, and `funds` checks those deposits against the fund’s own report. A new type starts as its kind’s guess, and a kind change carries the flags along while they are still the guess; one that differs is shown as set by hand, and `guess` puts it back. One destination per type — naming one clears the others, and `pays_into: null` links nothing. Refused by name: a target of the wrong kind (a fund or a goal takes a line that counts as saving, a debt a loan repayment), a name another type has, and removing a built-in type, a type a stored paycheck line names, or one another row links to. Types and their ids are listed by `paychecks`; funds by `funds`, goals by `savings`, debts by `debts`. **Dry-runs by default**; the batch applies whole or not at all, and the document is copied to a timestamped .bak first.
輸入參數path必填ordertypescommit
funds
Pension and hishtalmut funds
Every pension, study fund, provident fund and managers’ insurance the document tracks: the latest transcribed balance and how old it is, the management fee as stated and as measured against what was actually charged, what the dormant ones cost a year, and — the point of the feature — **whether the paychecks and the fund’s own report agree about what was deposited**. A fund finds its paycheck lines through the deduction types that pay into it — `set_deduction_types` sets that link. Name one with fund_id to also get its readings, its return window by window, and the working behind that check. Reads the document; changes nothing.
輸入參數path必填fund_idinclude_archived
record_fund_readings
Record fund balances from a report
Transcribe what a pension, hishtalmut, provident or managers’-insurance report says into the document — **several readings in one call**. Each entry names a fund by fund_id or by name, and carries one reading: the balance and its date, plus whatever else the report printed. A name that matches no fund creates it, once per batch, so four quarterly reports for one fund are four entries naming the same fund. **Leave out anything the report did not state** — an empty field means "not stated", and a 0 would be read as a stated zero. **Dry-runs by default**: it returns what it would write and writes nothing until called again with commit true. The document is copied to a timestamped .bak first. Editing a fund or correcting a reading already written is app-only.
輸入參數path必填commitreadings必填
Add goals — **several in one call**. A goal has a name; a target amount and a target date are optional, and an open-ended goal is a real answer. Its kind is optional and drives nothing (EARMARKED when left out, as the goal form makes it); how many months of expenses the bank should hold is the cushion (`set_cushion`). **Dry-runs by default**; the document is copied to a timestamped .bak before anything is written. **The goal form’s one question is `default_holding`**: a bank account’s Cash, a place kept by hand or a new one takes `already_there`; a holding a broker’s rows buy is followed by its rows, with nothing to type; a bank account’s Cash can count the transfers into it (`from_transfers`). Editing, closing and deleting a goal are app-only, except its default holding (`set_goal_deposit_categories`).
輸入參數path必填goals必填commit
record_deposits
Record savings movements
Record money moving in a goal — **several in one call**. Six modes: DEPOSIT, WITHDRAWAL, TRANSFER between holdings, OPENING for what was already there when tracking started, and GAIN / LOSS for what the market did. **The mode carries the direction; the amount is always positive.** OPENING, GAIN and LOSS are in the balance but are not money the user set aside, so they are kept out of the savings rate. **Dry-runs by default**; the document is copied to a timestamped .bak first. Editing and deleting a movement are app-only. A DEPOSIT here is money **set aside** by hand, still inside the bank; deposits **read from the bank** come from the Money moved category a goal reads (`set_goal_deposit_categories`).
輸入參數path必填commitdeposits必填
set_place_accounts
Name the account a place’s money is shown by
Say which account each place on Net worth is — the bank account, the broker, the savings account whose statements show its money — **several in one call**. A place *links* to an account and is not merged with it: a deposit or cash at home has no statements and names none (`account: null` takes a link away). A card cannot be named, and an account is named by one place at most. The account gets its own cash holding in the place, made with the link — or `cash_holding` picks one already there, such as the holding goal deposits already land in. Unlinking keeps the holding. A bank account or cash has a place named as it and its own cash from the moment it is made; naming it from another place moves it there while its own place holds nothing. A place that names one account is not re-pointed to another in one call: set it to `null` first. Nothing here moves a figure. Places and holdings are listed by `savings`; accounts by `document_info`. **Dry-runs by default**; the document is copied to a timestamped .bak first.
輸入參數path必填links必填commit
record_holding_readings
Record what a statement says a holding is worth
Write what a statement printed each holding was worth on a day — **a list in one call**. Transcribed, never fetched: there is no price feed. `value` is the worth as printed, in major units; `quantity`, `cost_basis` and `price` are optional and each is a check: the cost against what the rows that name the holding put in by that day, the quantity against their quantities, quantity × `price` against the value (a price printed in agorot is divided by 100 first — the check catches it when it is not). An account’s own cash holding takes a value only, checked against the account’s running balance. A goal fed by the holding is then worth its latest reading, with what went in beside it, and the difference is growth. The same figures for the same day again change nothing; other figures for a day that has a reading are refused unless `correct: true`; `remove: true` takes a day’s reading away. Holdings and their ids are listed by `savings`. **Dry-runs by default**; the document is copied to a timestamped .bak first.
輸入參數path必填commitreadings必填
set_goal_deposit_categories
Point goals at their deposits
Say what feeds each goal, **several in one call**. **One feed per goal, never both**, so nothing counts twice: either a **Money moved category** — a transaction categorized under it is a deposit into the goal, read from the bank — or, with `from_holding: true`, **the rows that name its default holding**: every buy or sale of that holding inside an account that holds things (`set_transaction_holdings`) is a deposit, and the bank transfer that brought the cash there feeds nothing — or, with `from_transfers: true`, **the confirmed transfers into the bank or cash account whose own Cash is its default holding**: each transfer pair (`answer_transfers`) arriving there is a deposit and one leaving it a withdrawal, after its Was already there; off unless asked, since a main account takes transfers that are not for a goal. A broker’s cash is no such feed. Categorizing is the only step, and a row re-categorized elsewhere takes its deposit with it. One goal per category, per holding and per account; the category must be a Money moved one (a transfer or a saving, not a spending category), and the goal needs a default holding for the money to sit in: `default_holding` sets it, in the same entry or on its own, as the Goals form’s "Where is the money?" does. `category: null` stops either feed, and removes the deposits it had derived. Money you set aside by hand (`record_deposits`) and what was already there are never touched. `savings` shows what a holding’s rows put in, against the cost basis a reading printed. **Dry-runs by default**; the document is copied to a timestamped .bak first.
輸入參數path必填goals必填commit
set_cushion
Set the cushion
Set the cushion. **What is in the bank** is the latest statement balance of the bank accounts that count — **every bank account and cash, until `accounts` unticks it**; **each goal saved in one of them holds its part** (its deposits into that account’s own cash holding), and **the cushion is what is left**, against a target of `months` of bills plus the Plan `buckets` (Groceries, say) counted beside the bills. No goal is the cushion and setting it makes none. Goals holding more than an account, or than the whole, is a failed check, never shrunk to fit. Every answer ends with the split as it stands. Wealth › Net worth and Plan both show it. **Dry-runs by default**; the document is copied to a timestamped .bak first.
輸入參數path必填commitmonthsbucketsaccounts
Pause goals for a number of months, or start them again, **several in one call**. A paused goal is skipped by the projection and gives its monthly amount back to what is left in Plan for those months; it is not deleted and keeps its history. `months` counts this month: 1 skips this month only. 0 starts the goal again. Plan's own bucket is left alone. **Dry-runs by default**; the document is copied to a timestamped .bak first.
輸入參數path必填goals必填commit
settle_set_aside
Is it still set aside?
Answer, for a goal, whether money **set aside** (promised to it but still inside the bank) is still there. A set-aside amount nobody has confirmed for more than 90 days is the one that is asked about; `goal_projection` lists them. `still_there: true` records today against them so they are not asked about again for 90 days. `false` withdraws them from the goal, with the reason written, so the balance stops counting money that is gone. Several goals in one call. **Dry-runs by default**; the document is copied to a timestamped .bak first.
輸入參數path必填goals必填commit
suggest_goal_amounts
Suggest a split
Suggest each goal's monthly amount: goals by nearest date (no kind of goal goes first), each taking what gets it there on its date until what Plan has for goals runs out (what they take now plus what is left after every bucket). A goal with no date is sized as a year of what is missing; a reached, target-less or paused goal gets nothing. The preview is the suggestion; `commit: true` writes it through the same rule a typed amount on Goals uses, so the rest of the split moves with it. **Dry-runs by default**; the document is copied to a timestamped .bak first.
輸入參數path必填commit
set_goal_amounts
Set what goes toward goals and debts each month
Set the monthly amount toward a goal, or the extra on a debt: the Plan bucket for it, in the default plan. The difference comes from (or goes back to) Plan's Everything else, as on the Goals and Debts screens; a goal or debt with no bucket gets one. **Dry-runs by default**; the document is copied to a timestamped .bak first.
輸入參數path必填commitamounts必填
set_opening_balances
Set accounts' opening balances
Say what each bank or cash account **held before its first statement**, and the day that stands for everything before — **several in one call**. A document cannot read this off a file. The answer shows the check it buys: opening, plus what the statements moved since, has to equal the balance the last statement ended on, and the row says by how much when it does not. `clear: true` forgets one. A credit card has none. **Dry-runs by default**; the document is copied to a timestamped .bak first.
輸入參數path必填commitaccounts必填
Add debts — **several in one call**. A debt is the original principal plus a stream of payments; **the balance is derived, never stored**, so there is nothing here that sets one. Optionally set how payments are discovered: a merchant pattern for a standing order, or a category. **Dry-runs by default**; the document is copied to a timestamped .bak first. Editing, closing and deleting a debt are app-only, as is promoting a card installment group.
輸入參數path必填debts必填commit
record_debt_payments
Record debt payments
Record payments against a debt — **several in one call**. The amount is a magnitude; the direction is known. A payment is **all principal** unless it names an interest portion, because v1 has no amortization schedule. Paying more than is outstanding is allowed and is flagged. **Dry-runs by default**; the document is copied to a timestamped .bak first. Payments the app discovered at import are a separate thing and are confirmed in the app.
輸入參數path必填commitpayments必填
guide_status
Guide: what to set up, and what is done
In a document that keeps the ledger: the places worth setting up, in order — reading statements and paychecks, making the list of categories yours, giving each merchant a category, the bills, the plan, the opening balance and the cushion — with which are done and what the document shows for each, and the tool that does each one. The same list and the same checks as the app’s Guide me. Call it after create_document, or when the user asks what is left to set up. Reads the document; changes nothing.
輸入參數path必填
update_account
Rename an account or set its last 4 digits
Give an account a real name and the last 4 digits of its card or number. The digits are what matches an imported statement to the right account, and what identifies a card charge on a bank statement, so they are worth asking for. Account ids come from document_info.
輸入參數kindnamepath必填last4account_id必填
choose_categories
Archive or restore categories
Put categories away, or bring them back. Give `archive_ids` and `restore_ids` to say exactly which — these work at any time. On a document that holds no account and no transaction yet, `keep_ids` is also accepted and archives everything not listed, which is the fast way to trim the shipped list; it is refused afterwards, when one forgotten id would archive a category out from under a year of transactions. Archiving is not deleting — the history stays and comes back with it — and system categories are never touched. Ids come from category_tree. On a document that already holds work it takes a timestamped .bak first.
輸入參數path必填keep_idsarchive_idsrestore_ids
create_category
Create categories
Add categories the document does not have — **a list of them in one call**. `choose_categories` only keeps or archives the ones already there, so this is what answers "none of these fit". Give a `parent_id` or `parent_name` to put it under another, in which case it takes the parent’s kind and `kind` is ignored. Categories nest three levels at most (Living → Utilities → Electricity): a top-level category is a heading — Living, Lifestyle, Income — and a Plan bucket for it counts everything under it. Entries are applied in order, so a parent and its children go in the same call. If a category of the same name already sits in the same place nothing is created: a live one is reported back with its id, an archived one is brought back rather than twinned. Writes immediately — the change is additive, so there is nothing to preview.
輸入參數path必填categories
edit_categories
Rename, colour, move, merge, archive or delete categories
Change categories that exist — **a list of edits in one call**. Per entry: `name`, `parent_id` (any category of its kind with room — three levels at most — or null for the top level), `move` (up or down), `archived`, `monthly_target` (major units, null clears — the category’s Plan bucket in the plan in use: one figure, shared with Plan), `color` (category-1 … category-11, null for automatic), `icon`, or, alone in the entry, `merge_into` (a category id) or `delete: true` — with `move_to` (a category id of the same kind) when rows, rules, subcategories, Plan buckets, bills, debts or goals still use it. This is the Categories screen’s rename, colour, move, merge, archive and delete, and its Plan bucket. A heading (Living, Income…) is a top-level category, renamed and moved like any other. Adding categories is `create_category`. **Dry-runs by default**: it says what each entry would do and which it refuses (a taken name, a fourth level, a delete with nowhere to move what uses it) and writes nothing until you call again with `commit: true`. The document is copied to a timestamped .bak first. Ids come from `category_tree`.
輸入參數path必填editscommit
set_excluded_from_totals
Leave rows out of the totals, or count them again
Leave transactions out of every total — **a list of ids in one call** — or count them again with `excluded: false`. A row left out stays in the ledger and is shown marked; it is simply not summed. This is what the Transactions screen’s row menu does: a card bill the bank statement shows as one charge is the same money as the card statement’s rows, and counting both doubles the month. **Dry-runs by default** and says how much it would take off the totals; commit with `commit: true` after the user has said yes. The document is copied to a timestamped .bak first. Ids come from `transactions`.
輸入參數path必填commitexcludedtransaction_ids必填
set_transaction_notes
Add, edit or clear notes on transactions
Write the note on a transaction — **a list in one call**. An empty `note` clears it. This is the row menu’s *Add a note*: a line about one charge that is worth remembering. **Dry-runs by default**; commit with `commit: true` after the user has seen the table. Ids come from `transactions`.
輸入參數path必填notes必填commit
set_transaction_holdings
Say what a transaction’s cash turned into
Name the holding a transaction bought or sold, and how much of it — **a list in one call**. Such a row is cash turning into a holding inside one account (a broker buying a fund, a savings account opening a deposit): it counts in neither came in, went out nor moved, and the holding’s quantity is the sum of its rows’ quantities. `quantity` is a count of the thing, never money — bought is positive, sold negative, up to six places. `holding: null` clears both. The holding must sit at a place that names the row’s own account, or at one that names none, and is never an account’s own cash. Holdings and their ids are listed by `savings`; transaction ids by `transactions`. **Dry-runs by default**; the document is copied to a timestamped .bak first.
輸入參數path必填rows必填commit
transfers
Transfers between the person’s own accounts
Lists the transfers between two of the person’s own accounts that imports have found: a row leaving one account and the same amount arriving in another within a few days. Undecided ones by default; `answer` reads the transfers or the rows that are not one. An answered transfer is neither income nor spending. `answer_transfers` answers them.
輸入參數path必填answer
answer_transfers
Answer transfers between the person’s own accounts
Answers the pairs `transfers` lists — **a list in one call**. `transfer`: one transfer between two of the person’s accounts, so neither row is income or spending (the end on an ordinary account counts as money moved, the end on an account with holdings is in no total). `not_a_transfer`: two rows that only looked like one; both stay what they were and are never suggested again. `undecided` takes an answer back. **Dry-runs by default**; the document is copied to a timestamped .bak first.
輸入參數path必填pairs必填commit
count_card_charges
Count a card payment with no statement as spending
The card payments in a bank statement for a card this document has no statement of — that month of card spending is otherwise missing. Without `cards`, lists them. With `cards` (the last four digits, or `unknown`), counts each card’s payments as the spending they stand for, so Home stops asking; the rows already count, so no figure moves. Reading the card’s statement later still makes them card payments to answer. **Dry-runs by default**; the document is copied to a timestamped .bak first.
輸入參數path必填cardscommit
set_no_pay_months
Say a month had no pay
Says months had no pay — between jobs, unpaid leave — so no paycheck is missing for them: Home stops asking "No paycheck for …" and `paychecks` stops listing them as gaps. `no_pay: false` takes it back. Stores the answer only; no paycheck and no figure. **Dry-runs by default**; the document is copied to a timestamped .bak first.
輸入參數path必填commitmonths必填no_pay
set_card_payers
Say which bank rows pay a card
Say that a bank merchant pays a credit card — **a list in one call**. Give the bank `transaction_id` and the `card_account_id` it pays (ids from `document_info`); omit the card to take the answer back. This is *This pays a card* on a Transactions row and on a By merchant row. Optionally `counts` says whether those bank rows count in the totals: false leaves them out, because the card’s own rows already hold that money. **Dry-runs by default**; commit with `commit: true` once the user has said yes.
輸入參數path必填commitpayments必填
skip_merchants
Skip merchants for now, or put them back
Set merchants aside in the By merchant queue — **a list in one call** — or put them back with `skip: false`. "I do not know what this is" is a real answer to a cryptic bank transfer; a skip writes nothing to any transaction. Names are the normalized ones `uncategorized_merchants` prints. **Dry-runs by default**; commit with `commit: true`.
輸入參數path必填skipcommitmerchants必填
assign_category
Assign categories to transactions
Categorize the transactions with no category — **many merchants in one call**. Send every merchant-to-category decision you have worked out as one `assignments` list rather than calling this once per merchant. **Dry-runs by default**: it returns what the whole batch would change and writes nothing until you call it again with commit true, so show the user the table and let them answer once. Rows categorized by hand are never touched, rows assigned by a rule are left to the rule, and every row it does change is marked AI so the batch stays findable. The document is copied to a timestamped .bak before anything is written. A row covered by two assignments refuses the batch rather than picking one.
輸入參數path必填commitassignments必填
note_merchant_suggestions
Note what was suggested about merchants
Remember what this conversation suggested for merchants that were **not** categorized — a category the user declined, or a name nobody could identify — so the next conversation does not ask about the same names again. Shown in the Asked about column of uncategorized_merchants, cleared automatically when a merchant is categorized. Writes immediately: it changes no transaction, no category and no rule.
輸入參數path必填suggestions必填
create_rule
Create a categorization rule
Create a standing rule that categorizes matching transactions into a category, now and on every future import. **This is the widest write here** — it reaches the whole history — so it needs two calls: the first returns the transactions the rule would match plus a confirmation token, and the second must quote that token back. Show the user the matches before you commit; the token expires the moment the match set changes. Rows categorized by hand are never touched.
輸入參數kindnamepath必填commitpatternaccount_idamount_maxamount_mincategory_id必填confirmationapply_to_existing
statement_preview
Look at a statement file
Read a card or bank statement (.xlsx, .xls, .csv) and report what is in it: which sheet, where the header row is, what each column looks like and what the app guesses it is, the first rows verbatim, and whether a reader already recognises these columns. Changes nothing — call this before import_statement so the user can confirm the columns. **The user maps the columns; the app only guesses**, so show them this and ask.
輸入參數file必填path必填sheetcolumnsheader_rowamount_convention
import_statement
Import a statement
Import a card or bank statement into an account, through the app’s own pipeline: the same parse, the same dedupe by natural key, the same rules applied on commit. **Dry-runs by default** — it reports what it would import and how the parse compares with the total the file prints for itself, and writes nothing until commit is true. **A file whose printed total does not reconcile is refused**, because importing over it puts numbers in the ledger the file itself says are wrong. Re-importing the same statement is safe: duplicates are matched and skipped.
輸入參數file必填path必填sheetcommitcolumnsaccount_idheader_rownew_accountsave_mapping_asamount_conventionignore_printed_total
record_holdings_statement
Record a statement of an account with holdings
Write what a broker’s (or any account with holdings’) statement printed — **the whole file in one call**: its movements onto the account, and its holdings table as dated readings. **You read the file** — a PDF, usually — and send what it printed; nothing here parses it. Each row is the **value date**, the description as printed, the signed `amount` (a buy negative, a deposit or a sale positive; a fee printed in its own column is already inside the amount), and `cash_after`, the cash balance the statement printed after it. A buy or a sale also names the `holding` and the `quantity` (bought positive, sold negative). **The balances are a check**: each must be the one before it plus the row, and the batch is refused at the first that is not — a misread figure. **They are also each row’s key** (date, amount, balance after), so an overlapping file — a half-year export repeating the months — adds only what is new, and the same file again adds nothing. A row of amount 0 naming no holding (the broker’s tax bookkeeping) is left out. `holdings` is the holdings table: per holding its `value`, and when printed its `quantity`, `cost_basis` and `price` (one unit, in major units — a price printed in agorot is divided by 100); `cash` is the cash line. Each is checked: the cost against what the holding’s rows put in by `as_of`, the quantity against theirs, quantity × price against the value, and the cash against the account’s rows. Holdings are named by id or name (`savings` lists them); a name the place does not have is made there. **Dry-runs by default**; the document is copied to a timestamped .bak first.
輸入參數cashfile必填frompath必填rowsas_of必填placecommitcurrencyholdingsaccount_idnew_account
frame_copy
The document, for a view drawn in the conversation
Not for a model: a view drawn in the conversation calls it with its own key to fetch its document, and any other call is refused. Read rows with `format_rows`.
輸入參數key必填path必填since