Use this reference to look up bea commands and their behavior. For your first ledger, follow the CLI quick start. For bank files, use the import walkthrough.
Commands at a glance
| Command | Purpose |
|---|---|
bea init [DIRECTORY] | Create a ledger with common accounts |
bea add TYPE | Add a dated directive |
bea add transactions --from FILE.json | Add a transaction batch |
bea import SOURCE | Preview an export; add --apply to write |
bea list TYPE | List and filter directives |
bea check | Validate the complete ledger |
bea format [PATH] | Align a file or recursively format a directory |
bea query [BQL] | Run a query or open the interactive query shell |
bea report TYPE | Produce financial reports |
bea ask [QUESTION] | Use optional hosted AI assistance with a local ledger |
bea cloud … | Sign in and manage hosted ledgers |
bea upgrade [--check] | Upgrade with the owning package manager, or check for an update |
Global options and paths
Global options go before the command:
bea --file ~/my-books/main.bean check
bea --json list transaction --limit 100| Option | Behavior |
|---|---|
--file / -f PATH | Select the root ledger; overrides BEA_FILE and ./main.bean |
--json | Structured output; also disables CLI prompts |
--no-input | Disable prompts; missing required input exits 2 |
--yes / -y | Confirm operations such as cloud deletion; does not grant AI write permission |
--debug | Include exception tracebacks |
--version | Show the installed version without a network request |
--help / -h | Show help; also available on subcommands |
--show-completion | Print shell completion |
--install-completion | Install shell completion |
--shell NAME | Select bash, zsh, fish, powershell, or pwsh instead of detecting the shell |
init creates its own directory/file target and ignores BEA_FILE. It accepts global --file instead of its directory argument. format uses its own positional target, defaulting to the working directory. Global --file does not choose the formatting target.
Create a ledger
bea init [DIRECTORY] defaults to the current directory. A directory creates main.bean; a .bean or .beancount path names the new file directly.
| Option | Behavior |
|---|---|
--currency / -c SYMBOL | Operating currency; required unattended, interactive default USD |
--date YYYY-MM-DD | Earliest history/opening date; otherwise a prompt or today |
--opening-balance "ACCOUNT NUMBER" | Repeat for template asset/liability accounts; amounts use the operating currency |
The template opens Assets:Checking, Assets:Savings, Assets:Cash, Liabilities:CreditCard, Income:Salary, Income:Interest, Expenses:Groceries, Expenses:Dining, Expenses:Rent, Expenses:Transport, Expenses:Utilities, Expenses:Fees, and Equity:OpeningBalances.
Opening balances offset against Equity:OpeningBalances. Debt is negative. Currency input is uppercased. Custom symbols are allowed; a symbol that is not three uppercase letters triggers a typo warning. This is not an ISO currency-registry check.
Existing files are never overwritten. New files use owner-only permissions, mode 0600 on POSIX. Later add, import, and format writes preserve permissions and respect read-only destinations.
Add transactions
bea add transaction -n "Groceries" --payee "Corner Market" \
-p "Expenses:Groceries 30" -p "Assets:Checking" \
--flag '!' --tag household --link receipt-42 --meta 'receipt:IMG_42.jpg'| Option | Behavior |
|---|---|
--posting / -p POSTING | Required; repeat for each posting |
--date YYYY-MM-DD | Default today |
--flag CHARACTER | Default *; use ! to mark a transaction for review |
--payee TEXT | Optional other party |
--narration / -n TEXT | Optional purpose; omitted text lists as (no narration) |
--tag TAG, --link LINK | Repeatable; optional leading # or ^ is accepted |
--meta KEY:VALUE | Repeatable transaction metadata |
--into FILE | Write an included file while validating the root |
--allow-errors | Explicitly permit semantic validation errors; syntax must still parse |
One posting may omit its amount. Numbered postings may omit currency when an account has one allowed currency or the ledger has one compatible operating currency. Otherwise, supply the symbol.
Native posting syntax supports arithmetic such as 84/2 EUR, costs such as {100 USD}, total costs {{1000 USD}}, and prices @ or @@. Use decimal amounts such as 1000, not exponent notation such as 1e3.
A currency exchange needs its actual transaction rate. For example, post 100 EUR @ 1.08 USD to an account open in EUR and -108 USD to checking. An investment purchase can post 2 AAPL {100 USD} to an account open in AAPL and -200 USD to checking. Add dated price quotes when reports need market valuation.
Metadata accepts bare strings such as --meta 'receipt:IMG_42.jpg'. Native numbers, booleans, dates, and amounts retain their types. Examples include --meta 'reviewed:TRUE', --meta 'received:2026-08-03', and --meta 'fee:2.50 USD'. Inner quotes force a string: --meta 'code:"1234"'. Keys must be distinct; filename and lineno are reserved.
Single adds, bulk adds, and imports replace line breaks in payees, narrations, and string metadata with spaces. Quotes and backslashes retain their contents.
Add other directives
All these commands require --date YYYY-MM-DD. They also accept --into FILE and --allow-errors.
| Type | Required fields | Additional options |
|---|---|---|
open | --account / -a | Repeat --currency / -c to restrict currencies |
close | --account / -a | — |
balance | --account / -a, --amount "NUMBER CURRENCY" | --pad-from ACCOUNT, --pad-date YYYY-MM-DD |
pad | --account / -a, --source / -s | — |
note | --account / -a, --comment / --message / -m | — |
event | --type / -t, --description / -d | — |
price | --currency / --commodity / -c, --amount "NUMBER CURRENCY" | Currency names the commodity being priced |
commodity | --currency / --commodity / -c | — |
document | --account / -a, --filename / --path | Repeated --tag and --link |
custom | --type / -t | Repeated --value / -v KIND:VALUE |
Account names have a capitalized root and colon-separated segments. Each subaccount starts with an uppercase letter or digit. Beancount supports Unicode letters and configured root names.
A balance checks the account at the start of its date. Tolerance syntax is supported, such as --amount "1538 ~ 1 EUR". The tolerance must be nonnegative.
Use add balance --pad-from Equity:OpeningBalances to write a pad and its balance assertion together. The pad defaults to the previous day; --pad-date can select another earlier day. Both accounts must be active. A standalone pad needs a later balance to consume it. --allow-errors can stage that intermediate state but cannot bypass an invalid pad account.
add price skips an exact date/commodity/price duplicate across the root and its includes. It exits 0 and identifies the existing location. Different dates or prices are new additions.
Document paths resolve beside the file containing the directive. With --into years/2026.bean, --filename receipt.pdf means years/receipt.pdf, not a file beside your shell's working directory.
Custom value kinds are text, number, amount, account, bool, and date. For example, a budget can use --value "text:travel" --value "amount:500 USD".
Bulk JSON input
bea add transactions --from transactions.json accepts a JSON array:
[
{
"date": "2026-08-04",
"narration": "Groceries",
"postings": [
{ "account": "Expenses:Groceries", "amount": "45.00 USD" },
{ "account": "Assets:Checking" }
],
"meta": { "receipt": "R-43", "reviewed": true }
}
]Each transaction requires date and postings. Optional fields are flag, payee, narration, tags, links, and meta.
A posting uses either amount or units, such as {"number":"45.00","currency":"USD"}. Omit both for the balancing posting. Posting fields also include cost, price, flag, and meta. Costs contain number and currency, with optional date and label. Prices contain number and currency.
Use strings for decimals. Metadata uses ordinary strings and booleans, or tagged values such as {"kind":"number","value":"1.125"}, {"kind":"date","value":"2026-08-04"}, and {"kind":"amount","number":"2.50","currency":"USD"}. The optional transaction source location is never written as metadata.
The default is an atomic batch: any rejected row leaves the ledger unchanged and exits 1. --partial writes a valid subset and still exits 1 if any rows are rejected. JSON errors describe the outcome in error.result; row indexes there are zero-based. Human row numbers are one-based.
Bulk add accepts --into and --allow-errors. It does not deduplicate. Use bea import for bank-export review.
Split ledgers and write safety
Keep --file pointed at the root. Add --into to select an existing included file:
bea --file ~/my-books/main.bean add transaction --into 2026.bean \
--date 2026-08-02 -n "Groceries" \
-p "Expenses:Groceries 30" -p "Assets:Checking"The destination is relative to the root directory. It must already be included; naming an unrelated file is refused. Add commands, imports, and interactive AI writes support this separation.
Writes validate the complete candidate ledger, including plugins and cost-lot booking. A concurrent change to the root or its include graph exits 4. A read-only destination exits 3. Successful additions use the same alignment as bea format, which may realign existing columns in that destination.
List directives
bea list TYPE supports the eleven types: transaction, open, close, balance, pad, note, event, price, commodity, document, and custom.
| Option | Applies to | Behavior |
|---|---|---|
--limit / -l N | All types | Positive limit; default 50 |
--from-date, --to-date | All types | Inclusive YYYY-MM-DD bounds |
--allow-errors | All types | Permit partial data despite loader errors |
--account / -a TEXT | Transaction, open, close, balance, pad, note, document | Case-insensitive account substring |
--currency / -c SYMBOL | Price, commodity | Case-insensitive exact symbol; price filters its base commodity |
--sort newest/oldest | Transaction | Default newest; applied before the limit |
--flag CHARACTER | Transaction | Filter entries such as ! before the limit |
--details | Transaction | Render Beancount syntax, every posting, metadata, and source locations |
Other directive types retain chronological order. An account-filtered transaction table labels its amount column MATCHING POSTING AMOUNTS. Details and JSON still include all postings of each selected transaction. Details render loaded entries, including inferred amounts; they are not raw source excerpts.
Check, format, and query
bea check validates the root and includes. It exits 1 for ledger errors and has no --allow-errors option. Queries, lists, and reports also reject loader errors unless you explicitly pass their --allow-errors option.
Formatting takes a .bean/.beancount file or a directory. A directory is searched recursively.
| Formatting mode | Writes? | Exit behavior |
|---|---|---|
bea format PATH | Yes | 0 after success |
bea format PATH --dry-run | No | 0 even when files would change |
bea format PATH --check | No | 1 when formatting is needed; 0 when clean |
Every mode reports syntax errors by file and line, skips those files, and exits 1. A recursive normal run can still format the valid files. JSON reports scanned, formatted, skipped, dry_run, and check, under error.result on failure.
bea query "BQL" runs a Beancount query. Omitting BQL opens an interactive shell; exit or quit closes it. A query argument is required unattended. BQL's default table has one row per posting. Query tables retain precision. Empty results print (no rows) on stderr; JSON returns an empty data.rows and column metadata in data.columns.
Financial reports
| Report | Output |
|---|---|
bea report overview | Assets, liabilities, income, expenses, net worth, and interval series |
bea report income-statement | Income/expense trees, net profit, and period rows |
bea report balance-sheet | Asset/liability/equity trees and derived reconciliation |
bea report trial-balance | Account balances |
All reports accept --conversion / -x, --time / -t, --account / -a, and --allow-errors. All except trial balance also accept --interval / -i: monthly by default, or quarterly, yearly, weekly, or daily.
Time filters include a year, month, date, quarter, week, or range, such as 2026, 2026-08, 2026-08-31, 2026-Q3, 2026-W32, or "2026-01 - 2026-08". Relative periods include year, quarter, month, week, day, and offsets such as month-1. Account filters retain every posting of a matching transaction.
Conversion defaults to the ledger's sole operating currency. Otherwise, it defaults to units, keeping commodities separate. at_cost uses acquisition costs. at_value uses market values with a cost fallback.
An explicit currency conversion needs prices on or before every valuation date, including interval dates. A missing-price error names the actual gap, such as No EUR → USD price on or before 2026-01-31. A later quote cannot fill an earlier gap. Add a historically appropriate price, use --conversion units, or choose --allow-errors to inspect partial values.
Partial reports preserve source currencies and mark combined totals unavailable. JSON includes valuation: "partial", missing_prices, and missing_price_dates. Affected net-profit/net-worth totals are null in the requested currency.
Income, liabilities, and equity normally use negative Beancount signs. Net profit is -(income + expenses), positive for a gain. The same convention applies to income-statement period rows. Balance-sheet reconciliation is derived for the report; it writes no directives. equity_reconciled identifies whether a complete reconciliation is available.
Report JSON also identifies the period, exclusive end date, as-of date, conversion, account filter, and ledger validation status. Check those fields before comparing totals.
Optional AI assistance
bea ask needs both the ask extra and Beancount.io credentials from bea cloud login or BEA_TOKEN. The default Homebrew installation omits AI dependencies. Homebrew users can run:
bea cloud login
uvx --from 'beancount-io[ask]' bea ask "What did I spend last month?" --printFor a uv installation, install beancount-io[ask] and run bea ask directly. --print / -p answers once and exits. Otherwise, a terminal session is interactive, and an optional question pre-fills its input. Non-interactive use requires a question. JSON mode is not supported.
Queries run locally. Questions, skill context, and tool results go to the hosted Beancount.io AI service. Interactive writes are previewed, confirmed, validated, and written atomically. They accept --into. Global --yes does not grant AI write permission. One-answer mode does not apply proposed writes.
Ask reads NAME/SKILL.md from .agents/skills/ in the working directory and from skills/ in the user configuration directory. Project definitions win by name. Each file needs YAML name and description fields. Full instructions load on demand.
Hosted ledgers
| Command | Options and behavior |
|---|---|
bea cloud login | Interactive browser/device sign-in |
bea cloud logout | Attempts remote logout and clears stored credentials |
bea cloud status | Account, credential source, and expiry |
bea cloud ledger list | --page defaults to 1; --limit defaults to 50, API maximum 100 |
bea cloud ledger show OWNER/NAME | Inspect a hosted ledger |
bea cloud ledger create NAME | --description / -d, --private / --public; private by default |
bea cloud ledger clone OWNER/NAME | SSH clone; optional --dir PATH |
bea cloud ledger delete OWNER/NAME | Permanent deletion; confirmation or global --yes required |
Creation also accepts --clone and --dir. Git and SSH access are required to clone. If cloning fails after creation, the hosted ledger still exists. Local commands do not upload your ledger automatically. There is no global --ledger option.
JSON and exit codes
Global --json puts successful results on stdout:
{
"bea": "0.1.0",
"target": { "file": "/home/alice/my-books/main.bean" },
"data": [],
"truncated": false,
"limit": 50
}bea is the installed version; data depends on the command. Targets identify a file, directory, server, or no target. Included writes also identify into. Decimal amounts and dates use strings. Limited lists include limit and truncated.
Failures write {"error":{"category":"validation","message":"…","exit_code":1}} to stderr. The error can also include details, result, a backend request_id, and a traceback with --debug.
| Code | Category | Meaning |
|---|---|---|
| 0 | — | Success, including previews and intentional duplicate skips |
| 1 | validation | Ledger/schema error, formatting check failure, or other runtime failure |
| 2 | usage | Invalid arguments, missing target/input, or missing optional dependencies |
| 3 | auth | Authentication or permission failure |
| 4 | conflict | Concurrent edit, import review required, existing init target, or uncertain remote write outcome |
Check error.result before retrying a mutation. A partial batch can write accepted rows, recursive formatting can change valid files, and create-and-clone can create a hosted ledger before exiting nonzero.
CLI prompts are disabled by --no-input, JSON mode, non-terminal stdin, or truthy CI. Cloud deletion still needs explicit --yes. Imports need an explicit duplicate decision when matches need review.
Output exceptions: Ask rejects JSON; cloud login needs interaction; successful cloud logout and clone return no JSON success object. Help, version, and completion retain text output. upgrade can stream its package manager's output to stderr, including in JSON mode.
Settings, updates, and stored state
| Environment variable | Purpose |
|---|---|
BEA_FILE | Default root ledger after --file |
BEA_CONFIG_DIR | Override the user configuration directory |
XDG_CONFIG_HOME | Otherwise use $XDG_CONFIG_HOME/bea, falling back to ~/.config/bea |
XDG_CACHE_HOME | Cache directory base; otherwise ~/.cache/bea |
BEA_TOKEN | Hosted credential override; takes precedence over stored credentials and is not saved |
BEA_API_URL | API base; default https://api.v3.beancount.io |
BEA_DASHBOARD_URL | Browser sign-in base; default https://beancount.io |
BEA_NO_UPDATE_NOTIFIER | Disable passive update notices when truthy |
CI | Disable CLI prompts and passive update notices when truthy |
Truthy values are 1, true, yes, and on, ignoring case and surrounding whitespace. Configuration state includes credentials, Ask prompt history, user skills, remembered importer paths, and update-check caches. Write locks live under the cache directory's locks/, outside your ledger directory.
bea upgrade --check reports versions and the installation method without upgrading. bea upgrade invokes brew upgrade bea, uv tool upgrade beancount-io, or pipx upgrade beancount-io. Editable installs receive manual update guidance. Passive checks run at most once a day in interactive installed copies; explicit upgrade --check still runs when the passive notifier is disabled.
Uninstall with the matching manager: brew uninstall bea, uv tool uninstall beancount-io, or pipx uninstall beancount-io. Your ledger files and user configuration remain.
Common fixes
| Symptom | Next step |
|---|---|
| No ledger found | Select --file PATH, enter the ledger directory, or use bea init for new books |
| A global flag says “No such option” | Move it before the command, as in bea --file main.bean check |
| An account is unknown | Open it with bea add open --date YYYY-MM-DD --account ACCOUNT |
| An account is inactive | Read the cited open/close dates; correct the transaction date or account history |
| A pad is unused | Complete its later balance assertion; use add balance --pad-from for an atomic pair |
| Currency conversion is incomplete | Add prices covering the dates named in the error, or inspect units |
| A document cannot be found | Resolve its path beside the directive's file, including an --into destination |
| A ledger changed during a write | Inspect the new content, then retry from a fresh preview |
| Shell detection failed | Specify a shell, such as bea --shell zsh --show-completion |
Use bea COMMAND --help to inspect your installed version. The source repository reference contains additional examples and the exact directive model definitions.