qb-cli — QuickBooks Online CLI
Manage QuickBooks Online from the command line. Designed for both human and AI agent use. Talks directly to Intuit's QuickBooks API — no third-party proxy. 164 commands across 29 groups covering AR, AP, chart of accounts, banking, reporting, imports, reconciliation, and month-end workflows.
Setup
The tool runs in a Docker container at ~/skills/qb-cli/.
Prerequisites
- Create a QuickBooks developer account at https://developer.intuit.com
- Create an app (Keys & OAuth section)
- Note your Client ID and Client Secret
- Add
http://localhost:8844/callbackas a Redirect URI
Configuration
cp ~/skills/qb-cli/.env.example ~/skills/qb-cli/.env
# Edit .env with your Client ID and Client Secret
Build (first time)
docker compose -f ~/skills/qb-cli/docker-compose.yml build
Authenticate (SSH / headless)
# Step 1: Get the auth URL
~/skills/qb-cli/run.sh auth login --print-url
# Step 2: Open the URL in any browser, authorize QuickBooks
# Step 3: Copy the full redirect URL from browser address bar
# Step 4: Paste it back
~/skills/qb-cli/run.sh auth login --callback-url "http://localhost:8844/callback?code=...&realmId=..."
CRITICAL AGENT RULES — READ BEFORE DOING ANYTHING
Rule 1: ALWAYS Search Before Creating
Never create a customer, vendor, or item without first searching for duplicates.
~/skills/qb-cli/run.sh customer search "Meridian"
~/skills/qb-cli/run.sh vendor search "Acme"
If the search returns results, confirm with the user which one they mean. Do NOT assume.
Rule 2: NEVER Create a Customer Without Complete Information
You MUST have ALL of the following before creating a customer record:
- Display name — Full legal name or business name
- Company name — The legal business entity name (if applicable)
- Billing email address — Required for sending invoices electronically
- Phone number — Primary contact phone
- Billing address — Full street address, city, state, and zip code
If the user says something vague like "send an invoice to Mike" or "bill Acme $500":
- STOP. Do not create anything.
- Search for the customer:
customer search "Mike"orcustomer search "Acme" - If no results, ask the user for: full name, company name, billing email, phone, and billing address.
- Only proceed once you have all fields.
Rule 3: Confirm Before Sending Invoices/POs
- Never send an invoice or PO by email unless the user explicitly says to send it.
- Creating and sending are separate actions.
- Always confirm the amount, customer/vendor, and line items before creating.
Rule 4: Verify Payments Before Applying
- Always confirm the customer, open invoices, and amounts before creating a payment.
- Include the payment reference (check #, ACH trace, wire ref) when available.
- Verify the payment total equals the sum of the per-invoice amounts.
Rule 5: Never Delete a Bill with Applied Payments
- Before deleting a bill, check if bill-payments have been applied to it.
- Delete or void the bill-payment first, then delete the bill.
Rule 6: Journal Entries Must Balance
- Debits must equal credits. The CLI validates this before sending.
- Always specify a memo/description for audit trail.
Rule 7: Never Void Deposited Payments
- If a payment has been grouped into a deposit, void/delete the deposit first.
- Then void/delete the individual payment.
Rule 8: Reconciliation Is Read-Only
- The reconcile commands are helpers only — they cannot mark transactions as reconciled in QB.
- Use them to identify matches, flag discrepancies, and generate reports.
- The actual reconciliation click must happen in the QuickBooks UI.
Usage
All commands: ~/skills/qb-cli/run.sh [resource] [action] [options]
Default output is JSON. Use -o table for human-readable or -o csv for export.
Auth & Config
~/skills/qb-cli/run.sh auth login --print-url # Get OAuth URL
~/skills/qb-cli/run.sh auth login --callback-url "..." # Complete OAuth
~/skills/qb-cli/run.sh auth status # Check auth status
~/skills/qb-cli/run.sh auth refresh # Force token refresh
~/skills/qb-cli/run.sh auth logout # Remove tokens
~/skills/qb-cli/run.sh config init # Initialize config
~/skills/qb-cli/run.sh config show # Show config
~/skills/qb-cli/run.sh company info # Company details
Customers (AR)
# Search (ALWAYS do this first!)
~/skills/qb-cli/run.sh customer search "Acme"
~/skills/qb-cli/run.sh customer search "jane@example.com"
~/skills/qb-cli/run.sh customer search "555-0199"
# List
~/skills/qb-cli/run.sh customer list
~/skills/qb-cli/run.sh customer list -o table
~/skills/qb-cli/run.sh customer list --no-active-only
# CRUD
~/skills/qb-cli/run.sh customer get 123
~/skills/qb-cli/run.sh customer create --name "Jane Smith" --company "Smith LLC" --email "jane@smith.com" --phone "555-0199"
~/skills/qb-cli/run.sh customer update 123 --name "Jane Smith-Jones" --email "new@email.com"
~/skills/qb-cli/run.sh customer delete 123 # soft-delete (Active=false)
# Query
~/skills/qb-cli/run.sh customer query "Balance > '0'"
Invoices
~/skills/qb-cli/run.sh invoice list
~/skills/qb-cli/run.sh invoice get 456
~/skills/qb-cli/run.sh invoice create --customer-id 123 --amount 500 --due-date 2026-04-01
~/skills/qb-cli/run.sh invoice create --customer-id 123 --line-json '[{"Amount":500,"DetailType":"SalesItemLineDetail","SalesItemLineDetail":{"ItemRef":{"value":"1"},"Qty":2,"UnitPrice":250}}]'
~/skills/qb-cli/run.sh invoice update 456 --json '{"DueDate": "2026-05-01"}'
~/skills/qb-cli/run.sh invoice send 456 # email to customer
~/skills/qb-cli/run.sh invoice send 456 --email "override@email.com"
~/skills/qb-cli/run.sh invoice void 456 # zeros amounts, keeps record
~/skills/qb-cli/run.sh invoice delete 456
~/skills/qb-cli/run.sh invoice query "CustomerRef = '123' AND Balance > '0'"
Payments
~/skills/qb-cli/run.sh payment list
~/skills/qb-cli/run.sh payment get 182
~/skills/qb-cli/run.sh payment create --customer-id 63 --amount 5000 --invoice-ids "148,149" --invoice-amounts "3000,2000" --ref "ACH-12345" --date "2026-02-15"
~/skills/qb-cli/run.sh payment void 182
~/skills/qb-cli/run.sh payment delete 182
~/skills/qb-cli/run.sh payment query "TxnDate > '2026-01-01'"
Estimates (Quotes/Proposals)
~/skills/qb-cli/run.sh estimate list
~/skills/qb-cli/run.sh estimate get 789
~/skills/qb-cli/run.sh estimate create --customer-id 123 --amount 1500 --expiration-date 2026-04-30
~/skills/qb-cli/run.sh estimate update 789 --json '{"ExpirationDate": "2026-05-31"}'
~/skills/qb-cli/run.sh estimate send 789
~/skills/qb-cli/run.sh estimate to-invoice 789 # convert estimate to invoice
~/skills/qb-cli/run.sh estimate delete 789
~/skills/qb-cli/run.sh estimate query "TxnStatus = 'Pending'"
Credit Memos
~/skills/qb-cli/run.sh credit-memo list
~/skills/qb-cli/run.sh credit-memo create --customer-id 123 --amount 200
~/skills/qb-cli/run.sh credit-memo send 456
~/skills/qb-cli/run.sh credit-memo void 456
~/skills/qb-cli/run.sh credit-memo delete 456
~/skills/qb-cli/run.sh credit-memo query "TotalAmt > '100'"
Sales Receipts (Cash Sales)
~/skills/qb-cli/run.sh sales-receipt list
~/skills/qb-cli/run.sh sales-receipt create --customer-id 123 --amount 750 --deposit-to 35 --payment-method "Cash"
~/skills/qb-cli/run.sh sales-receipt send 456
~/skills/qb-cli/run.sh sales-receipt void 456
~/skills/qb-cli/run.sh sales-receipt delete 456
Refund Receipts
~/skills/qb-cli/run.sh refund-receipt list
~/skills/qb-cli/run.sh refund-receipt create --customer-id 123 --amount 200 --deposit-from 35
~/skills/qb-cli/run.sh refund-receipt void 456
~/skills/qb-cli/run.sh refund-receipt delete 456
Vendors (AP)
# Search (ALWAYS do this first!)
~/skills/qb-cli/run.sh vendor search "Office Depot"
# List
~/skills/qb-cli/run.sh vendor list
~/skills/qb-cli/run.sh vendor list --no-active-only
# CRUD
~/skills/qb-cli/run.sh vendor get 42
~/skills/qb-cli/run.sh vendor create --name "Office Depot" --company "Office Depot Inc" --email "ap@officedepot.com" --phone "800-555-1234" --1099
~/skills/qb-cli/run.sh vendor create --name "John the Plumber" --1099 --tax-id "123-45-6789"
~/skills/qb-cli/run.sh vendor update 42 --email "new@email.com" --1099
~/skills/qb-cli/run.sh vendor delete 42 # soft-delete
~/skills/qb-cli/run.sh vendor query "Balance > '0'"
Bills
~/skills/qb-cli/run.sh bill list
~/skills/qb-cli/run.sh bill get 100
~/skills/qb-cli/run.sh bill create --vendor-id 42 --amount 1500 --due-date 2026-03-15
~/skills/qb-cli/run.sh bill create --vendor-id 42 --amount 800 --account-id 80 --memo "Office supplies Jan"
~/skills/qb-cli/run.sh bill update 100 --json '{"DueDate": "2026-04-01"}'
~/skills/qb-cli/run.sh bill delete 100
~/skills/qb-cli/run.sh bill query "VendorRef = '42' AND Balance > '0'"
Bill Payments
~/skills/qb-cli/run.sh bill-payment list
~/skills/qb-cli/run.sh bill-payment get 200
~/skills/qb-cli/run.sh bill-payment create --vendor-id 42 --amount 1500 --pay-type Check --account-id 35 --bill-ids "100,101" --bill-amounts "1000,500" --ref "1042"
~/skills/qb-cli/run.sh bill-payment create --vendor-id 42 --amount 800 --pay-type CreditCard --account-id 41 --bill-ids "102"
~/skills/qb-cli/run.sh bill-payment void 200
~/skills/qb-cli/run.sh bill-payment delete 200
~/skills/qb-cli/run.sh bill-payment query "TxnDate >= '2026-01-01'"
Vendor Credits
~/skills/qb-cli/run.sh vendor-credit list
~/skills/qb-cli/run.sh vendor-credit create --vendor-id 42 --amount 150 --account-id 80
~/skills/qb-cli/run.sh vendor-credit delete 300
~/skills/qb-cli/run.sh vendor-credit query "VendorRef = '42'"
Purchase Orders
~/skills/qb-cli/run.sh purchase-order list
~/skills/qb-cli/run.sh purchase-order create --vendor-id 42 --amount 5000 --item-id 10 --memo "Q2 inventory"
~/skills/qb-cli/run.sh purchase-order send 500
~/skills/qb-cli/run.sh purchase-order to-bill 500 # convert PO to bill
~/skills/qb-cli/run.sh purchase-order update 500 --json '{"POStatus": "Closed"}'
~/skills/qb-cli/run.sh purchase-order delete 500
Chart of Accounts
~/skills/qb-cli/run.sh account list
~/skills/qb-cli/run.sh account list --type Bank # filter by type
~/skills/qb-cli/run.sh account list --type Expense
~/skills/qb-cli/run.sh account get 35
~/skills/qb-cli/run.sh account create --name "Marketing" --type Expense --sub-type AdvertisingPromotional
~/skills/qb-cli/run.sh account create --name "Business Checking" --type Bank --sub-type Checking --acct-num "1010"
~/skills/qb-cli/run.sh account update 35 --name "Updated Name" --description "New description"
~/skills/qb-cli/run.sh account delete 35 # soft-delete (Active=false)
~/skills/qb-cli/run.sh account query "AccountType = 'Bank'"
Items (Products & Services)
~/skills/qb-cli/run.sh item list
~/skills/qb-cli/run.sh item list --type Service
~/skills/qb-cli/run.sh item list --type Inventory
~/skills/qb-cli/run.sh item get 10
~/skills/qb-cli/run.sh item create --name "Consulting" --type Service --income-account 1 --price 150 --description "Hourly consulting"
~/skills/qb-cli/run.sh item create --name "Widget" --type Inventory --income-account 1 --expense-account 80 --asset-account 81 --price 29.99 --cost 12.50 --qty 100 --inv-start-date 2026-01-01
~/skills/qb-cli/run.sh item update 10 --price 175 --name "Senior Consulting"
~/skills/qb-cli/run.sh item delete 10 # soft-delete
~/skills/qb-cli/run.sh item query "Type = 'Service'"
Expenses (Purchases / Checks / CC Charges)
~/skills/qb-cli/run.sh expense list
~/skills/qb-cli/run.sh expense get 400
~/skills/qb-cli/run.sh expense create --account-id 35 --pay-type Check --vendor-id 42 --amount 250 --doc-number "1042" --memo "Office supplies"
~/skills/qb-cli/run.sh expense create --account-id 41 --pay-type CreditCard --amount 99.99 --memo "Software subscription"
~/skills/qb-cli/run.sh expense update 400 --json '{"PrivateNote": "Updated memo"}'
~/skills/qb-cli/run.sh expense delete 400
~/skills/qb-cli/run.sh expense query "TxnDate >= '2026-01-01' AND TxnDate <= '2026-01-31'"
Journal Entries
~/skills/qb-cli/run.sh journal list
~/skills/qb-cli/run.sh journal get 500
~/skills/qb-cli/run.sh journal create --lines '[{"account_id":"80","amount":500,"type":"Debit","description":"Depreciation exp"},{"account_id":"35","amount":500,"type":"Credit","description":"Accum depreciation"}]' --date 2026-01-31 --memo "Jan depreciation"
~/skills/qb-cli/run.sh journal delete 500
~/skills/qb-cli/run.sh journal query "TxnDate = '2026-01-31'"
Deposits
~/skills/qb-cli/run.sh deposit list
~/skills/qb-cli/run.sh deposit get 600
~/skills/qb-cli/run.sh deposit create --account-id 35 --payment-ids "182,183,184" --date 2026-02-15 --memo "Week of 2/15 deposits"
~/skills/qb-cli/run.sh deposit delete 600
~/skills/qb-cli/run.sh deposit query "TxnDate >= '2026-02-01'"
Transfers
~/skills/qb-cli/run.sh transfer list
~/skills/qb-cli/run.sh transfer get 700
~/skills/qb-cli/run.sh transfer create --from 35 --to 36 --amount 10000 --date 2026-02-01 --memo "Move to savings"
~/skills/qb-cli/run.sh transfer delete 700
Financial Reports
All reports support -o json (default), -o table, -o csv.
# Income Statement
~/skills/qb-cli/run.sh report profit-and-loss --start-date 2026-01-01 --end-date 2026-01-31
~/skills/qb-cli/run.sh report profit-and-loss --period "Last Month"
~/skills/qb-cli/run.sh report profit-and-loss --start-date 2026-01-01 --end-date 2026-12-31 --summarize-by Month
~/skills/qb-cli/run.sh report profit-and-loss-detail --start-date 2026-01-01 --end-date 2026-01-31
# Balance Sheet
~/skills/qb-cli/run.sh report balance-sheet --date 2026-01-31
~/skills/qb-cli/run.sh report balance-sheet --period "This Fiscal Year"
# Cash Flow
~/skills/qb-cli/run.sh report cash-flow --start-date 2026-01-01 --end-date 2026-01-31
# Trial Balance
~/skills/qb-cli/run.sh report trial-balance --date 2026-01-31
# General Ledger
~/skills/qb-cli/run.sh report general-ledger --start-date 2026-01-01 --end-date 2026-01-31 --account 35
# AR/AP Aging
~/skills/qb-cli/run.sh report ar-aging
~/skills/qb-cli/run.sh report ar-aging-detail --customer 123
~/skills/qb-cli/run.sh report ap-aging
~/skills/qb-cli/run.sh report ap-aging-detail --vendor 42
# Balance Reports
~/skills/qb-cli/run.sh report customer-balance
~/skills/qb-cli/run.sh report vendor-balance
# Income/Expense Analysis
~/skills/qb-cli/run.sh report customer-income --start-date 2026-01-01 --end-date 2026-12-31
~/skills/qb-cli/run.sh report vendor-expenses --start-date 2026-01-01 --end-date 2026-12-31
# Transaction List
~/skills/qb-cli/run.sh report transaction-list --start-date 2026-01-01 --end-date 2026-01-31
# Tax Summary
~/skills/qb-cli/run.sh report tax-summary --start-date 2026-01-01 --end-date 2026-12-31
Bank Statement Import
# Preview a statement file (no changes to QB)
~/skills/qb-cli/run.sh import preview /workspace/statement.ofx
~/skills/qb-cli/run.sh import preview /workspace/statement.csv --date-col "Trans Date" --amount-col "Amount" --desc-col "Description"
# Import and match (dry run first!)
~/skills/qb-cli/run.sh import bank /workspace/statement.ofx --account-id 35 --dry-run
~/skills/qb-cli/run.sh import bank /workspace/statement.ofx --account-id 35
# CSV import with custom columns
~/skills/qb-cli/run.sh import bank /workspace/statement.csv --account-id 35 --format csv --date-col "Date" --amount-col "Amount" --desc-col "Payee" --dry-run
Supported formats: OFX, QFX, QBO, CSV (with configurable column mapping).
Matching algorithm:
- Exact match — same amount AND same date AND (matching FITID or check number) → skipped
- Probable match — same amount AND date within ±3 days → flagged for review
- No match → created as Purchase (debit) or Deposit (credit)
Bank Reconciliation
# Start reconciliation session
~/skills/qb-cli/run.sh reconcile start --account-id 35 --statement-date 2026-01-31 --statement-balance 45000.00
# Check status
~/skills/qb-cli/run.sh reconcile status --account-id 35
# Match statement against QB
~/skills/qb-cli/run.sh reconcile match --account-id 35 --statement-file /workspace/jan-statement.ofx
# Generate reconciliation report
~/skills/qb-cli/run.sh reconcile report --account-id 35 --start-date 2026-01-01 --end-date 2026-01-31
Note: These are helper commands. QB has no reconciliation API — you cannot programmatically mark transactions as reconciled. Use these to identify matches and discrepancies, then complete the reconciliation in the QB UI.
Bookkeeping Workflows
# Month-end close checklist
~/skills/qb-cli/run.sh workflow month-close --month 2026-01 --check-only # just run checks
~/skills/qb-cli/run.sh workflow month-close --month 2026-01 # checks + generate reports
# 1099 preparation
~/skills/qb-cli/run.sh workflow 1099-prep --year 2025
~/skills/qb-cli/run.sh workflow 1099-prep --year 2025 --threshold 600
# AR follow-up (overdue invoices grouped by customer)
~/skills/qb-cli/run.sh workflow ar-followup --days-overdue 30
~/skills/qb-cli/run.sh workflow ar-followup --days-overdue 60
# Undeposited funds check
~/skills/qb-cli/run.sh workflow undeposited-funds
Month-end close runs these checks:
- Undeposited Funds balance (should be ~$0)
- Overdue AR (invoices past due before month start)
- Overdue AP (bills past due before month start)
- Open invoices for the month
Then generates P&L, Balance Sheet, and Trial Balance to /workspace/.
Batch Operations
# Run batch operations from a JSON file (max 30 per batch, auto-chunks)
~/skills/qb-cli/run.sh batch run --file /workspace/batch_ops.json
Batch file format:
[
{"operation": "create", "entity": "Customer", "body": {"DisplayName": "New Customer"}},
{"operation": "query", "sql": "SELECT * FROM Invoice WHERE Balance > '0'"},
{"operation": "delete", "entity": "Invoice", "id": "123", "sync_token": "0"}
]
Company Preferences
~/skills/qb-cli/run.sh preferences show
~/skills/qb-cli/run.sh preferences update --json '{"AccountingInfoPrefs": {"BookCloseDate": "2025-12-31"}}'
Tax
~/skills/qb-cli/run.sh tax codes
~/skills/qb-cli/run.sh tax rates
~/skills/qb-cli/run.sh tax summary --start-date 2026-01-01 --end-date 2026-12-31
Attachments
~/skills/qb-cli/run.sh attachment list --entity-type Invoice --entity-id 456
~/skills/qb-cli/run.sh attachment get 800
~/skills/qb-cli/run.sh attachment upload --entity-type Invoice --entity-id 456 --file /workspace/receipt.pdf
~/skills/qb-cli/run.sh attachment note --entity-type Invoice --entity-id 456 --text "Approved by CFO 2/15"
~/skills/qb-cli/run.sh attachment delete 800
Agent Workflow Guides
Processing a Vendor Bill
1. vendor search "Vendor Name" → find or create vendor
2. bill create --vendor-id X --amount $ → record the bill
3. bill-payment create --vendor-id X --amount $ --pay-type Check --account-id Y --bill-ids Z
→ pay the bill when ready
Sending an Invoice
1. customer search "Customer Name" → find existing customer
2. invoice create --customer-id X --amount $ --due-date YYYY-MM-DD
3. (confirm with user before sending)
4. invoice send <id> → only if user explicitly requests
Converting Estimate to Invoice
1. estimate create --customer-id X --amount $ --expiration-date YYYY-MM-DD
2. estimate send <id> → send to customer for review
3. (once accepted)
4. estimate to-invoice <id> → creates linked invoice
Month-End Close
1. workflow month-close --month YYYY-MM --check-only → review issues
2. (fix any warnings: deposit undeposited funds, follow up on AR, etc.)
3. journal create --lines '[...]' --date YYYY-MM-DD → adjusting entries
4. workflow month-close --month YYYY-MM → generate reports
5. report profit-and-loss --start-date ... --end-date ... → final P&L
6. report balance-sheet --date YYYY-MM-DD → final BS
Bank Reconciliation
1. import preview /workspace/statement.ofx → inspect transactions
2. import bank /workspace/statement.ofx --account-id X --dry-run → preview matches
3. import bank /workspace/statement.ofx --account-id X → create unmatched
4. reconcile start --account-id X --statement-date ... --statement-balance $
5. reconcile match --account-id X --statement-file /workspace/statement.ofx
6. (review matches, investigate discrepancies)
7. reconcile report --account-id X --start-date ... --end-date ...
1099 Year-End Prep
1. workflow 1099-prep --year YYYY → identify vendors over threshold
2. (review missing TINs — flag them for user)
3. vendor update <id> --tax-id "XX-XXXXXXX" → add missing TINs
4. (CSV exported to /workspace/1099_prep_YYYY.csv for filing software)
Agent Integration Notes
Exit codes:
0— Success1— API error (bad request, server error)2— Configuration error (missing config)3— Authentication error (expired/invalid tokens)4— Not found5— Validation error (missing required arguments)
Error format:
{"error": true, "code": 3, "message": "Not authenticated", "hint": "qb auth login"}
Key behaviors:
- Token refresh is automatic — no manual refresh needed
- SyncToken is auto-fetched before update/delete operations
- Account entity requires full update (not sparse) — handled automatically
- Soft-delete for name-list entities (customer, vendor, account, item) uses
Active: false - Hard delete for transactions (invoice, bill, payment, etc.) uses
operation=delete - Void for transactions zeros amounts but keeps the record
- QuickBooks uses SQL-like queries:
SELECT * FROM Customer WHERE Balance > '0' - Query operators:
=,LIKE,<,>,<=,>=,IN - Date format:
YYYY-MM-DD - Reports return hierarchical JSON with Header/Columns/Rows
- Workspace directory at
/workspace/(mapped to~/skills/qb-cli/workspace/on host)
Command group count (29 groups, 164 commands): auth, config, company, customer, invoice, payment, estimate, credit-memo, sales-receipt, refund-receipt, vendor, bill, bill-payment, vendor-credit, purchase-order, account, item, expense, journal, deposit, transfer, report, import, reconcile, workflow, batch, preferences, tax, attachment
QB query limitations:
Vendor1099is not queryable via SQL — usevendor listand filter client-sideORis not supported — searches run multiple queries and deduplicate- Phone fields are not indexable via
LIKE— search falls back to client-side filter - String values in queries must be single-quoted:
WHERE Balance > '0'