create_transactions
Create single or bulk GnuCash transactions atomically, with validation, duplicate detection, and optional dry-run to screen before committing.
Instructions
Create transactions in one atomic command (bulk entry) —
the canonical entry tool for one transaction or many. A
single transaction is a one-row batch (the former
create_transaction tool was removed; this replaces it).
INPUT — transactions is a TSV block: a header row, then one
row per transaction. The HEADER DECLARES THE LAYOUT. Base form:
splits are (amount, account) column PAIRS, repeated as wide
as a transaction needs::
ref<TAB>date<TAB>description<TAB>amt1<TAB>acct1<TAB>amt2<TAB>acct2...
1<TAB>2026-05-21<TAB>Gas<TAB>-54.19<TAB>Assets:Checking<TAB>54.19<TAB>Expenses:Auto:FuelTwo opt-in extensions, each activated by naming it in the header (legacy headers parse exactly as before):
PER-SPLIT MEMOS — declare
memosplit columns; splits become(amount, account, memo)TRIPLES::ref<TAB>date<TAB>description<TAB>amt1<TAB>acct1<TAB>memo1<TAB>amt2<TAB>acct2<TAB>memo2 1<TAB>2026-05-21<TAB>Gas<TAB>-54.19<TAB>Assets:Checking<TAB>card #4471<TAB>54.19<TAB>Expenses:Auto:FuelEmpty memo cells mid-row keep their tabs; a row may simply END once its last split's amount and account are present (trailing memo/qty cells are read as empty — no placeholder tabs needed, as above).
PER-TRANSACTION NOTES — declare a
notescolumn directly afterdescription::ref<TAB>date<TAB>description<TAB>notes<TAB>amt1<TAB>acct1...FIELD TARGETING for statement entry:
descriptionis the clean name;notesis what the purchase WAS — interpreted, not transcribed — and is what humans see in GnuCash's double-line register; the bank leg'smemois where the RAW statement line goes (provenance, visible only in expanded split view). Prefer fillingnoteswhenever the description alone doesn't tell the story.PER-TRANSACTION CURRENCY — declare a
curcolumn afterdescription(before or afternotes); an ISO code cell sets THAT ROW's transaction currency, an empty cell keeps the book default::ref<TAB>date<TAB>description<TAB>cur<TAB>amt1<TAB>acct1<TAB>amt2<TAB>acct2 1<TAB>2026-07-15<TAB>USD Card Payment<TAB>USD<TAB>-500<TAB>Assets:USD Checking<TAB>500<TAB>Liabilities:USD CardWith
cur, the row'samtcells are in that currency and must balance in it. Use it when NO leg is in the book's default currency (a USD-to-USD transfer inside a CNY book needs no invented CNY values and no qty). Splits on accounts of any OTHER commodity still needqty. The currency must already exist in the book, andcurcannot combine with an auto-fill row.PER-SPLIT QUANTITY — declare
qtysplit columns for splits whose ACCOUNT commodity differs from the book default (investment shares, foreign-currency accounts)::ref<TAB>date<TAB>description<TAB>amt1<TAB>acct1<TAB>qty1<TAB>amt2<TAB>acct2<TAB>qty2 1<TAB>2026-07-01<TAB>VFIFX Purchase<TAB>-505.17<TAB>Assets:Checking<TAB><TAB>505.17<TAB>Assets:401k:VFIFX<TAB>7.7936amountstays in the book's default currency (the transaction currency — batch never changes that);qtyis the amount in the account's own commodity. An EMPTY qty cell means the account uses the default currency (quantity == amount). A non-default-commodity account with an empty qty rejects that row.PER-SPLIT ACTION — declare
actsplit columns for GnuCash's typed movement tag ("Buy"/"Sell"/"Dividend" on investment legs — desktop convention; "Wire"/"ATM" on bank legs). Same group mechanics asmemo/qty; empty cells skip it. Rarely needed for plain spending.
All extensions combine; when several split fields are
declared, the header's FIRST group fixes their order (e.g.
amt, acct, memo, qty).
AUTO-FILL — a row with NO split cells at all (ends right
after description/notes) reproduces the most recent
transaction with the same description — splits, memos, and
quantities included::
1<TAB>2026-07-01<TAB>Rent
2<TAB>2026-07-01<TAB>NetflixAuto-filled rows are marked auto_filled_from:<guid> in
the results reason column; a row whose description
matches nothing rejects ("no matching transaction to
auto-fill from"). Use dry_run=true to preview what a
batch of auto-fills would book. Perfect for recurring
monthly entries. Transaction notes are NOT copied from
the source (notes are often time-bound — "first
appearance, investigate" must not replicate); supply a
notes cell when the new instance needs one.
ref: YOUR correlation key per row (e.g. 1, 2, 3), unique within the batch. It is echoed back so you can match results to what you sent; the server never reuses or interprets it.date: ISO YYYY-MM-DD.amount/qty: decimal STRINGS (never raw JSON numbers). Each transaction needs=2 splits balancing to zero in the default currency. Rows may differ in width (2 splits vs 3).
The transaction currency is the book default unless the row declares one via the
curcolumn (see PER-TRANSACTION CURRENCY above).
BEHAVIOR — one book-open, one atomic save:
A STRUCTURAL error (unbalanced, unknown account, bad pairs) aborts the WHOLE batch by default; nothing is written. Pass
on_error="skip"to write the good rows and reject only the bad ones.A duplicate rejects ONLY its row;
force=Trueoverrides all blocking duplicates.dry_run=Truevalidates + screens without writing.
OUTPUT — a JSON envelope of two TSV tables joined by ref:
results(always):ref, status, txn_guid, dup_count, max_confidence, reason. status iscreated|rejected|would_create(dry_run, candidate-free rows only) |review_required(dry_run rows with >=1 duplicate candidate — rule each against the duplicates table before committing); reason is a code likeduplicate_detectedor the validation message.max_confidence(HIGH/MEDIUM/blank) is the row's top duplicate candidate — enough for the common keep/drop call without the join.duplicates(only when matches exist): SELF-CONTAINED comparison rows, sorted strongest-correspondence first —ref, candidate_guid, confidence, state, date_new, date_old, date_delta_days, amt_new, amt_old, amt_delta, cur, desc_new, desc_old, notes_old, memo_old, cat_new, cat_old, split_match, signals._new= your proposed row,_old= the existing transaction;cat_*are the category (non-payment) legs asaccount=amount|...;split_match(exact/partial/none) compares them — MEDIUM on date+amount butnoneon category is usually a distinct purchase. Amounts are SIGNED (direction matters: a deposit is not a payment's twin).amt_deltais blank on cross-currency candidates (curnames the candidate's currency exactly when the frames differ);memo_oldandstateblanks mean this surface can't fill them. Never re-read your own input — both sides are in the row. Σ(dup_count) equals the duplicates row count.Dry runs additionally lead with
summary(would-create/ review-required/rejected counts + the homework line) and close witheffects— the projected per-account balance deltas of the rows that would land.
Args: transactions: The TSV block described above. force: Override ALL blocking (HIGH) duplicates this batch. dry_run: Validate + screen, write nothing. on_error: "abort" (default) or "skip" for structural errors.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| force | No | ||
| dry_run | No | ||
| on_error | No | abort | |
| transactions | Yes |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| result | Yes |