Skip to content

Transaction Directive

Overview

The transaction directive records a financial exchange between two or more accounts. It is the fundamental directive for recording economic activity.

Syntax

transaction = date WHITESPACE txn_flag
              [WHITESPACE txn_strings]
              [WHITESPACE tags_links]*
              (NEWLINE posting)+
              (NEWLINE metadata)*

txn_flag    = "*" | "!" | "txn" | "P" | "#"
txn_strings = string [WHITESPACE string]
posting     = WHITESPACE [posting_flag] account
              [WHITESPACE amount [cost_spec] [price_annotation]]

Components

Date

The transaction date in ISO 8601 format (YYYY-MM-DD).

Flag

Indicates transaction status:

Flag Meaning Description
* Complete Transaction verified as correct
! Incomplete Needs review or correction
txn Complete Keyword equivalent to *
P Padding Auto-generated by pad directive
# Linked Used by some plugins

Payee and Narration

Transactions accept zero, one, or two strings:

; No strings
2024-01-15 *
  Assets:Cash  -20 USD
  Expenses:Food

; One string (narration only)
2024-01-15 * "Grocery shopping"
  Assets:Cash  -20 USD
  Expenses:Food

; Two strings (payee and narration)
2024-01-15 * "Whole Foods" "Weekly groceries"
  Assets:Cash  -85.50 USD
  Expenses:Groceries

When two strings are provided: - First string = payee (who the transaction is with) - Second string = narration (description of transaction)

Tags (#tag) and links (^link) appear after the strings:

2024-01-15 * "Flight to Berlin" #travel #berlin-trip ^invoice-2024-001
  Expenses:Travel:Flights  -450.00 USD
  Liabilities:CreditCard

Postings

Each posting transfers an amount to or from an account.

Posting Structure

  [flag] account  [amount] [cost] [price]

Posting Flag

Optional flag on individual postings:

2024-01-15 * "Mixed transaction"
  Assets:Checking   -100 USD    ; no flag
  ! Expenses:Food     50 USD    ; flagged as incomplete
  * Expenses:Coffee   50 USD    ; flagged as complete

Amount

A number followed by a currency:

  Assets:Cash  100.00 USD
  Assets:Cash  -50 EUR
  Assets:Cash  1,234.56 CAD

Amounts support arithmetic expressions:

  Expenses:Food  (100 / 3) USD
  Expenses:Food  (50 * 1.08) USD  ; with tax

Elided Amounts

UNDEFINED: See posting.md for the pending elision rule clarification.

2024-01-15 * "Deposit"
  Assets:Checking   1000 USD
  Income:Salary                  ; amount computed as -1000 USD

Multiple elided postings for the same currency is always an error:

; ERROR: Multiple missing amounts for USD
2024-01-15 * "Ambiguous"
  Assets:Checking   100 USD
  Expenses:Food
  Expenses:Coffee

Balancing

Transactions MUST balance: the sum of all posting weights MUST equal zero for each currency.

Weight Calculation

Posting Type Weight
Simple amount The amount
With cost {...} Units × cost per unit
With total cost {{...}} The total cost
With price @ Units × price per unit
With total price @@ The total price

Example

2024-01-15 * "Buy stock"
  Assets:Brokerage   10 AAPL {150 USD}  ; weight: 1500 USD
  Assets:Cash       -1500 USD           ; weight: -1500 USD
  ; Sum: 0 USD ✓

Cost Specification

Costs track the acquisition price of commodities:

; Per-unit cost
Assets:Stock  10 AAPL {150.00 USD}

; Total cost
Assets:Stock  10 AAPL {{1500.00 USD}}

; Cost with date
Assets:Stock  10 AAPL {150.00 USD, 2024-01-15}

; Cost with label
Assets:Stock  10 AAPL {150.00 USD, "lot1"}

; Full specification
Assets:Stock  10 AAPL {150.00 USD, 2024-01-15, "lot1"}

; Merge cost (average all lots)
Assets:Stock  0 AAPL {*}

See costs.md for detailed cost specification documentation.

Price Annotation

Prices record exchange rates without affecting balancing:

; Per-unit price
Assets:EUR  100 EUR @ 1.10 USD

; Total price
Assets:EUR  100 EUR @@ 110 USD

The price annotation does NOT affect the transaction's balance calculation when a cost is also specified.

Metadata

Metadata can be attached to transactions and postings:

2024-01-15 * "Purchase"
  receipt: "scan.pdf"
  category: "groceries"
  Assets:Cash  -50 USD
    vendor-id: "12345"
  Expenses:Food

Transaction-level metadata is indented once; posting-level metadata is indented twice.

Examples

Simple Transfer

2024-01-15 * "ATM Withdrawal"
  Assets:Cash        200 USD
  Assets:Checking   -200 USD

Multi-Currency

2024-01-15 * "Currency exchange"
  Assets:EUR   100 EUR @ 1.10 USD
  Assets:USD  -110 USD

Stock Purchase

2024-01-15 * "Buy Apple stock"
  Assets:Brokerage    10 AAPL {185.50 USD}
  Expenses:Commission  9.99 USD
  Assets:Cash

Split Expense

2024-01-15 * "Dinner with friends" #dinner
  Expenses:Food:Restaurant   (75.00 / 3) USD
  Assets:Receivable:Alice    (75.00 / 3) USD
  Assets:Receivable:Bob      (75.00 / 3) USD
  Assets:Cash               -75.00 USD

Validation

The following conditions produce validation errors:

Condition Error Type
Transaction does not balance ValidationError
Multiple postings missing amounts for same currency ValidationError
Posting references account not opened ValidationError
Posting references account after close ValidationError

Note: See validation/balance.md for rules on empty and single-posting transactions.

Implementation Notes

  1. Parse all postings before computing elided amounts
  2. Validate balance after all amounts are known
  3. Store original precision for display
  4. Apply tolerance rules for balance checking