Skip to main content

Crafting & Tracking Invoices with Beancount

· 6 min read
Mike Thrift
Mike Thrift
Marketing Manager

A plain‑text template, a repeatable workflow, and a single query for “Who still owes me?”


2022-02-12-crafting-tracking-invoices-with-beancount

Invoicing can feel like a chore, stuck between the work you've finished and the payment you're waiting for. But a good invoice process is the backbone of healthy cash flow. It pulls double duty: it clearly tells your clients what they owe and when, and it feeds your accounting system the unassailable facts it needs.

While dedicated SaaS apps can send slick, automated PDFs, they often come with monthly fees and lock your data in a proprietary silo. A lightweight, plain-text approach using Beancount offers a powerful alternative. You can turn each invoice into a clear set of accounting entries, giving you all the benefits of version control, powerful metadata, and instant querying—no subscription required.


The Minimum Viable Invoice (Fields You Should Never Skip)

Before you touch your ledger, you need a professional invoice. The format can be simple, but the contents must be precise. These fields, borrowed from time-tested small-business practice, are non-negotiable.

  • Seller details: Your business name and physical address.
  • Client details: Your client’s name and (ideally) their address.
  • Invoice number: A unique, sequential ID that is never reused. INV-045 follows INV-044.
  • Issue & due dates: Clearly state when the invoice was issued and when payment is expected.
  • Line items: A clear description of services or products, along with quantity, rate, and the line total.
  • Subtotal, tax, and total: Show the math so the client can follow it easily.
  • Optional notes: A place for a thank you, wiring instructions, or a client-provided purchase order number.

To get you started, we've created a set of ready-to-edit templates that include all these fields. The spreadsheet versions even pre-calculate the totals for you.

Grab our templates here: beancount.io/invoice‑templates (Available in Google Docs, Word, Sheets, Excel, and PDF formats)


Record the Invoice in Your Ledger

Once you've sent the invoice PDF to your client, you must record it in Beancount. This is a crucial step that recognizes the revenue when it's earned, not just when it's paid. The process involves two distinct transactions.

1. When you issue the invoice:

You create a transaction that moves the total amount from your Income account into Assets:AccountsReceivable. This creates a digital IOU in your books.

; 2025‑07‑21 Invoice #045  Web design sprint for Acme Corp.
2025-07-21 * "Acme Corp" "INV-045 Web design sprint"
Assets:AccountsReceivable 3500.00 USD ; due:2025-08-04
Income:Design:Web
invoice_id: "INV-045"
contact_email: "ap@acme.example"
link: "docs/invoices/2025-07-21_Acme_INV-045.pdf"

Here, you debit AccountsReceivable and credit your Income account. Notice the rich metadata: the due date, a unique invoice_id, and even a direct link: to the PDF you sent.

2. When the client pays:

When the cash hits your bank account, you record a second transaction to "close out" the IOU. This moves the balance from AccountsReceivable to your checking account.

2025-08-01 * "Acme Corp" "Payment INV-045"
Assets:Bank:Checking 3500.00 USD
Assets:AccountsReceivable
invoice_id: "INV-045"

The balance for INV-045 in Assets:AccountsReceivable is now zero, and your books are perfectly balanced.

Attach the PDF: The link: metadata key is especially powerful when used with Fava, Beancount's web interface. Fava will render a clickable link directly in the transaction view, so the source document is never more than a click away. This workflow was envisioned as early as 2016 in a feature request (GitHub).


One Query to List All Open Invoices

So, who still owes you money? With this system, you don't need to hunt through emails or spreadsheets. You just need one simple query.

Save the following as a file named open-invoices.sql:

SELECT
meta('invoice_id') AS id,
payee,
narration,
date,
number(balance) AS outstanding
WHERE
account = "Assets:AccountsReceivable"
AND balance != 0
ORDER BY
date;

Now, run it from your command line:

bean-query books.beancount open-invoices.sql

In seconds, you'll get a clean, up-to-the-minute aging report of all outstanding invoices, showing the invoice ID, client, date issued, and amount owed. No extra software required.


Automate the Busywork

The beauty of plain text is scriptability. You can automate the tedious parts of this workflow.

  • Template + Pandoc = PDF: Maintain your invoice template in Markdown. A tiny Python script can populate the variables (client name, line items, invoice number), and the command-line tool Pandoc can instantly convert it to a professional PDF.
  • Git Pre-commit Hook: If you store your ledger in Git, a simple pre-commit hook can run checks before you save your work. It can verify that every new invoice_id is unique, that the transaction postings balance to zero, and that the file referenced in the link: metadata actually exists.
  • Cron Job: Set up a scheduled task (a cron job) to run your open-invoices.sql query every night and email you the summary. You'll start each day knowing exactly who needs a friendly nudge.

A Realistic Caveat

Beancount is an accounting tool, not an invoicing service. It will not automatically send payment reminders to your clients or process their credit card payments. The workflow is: you create and send the invoice using your chosen method (like the templates above), and then you book the accounting entries in your ledger.

For most freelancers and small shops, this manual step is a small price to pay for a bulletproof, auditable, and free accounting system that you completely own and control (beancount.io).


Next Steps

Ready to take control of your invoicing? Here’s how to start.

  1. Download a template and use it to create your next real invoice, making sure to use a sequential invoice number.
  2. Store your sent PDFs in a dedicated folder like docs/invoices/ and use the link: metadata key in your Beancount transaction to reference them.
  3. Save the open-invoices.sql query and make running it a part of your weekly financial review.

Plain-text accounting doesn’t mean giving up polish or control—it just means the database is grep-able. With a simple template and the snippets above, you’ll get paid faster and keep your books immaculate.