CLI for QuickBooks Online — CRUD, reports, queries, and batch ops from the terminal.
- Direct API access — thin wrapper over the QBO REST API, no scraping or browser automation
- Agent-friendly —
--jsonon every command, structured exit codes, stderr-only progress - Full CRUD — list, get, create, update, delete any QBO entity plus raw queries
- Financial reports — profit & loss, balance sheet, and more with date range filters
- Single binary — install via Homebrew, Scoop, or
go install
$ qbo list invoices --where "Balance > '0'" --sandbox --json
[
{"Id": "130", "DocNumber": "1038", "CustomerRef": {"name": "Amy's Bird Sanctuary"}, "Balance": 629.10},
{"Id": "131", "DocNumber": "1039", "CustomerRef": {"name": "Bill's Windsurf Shop"}, "Balance": 239.00}
]
$ qbo report profit-and-loss --start-date 2025-01-01 --sandbox
Profit and Loss (2025-01-01 — 2025-12-31)
──────────────────────────────────────────
Income $12,450.00
Cost of Goods Sold $4,200.00
─────────────────────────────────
Gross Profit $8,250.00
Expenses $3,100.00
─────────────────────────────────
Net Income $5,150.00Homebrew (macOS / Linux):
brew install voska/tap/qboScoop (Windows):
scoop bucket add voska https://github.com/voska/scoop-bucket
scoop install qboOther install methods
Go:
go install github.com/voska/qbo-cli/cmd/qbo@latestBinary: download from Releases.
Linux (deb) — also available for arm64:
curl -LO https://github.com/voska/qbo-cli/releases/latest/download/qbo_linux_amd64.deb
sudo dpkg -i qbo_linux_amd64.deb# Store app-level OAuth client credentials in the system keychain (one time).
# Env vars (QBO_CLIENT_ID/QBO_CLIENT_SECRET) still work and override this.
qbo auth set-client --client-id your_client_id --client-secret your_client_secret
# Authenticate (sandbox)
qbo auth login --sandbox
# Verify connection
qbo auth status
qbo company info --sandbox --json
# List invoices
qbo list invoices --sandbox
# Get a specific customer
qbo get customer 123 --sandbox
# Query with filters
qbo list invoices --where "Balance > '0'" --sandbox --json
# Run a report
qbo report profit-and-loss --start-date 2025-01-01 --end-date 2025-12-31 --sandboxInstall as a Claude Code skill for AI-assisted QuickBooks workflows:
npx skills add -g voska/qbo-cliThe skill includes setup guidance, usage patterns, troubleshooting, and a full command reference.
You need an Intuit Developer account to get OAuth credentials.
- Sign up at developer.intuit.com and create an app.
- Select QuickBooks Online and Payments as the platform.
- Under Keys & credentials, grab your Client ID and Client Secret.
- Add a Redirect URI —
http://localhost:8844/callbackfor sandbox, or a public URI for production.
See Intuit's OAuth 2.0 guide for the full walkthrough.
Sandbox vs Production: Development keys only work with sandbox companies. For production, complete Intuit's app assessment and use
--redirect-uri(orQBO_REDIRECT_URI) with a public URI.
Uses the QuickBooks Online Accounting API via REST. Requires OAuth 2.0 credentials from an Intuit Developer app. Sandbox access is free with no rate limits.
| Command | Description |
|---|---|
auth login|logout|status|refresh |
OAuth 2.0 authentication |
list <entity> |
List entities with optional --where filter |
get <entity> <id> |
Read a single entity by ID |
create <entity> --data <json> |
Create an entity from JSON |
update <entity> --data <json> |
Update an entity |
delete <entity> <id> |
Delete an entity by ID |
send invoice <id> [--to <email>] |
Email an invoice (optionally replacing its billing email) |
void <entity> <id> |
Zero a transaction's amounts and preserve its audit trail |
query "<sql>" |
Raw QBO query |
report <type> |
Financial reports (profit-and-loss, balance-sheet, etc.) |
batch --file <path> |
Batch operations from file |
cdc --entities --since |
Change data capture polling |
company info|list|switch |
Company management |
schema |
CLI command tree as JSON (agent introspection) |
exit-codes |
Print exit code reference |
All commands support --dry-run, --no-input, and --sandbox.
qbo send invoice <id> [--to <email>] emails a third party. Without --to,
QuickBooks uses the invoice's stored BillEmail. With --to, it sends to that
address and replaces the invoice's BillEmail.Address. Only invoices are
supported. Use the numeric Id, not the document number. Invoice Cc/Bcc settings
still apply; --to replaces only the main billing email.
qbo send invoice 145 --dry-run
qbo send invoice 145 --to 'a+b@example.com' --dry-run
qbo send invoice 145 --force --no-input --sandbox --jsonDry runs print the POST and recipient to stderr without authentication or HTTP.
The command prompts for confirmation unless --force is set. Noninteractive
stdin and --no-input both require --force.
Never automatically retry a send. Each invocation makes at most one send
POST, without backoff or redirects. A 429, 5xx, timeout, transport failure, or
unverified response exits 1, never the retryable codes 7 or 8. Read the
invoice's EmailStatus and DeliveryInfo before deciding to send again:
qbo get invoice 145 --json | jq '.Invoice | {EmailStatus, DeliveryInfo}'401, 403 and 404 retain exit codes 4, 6 and 5. Successful output requires the
returned invoice to show EmailSent, a delivery timestamp and its recipient;
this is QuickBooks' send record, not proof of arrival in the recipient's inbox.
Human and plain output show Id, DocNumber, EmailStatus, BillEmail.Address
and DeliveryInfo.DeliveryTime. JSON preserves the complete QBO response,
including its Invoice wrapper. --select remains available for projection.
qbo void supports Invoice, Payment, SalesReceipt, and BillPayment. QBO zeroes
the amounts and preserves the document number, date, and audit trail. A void
can't be undone. The command prompts for confirmation unless --force is set;
--no-input requires --force.
qbo void invoice 145 --dry-run
qbo void invoice 145 --force --no-input --sandbox --json
qbo void payment 200 --sync-token 3 --force --no-input --sandbox --jsonThe command reads the current SyncToken before posting. --sync-token pins the
expected token and fails without posting if it differs. Records with TotalAmt
zero and PrivateNote starting with Voided return successfully with a hint and
no POST. Output retains QBO's entity wrapper. Dry runs make no API calls and
show the exact request body when --sync-token is supplied.
| Flag | Description |
|---|---|
| (default) | Colored tables, summaries on stderr |
--json / -j |
Structured JSON to stdout |
--plain / -p |
Tab-separated values, no color |
--results-only |
Strip pagination metadata |
--select f1,f2 |
Project output to specific fields |
Auto-JSON: when stdout is not a TTY and QBO_AUTO_JSON=1, defaults to JSON output.
| Code | Name | Description |
|---|---|---|
| 0 | success |
Operation completed successfully |
| 1 | error |
General error |
| 2 | usage |
Invalid usage or arguments |
| 3 | empty |
No results found |
| 4 | auth_required |
Authentication required or token expired |
| 5 | not_found |
Resource not found |
| 6 | forbidden |
Permission denied |
| 7 | rate_limited |
API rate limit exceeded |
| 8 | retryable |
Transient error, safe to retry |
| 10 | config_error |
Configuration error |
make build # Build to bin/qbo
make test # Run tests with race detector
make lint # golangci-lint
make vet # go vet
make fmt # gofmt
make ci # fmt + lint + vet + test + buildMIT