Beautiful invoices from the CLI — international, stateful, agent-friendly.
A stateful, single-binary CLI for generating, tracking, and rendering invoices. Built for humans who want a clean terminal workflow and for AI agents that need a deterministic JSON interface to bill clients on their owner's behalf.
Most invoice tools fall into two camps:
- Full accounting suites (Xero, Wave, QuickBooks) — powerful but overkill if you just need to send a beautifully-branded invoice.
- Static generators (typst templates, LaTeX invoices) — gorgeous output but no state: you re-type client details every time, numbering drifts, and an agent can't "render last month's invoice".
invoice-cli sits between them: a stateful SQLite store of issuers, clients,
products and invoices, a small set of beautiful Typst templates, and a JSON
interface built for agents.
- Multi-issuer first-class. Run several companies (SG Pte. Ltd., UK Ltd.,
US LLC, …) from one binary. Each issuer has its own jurisdiction, tax
profile, default template, numbering series, and logo. New issuers default
to
{issuer}-{year}-{seq:04}numbering so invoice IDs stay globally addressable for agents even when several companies share one database. - Per-client defaults. Pin a default issuer and/or template per client —
then just
invoice invoices new --client meridian --item designand the right entity + branding lights up automatically. - International tax profiles. Built-in: Singapore GST 9%, UK VAT 20%,
US (state-variable), EU VAT, plus a
customprofile. Reverse-charge flag for EU cross-border B2B — rendered on the PDF with the legally-required callout. - Precise money math. Amounts stored as
i64minor units; tax math usesrust_decimal— no float rounding artefacts. Discount math clamped at zero so a mis-sized fixed discount can't flip totals negative. - Discounts at line or invoice level (rate or fixed amount).
- Five polished Typst templates out of the box:
vienna,helvetica-nera,tiefletter-gold,monoline,boutique. Self-contained — single binary, templates are embedded and extracted on first use. - Logos per issuer. Attach a PNG/SVG/JPG and each template renders it in the header at the appropriate size for its design language.
- Credit notes. Issue against any existing invoice with
credit-note --full(full reversal) or--item ...(specific refund lines). IndependentCN-{issuer}-YYYY-NNNNnumbering series so credit notes don't collide with invoices. - Draft-only editing. Amend a draft's metadata with
invoices editor its line items withinvoices items add|remove|edit. Once issued, invoices are immutable — the correct path for corrections is a credit note, which preserves the audit trail and number-sequence integrity required by SG/EU/UK regulations. - QR pay-links. Set
--pay-link https://buy.stripe.com/...on an invoice and the renderer stamps a scan-to-pay QR on the PDF. - Lifecycle timestamps.
mark issued/mark paidauto-stampissued_at/paid_at(idempotent, first-transition-only). - Aging + CSV export.
invoices agingbuckets unpaid invoices into 0-30/31-60/61-90/90+ days overdue.invoices export --from X --to Y --format csvgives you a clean accountant handoff. - Agent-friendly. Every command emits a JSON envelope when piped or
--json;invoice agent-inforeturns a capability manifest; structured error codes with suggestions; exit codes distinguish transient vs permanent failures. Install the embedded Claude/Codex/Gemini skill withinvoice skill install.
brew tap 199-biotechnologies/tap
brew install invoice
cargo install invoice-cli
git clone https://github.com/paperfoot/invoice-cli
cd invoice-cli
cargo install --path .
All install paths produce a single invoice binary. Typst is the only runtime
dependency (brew install typst on macOS).
# 1. Register your billing entity (with logo + bank details)
invoice issuer add acme \
--name "Acme Studio" --jurisdiction sg --tax-registered \
--tax-id "M2-1234567-8" --address "1 Marina Bay\nSingapore 018989" \
--template boutique --logo ~/Pictures/acme.png \
--bank-line "Bank: DBS" --bank-line "IBAN: SG11DBSS..." \
--bank-line "SWIFT: DBSSSGSG"
# 2. Add a client, pinning acme as their default issuer
invoice clients add meridian \
--name "Meridian & Co." --country US \
--address "530 5th Ave\nNew York, NY 10036" \
--default-issuer acme --default-template boutique
# 3. Register a reusable line item
invoice products add design \
--description "Creative direction" --unit project \
--price 8400 --currency SGD --tax-rate 9
# 4. Create an invoice — no --as needed, uses client's default issuer
# (or config.default_issuer if the client has no pinned issuer)
invoice invoices new --client meridian --item design --due 30d
# 5. Render + open
invoice invoices render acme-2026-0001 --open
# 6. Later: mark paid, clone for next month
invoice invoices mark acme-2026-0001 paid
invoice invoices duplicate acme-2026-0001
# 7. Need a refund? Credit note against the original. Positive refund
# specs are stored as credits automatically:
invoice invoices credit-note acme-2026-0001 --item "Refund:1:500" --notes "Goodwill credit"
# 8. Month-end accountant handoff
invoice invoices export --from 2026-01-01 --to 2026-03-31 --format csv --out q1.csv
| Command | Purpose |
|---|---|
issuer add|edit|list|show|delete |
Manage billing entities (your companies) |
issuer set-template <slug> <tmpl> |
Shorthand to change an issuer's default template |
clients add|edit|list|show|delete |
Manage clients (who you bill) |
clients set-issuer <slug> <issuer> |
Pin default issuer for a client |
clients set-template <slug> <tmpl> |
Pin default template for a client |
products add|edit|list|show|delete |
Manage reusable line items |
invoices new --client X --item Y |
Create a new invoice |
invoices edit <number> |
Edit draft metadata (due, terms, notes, discount…) |
invoices items add|remove|edit <number> |
Mutate line items on a draft invoice |
invoices duplicate <number> |
Clone an invoice as a fresh draft (recurring billing) |
invoices credit-note <number> |
Issue a credit note against an existing invoice |
invoices render <number> [--template T] [--open] |
Generate PDF |
invoices mark <number> draft|issued|paid|void |
Update status (auto-stamps timestamps) |
invoices list [--status X] [--as Y] [--overdue] |
List invoices with totals |
invoices aging [--as Y] |
Aging buckets for unpaid invoices |
invoices export --from X --to Y --format csv|json |
Accountant handoff |
invoices delete <number> [--force] |
Delete an invoice (--force for non-draft) |
template list|preview <name> |
Inspect available templates |
doctor |
Diagnose typst install, DB, templates, default issuer, numbering |
agent-info |
Full JSON capability manifest |
skill install |
Install embedded Claude/Codex/Gemini skill |
update [--check] |
Self-update via brew or cargo |
Run invoice --help for the full reference or invoice <subcommand> --help
for any subcommand.
At render time, the template chain is:
--template flag > client.default_template > issuer.default_template > "vienna"
So pinning a template on a client gives them consistent branding without you
having to pass --template every time.
On invoices new, each --item is one of:
product-slug— uses the product's price, unit, and tax rateproduct-slug:qty— e.g.design:2for two units"Description:qty:price"— ad-hoc item with default tax rate from jurisdiction"Description:qty:price:rate"— ad-hoc item with explicit tax rate
- Config: shared Paperfoot accounting config. Run
invoice config pathfor the exact OS path; on macOS it lives under~/Library/Application Support/com.paperfoot.accounting/. - Database: shared Paperfoot accounting SQLite database
(
accounting.dbin the shared state dir;invoice agent-inforeports the exact path). - Templates: extracted once to the state dir, refreshed on upgrade
Nothing ever leaves your machine unless you choose to — no telemetry, no phone-home, no cloud sync.
This CLI is designed to be driven by AI agents as well as humans. The contract:
- Every command emits a
{version, status, data|error}envelope when piped. invoice agent-inforeturns a full capability + exit-code manifest.invoice doctor --jsonreports setup, default issuer validity, and risky multi-company numbering formats.invoice skill installdrops a ready-to-use skill file into~/.claude/skills/invoice-cli/SKILL.md(and the Codex/Gemini equivalents).
Typical agent workflow:
USER: "Bill Meridian for last month's design work"
AGENT: invoice invoices duplicate $(invoice invoices list --json | jq -r '.data[0].number')
Agents should use invoice numbers returned by JSON responses, not predict the
next sequence. The CLI keeps numbers globally unique; if legacy issuers share a
plain {year}-{seq:04} format, generation auto-prefixes the issuer slug on
collision and doctor warns so you can clean up the formats.
- Rust binary via
cargo/ single-binary distribution. - SQLite via
rusqlitewithrefinerymigrations (migrations/V*.sql). - Typst for PDF rendering — templates live in
typst/, embedded viarust-embedand extracted to the user's state dir. - Money as
i64minor units; tax withrust_decimal. No floats in financial paths. - Built on
agent-cli-framework(ACF) conventions for agent ergonomics.
This is an invoicing tool, not an accounting suite. In scope:
- Clean, branded invoice generation across multiple entities
- Multi-currency, multi-jurisdiction tax handling
- Lifecycle tracking (draft → issued → paid → void)
- Recurring billing via
duplicate
Explicitly out of scope:
- Double-entry bookkeeping
- Bills payable / accounts payable
- Bank reconciliation
- GST/VAT filing reports
- Chart of accounts, P&L, balance sheet
- Payroll, inventory, expense tracking
For those, reach for Xero, Wave, or QuickBooks.
MIT © 199 Biotechnologies