enter_statement
Enter and reconcile a complete bank or card statement in one atomic save, using a dry-run to classify each line as new, matched, or ambiguous before committing.
Instructions
Enter a COMPLETE bank/card statement in one atomic call: create the new lines, claim the ones already in the book, and reconcile everything against the closing balance — all in one save, or nothing at all.
THE WORKFLOW (two calls around your judgment):
dry_run=true(the DEFAULT) — transcribe the statement and get back a classification of every line: NEW (not in the book), MATCH (an existing unreconciled split corresponds), OVERLAP (already reconciled), AMBIGUOUS (several candidates). MATCH/AMBIGUOUS rows come with the candidate's full annotation (date, amount, description, notes, memo, short GUID) so you can adjudicate each one.Rule every MATCH/AMBIGUOUS row yourself, adapt annotations, confirm with the user.
dry_run=false— NEW rows now carry interpreted description/notes and counter-splits; MATCH rows carrymatch=<split guid>claims. The server enters, claims, reconciles every statement-touched split atstatement_date, and saves once.
TRANSCRIBE, DON'T INTERPRET (dry-run): amounts and balances
go in EXACTLY as the statement prints them — for credit
cards too (charges positive, balance as amount owed). The
server applies the sign convention from the account's type;
you never flip a sign. The gate
opening + sum(lines) == closing must hold or the call
rejects: transcribe every line.
INPUT — lines is a TSV block. Header: ref, date
first, then any order of description, notes,
raw, match, amount (required), then optional
amt, acct, memo, qty counter-split groups (batch
grammar). The statement account's own leg is SYNTHESIZED —
never a column. Dry-run typically needs only::
ref<TAB>date<TAB>raw<TAB>amount
1<TAB>2026-07-03<TAB>POS DEBIT WHOLEFDS #123<TAB>-87.12raw= the verbatim statement line; it lands on the bank leg's memo (provenance).description/notesare your interpretation (commit).match= the split GUID this line claims instead of creating (from the dry-run candidates table). Claim rows may also carryraw(updates the claimed split's memo) andnotes(updates the transaction's notes), and END at their last fixed column — they take no split cells. The claimed amount must equal the line amount exactly — fix the book first if they disagree.A commit row with no counter-splits auto-fills from the most recent same-description 2-split transaction, adapted to the line amount (marked
auto_filled_from:<guid>). The precedent must have exactly one leg on the statement account and no cross-commodity leg — anything else rejects with "supply explicit counter-splits". Explicit counter-splits must not name the statement account (its leg is synthesized).
SAFETY: the account's reconciled balance must tie to
opening_balance (a prior unentered statement blocks
commit), every created-vs-existing exact overlap must be
explicitly claimed or forced, and the projected closing tie
is verified BEFORE anything is written. The two force
flags are INDEPENDENT: force_base=true lands onto an
untied opening base (the consequent tie discrepancy is
recorded, and duplicate detection STAYS ON);
force_duplicates=true creates past exact twins you
have adjudicated as distinct. Neither bypasses the
statement's own self-check. After the save, the reconciled
balance is read back and verified against the tie.
OUTPUT (dry-run): summary (class counts), lines
(ref, class, cands, note — the note is the resolved
disposition: the guard's refusal coaching verbatim, the
auto-fill prediction, or "will claim …"), candidates —
SELF-CONTAINED comparison rows sorted
strongest-correspondence first (ref, candidate_guid, confidence, state, date_new/old + delta, amt_new/old + delta, cur, desc_new/old, notes_old, memo_old, cat_new/old, split_match, signals; _new = the statement line in
book convention, _old = the existing split — never
re-read your own input; cur is structurally blank on
this surface), plus warnings (only when present;
candidates likewise) and tie — the projected
reconciled balance vs the closing, with a count of rows
this exact payload would refuse at commit. The dry-run
rehearses the SAME disposition procedure commit runs —
force included. The tie is the only verdict;
MATCH/AMBIGUOUS rows are yours to rule.
OUTPUT (commit): results (ref, status, guid, note;
status is created | claimed | skipped_duplicate, or on a
refused statement rejected | statement_aborted — the note
column carries the row's coaching and
auto_filled_from:<guid> markers), plus, on success
only, the new reconciled balance and the tie (a refusal
returns just summary + results).
Args: account: Statement account ref (path, %short, or GUID). BANK/CASH/ASSET/CREDIT/LIABILITY only. statement_date: The statement's closing date (YYYY-MM-DD); every touched split reconciles at it. opening_balance: Opening balance, exactly as printed. closing_balance: Closing balance, exactly as printed. lines: The TSV block described above. dry_run: DEFAULT TRUE — the rehearsal is the workflow. force_base: Land onto an untied opening base; the tie discrepancy is recorded, twin detection stays on. force_duplicates: Create past exact unclaimed twins (you adjudicated them as distinct charges). show_all: Dry-run only. Lines with MEDIUM/HIGH candidates suppress their LOW amount-coincidences (the cands column notes "+N LOW suppressed"); show_all=true lists everything.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| lines | Yes | ||
| account | Yes | ||
| dry_run | No | ||
| show_all | No | ||
| force_base | No | ||
| statement_date | Yes | ||
| closing_balance | Yes | ||
| opening_balance | Yes | ||
| force_duplicates | No |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| result | Yes |