Skip to main content

Accounting with AI agents

Give an external AI agent two bookkeeping jobs: import a bank CSV with preview-first review, and answer a balance question from BQL and report output.

Hand an external AI agent two bookkeeping jobs and keep every ledger change under your review: import a bank CSV with a preview-first check, then answer a balance question from query and report output.

Install bea with the CLI quick start. Local import, query, and report commands need no Beancount.io account. Your chat agent needs only its own subscription or API key. Only the built-in bea ask helper needs hosted credentials, and these recipes do not use it.

What the agent does and what bea does

The agent proposes categories and drafts queries. Bea validates the ledger structure and computes the totals. You run every command below yourself. The agent never writes your ledger directly.

Recipe 1: import a statement with agent-proposed categories

Turn a bank export into validated entries. The agent suggests the categorization rules. Preview and duplicate review decide what gets written.

Prerequisites

You need bea and a fresh demo directory. No account and no Python importer.

Prepare the inputs

Create the directory and the ledger. Explicit --file targets keep every command pointed at the demo books.

mkdir ai-agent-demo && cd ai-agent-demo
bea --no-input init books --currency USD --date 2026-08-01 --opening-balance "Assets:Checking 1000"
bea --file books/main.bean add open --date 2026-08-01 --account Expenses:Transport:Fuel -c USD

Save this sample as statement.csv:

Date,Payee,Narration,Amount
2026-08-02,Whole Foods,groceries,-20.00
2026-08-03,Shell,gas,-40.00
2026-08-04,Unknown Shop,mystery,-9.99

Save these categorization rules as rules.toml:

[[rule]]
match = "whole foods|trader joe|corner market"
account = "Expenses:Groceries"
 
[[rule]]
match = "shell|chevron|exxon"
account = "Expenses:Transport:Fuel"

Copy the task prompt

Paste this prompt to your external agent along with the CSV contents:

Propose Beancount categorization rules for my bank CSV rows.
Suggest one TOML rule per merchant with a match pattern and a
destination expense account. Never change dates, payees, or amounts.
Leave rows you cannot categorize confidently unmatched: they post to
Expenses:Uncategorized with flag ! for my review. I run bea import
preview and apply locally. You do not write my ledger.

Run the CLI equivalents

Preview first, then apply the reviewed entries and verify the result:

bea --file books/main.bean import statement.csv --csv date=Date,amount=Amount,payee=Payee,narration=Narration --account Assets:Checking --rules rules.toml
bea --file books/main.bean import statement.csv --apply
bea --file books/main.bean check
bea --file books/main.bean list transaction --flag '!'
bea --file books/main.bean query "SELECT account, sum(position) WHERE account = 'Assets:Checking' GROUP BY account"

Check the expected output

The preview reports 3 ready, 0 exact duplicates, 0 possible duplicates and exits 0. It writes nothing. The apply run reports Wrote 3 entries. The check reports no errors. The ! queue lists the one unmatched row from Unknown Shop at -9.99 USD. The balance query reports 930.01 USD in checking. Repeating the apply reports 0 ready, 3 exact duplicates and writes 0 entries.

Review and correct

Recategorize the ! row deliberately in your ledger after reading it. A passing check only proves the ledger balances. It says nothing about whether Expenses:Uncategorized is the right account. For duplicate policy, conflict handling, and the advanced Python path, follow the bank CSV walkthrough.

Recipe 2: answer a balance question without writes

Answer one question from the demo books: how much went to groceries and fuel in August 2026. The agent drafts the query. Read-only commands produce the answer.

Prerequisites

You need the demo ledger from recipe 1 or your own books. Every command below is read-only.

Copy the task prompt

Paste this prompt to your external agent:

Draft a Beancount query (BQL) that answers: how much did I spend on
groceries and fuel in August 2026. Use my account names exactly as
listed by bea list open. Propose read-only commands only: bea query
or bea report with an explicit --file target. Do not propose ledger
edits to fix a surprising total. A surprising total means the query
or the books need review, not a compensating entry.

Run the CLI equivalents

Run the drafted query against an explicit ledger target, then cross-check with a report:

bea --file books/main.bean query "SELECT account, sum(position) WHERE account ~ 'Expenses' GROUP BY account"
bea --file books/main.bean report income-statement --time 2026-08

Machine consumers add the global --json flag before the subcommand:

bea --file books/main.bean --json query "SELECT account, sum(position) WHERE account ~ 'Expenses' GROUP BY account"

JSON envelopes, exit codes, and global flag placement follow the CLI reference. The query language guide documents the full BQL surface.

Check the expected output

The query reports 20.00 USD for Expenses:Groceries, 40.00 USD for Expenses:Transport:Fuel, and 9.99 USD for Expenses:Uncategorized. Groceries plus fuel is 60.00 USD. The income statement agrees: 69.99 USD of August expenses with a net profit of -69.99 USD. The JSON variant returns the same rows inside data.rows with column metadata in data.columns.

Review and correct

A query against a wrong account name prints (no rows) on stderr and still exits 0. Compare the name with bea list open output and run the query again. Never correct a surprising total by editing the ledger to match the answer. Fix the query or fix the books, then re-run bea check.

When a recipe becomes a repeat job

The prompts above suit one-off tasks driven by hand. Jobs you repeat weekly suit the versioned skills in the product repository. Each link below opens the skill's current source, which carries its own setup and trigger instructions. This CMS keeps no copy of these files.

  • Start a fresh ledger from an empty directory with beancount-init. It creates the ledger file, a Fava runner, and a Python project.
  • Turn a bank export into categorized entries with beancount-import. It stages every row, suggests categories from ledger history, skips rows already imported, and writes only after confirmation. Recipe 1 above is the manual form of this job.
  • Prove one account matches its statement with beancount-reconcile. It diffs the ledger against the statement and lands a period-end balance assertion after confirmation.
  • Answer a question from the books with beancount-ask. It runs read-only queries and shows each query beside its figure. Recipe 2 above is the manual form of this job.
  • Close out a month with beancount-close. It reconciles every active account, checks recurring entries and review flags, and records the close as a commit.

The full collection lives in the canonical skills tree.

Give a shell-capable agent bea, or load a skill into a client

These are two separate steps. Installing bea never installs or configures skills.

A shell-capable agent needs the command on its path. Install it with the CLI quick start, then the agent runs the same bea commands the recipes show. The agent proposes, you review, and bea validates.

A skill loads into the agent client itself, through that client's own skill mechanism. The product repository exposes one canonical tree to both supported clients: Claude Code reads it under .claude/skills and Codex under .agents/skills, as linked directories into the same source. Point the client's skill location at the skill folder you need, and keep reading its current source for trigger phrases and updates.

Ask-like questions without bea ask

Recipe 2 answers ledger questions with an external agent plus read-only bea query and bea report commands. That path is local. It needs no account and no extra install.

bea ask is a separate built-in helper. It is neither the recipe above nor the beancount-ask skill. It routes the question through the hosted AI service, so it needs the ask extra and hosted credentials, as documented in the CLI reference. Install the base CLI first, then add the extra only when you want hosted answers.

Quoted outputs and CLI verification

Every command above was executed against the published bea 0.1.0 package in a fresh directory. Quoted counts, balances, and messages are CLI verification. The task prompts were not run against any model. Model behavior is evaluated separately.

Source: https://beancount.io/docs/Solutions/accounting-with-ai-agents