create_transactions
Create many double-entry transactions atomically from a TSV table, with validation, duplicate detection, and dry-run support.
Instructions
Create MANY transactions in one atomic command (bulk entry).
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 — exactly like calling
create_transaction without splits::
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 always the book default — for a transaction denominated in another currency, use
create_transactionwith itscurrencyparameter.
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 (as increate_transaction).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, reason. status iscreated|rejected|would_create(dry_run); reason is a code likeduplicate_detectedor the validation message.duplicates(only when matches exist):ref, confidence, guid, date, amount, description, signals— the columnscreate_transactionemits, keyed back to the offendingref. Σ(dup_count) equals the duplicates row count.
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 |