Skip to main content

Command-line interface for QuickBooks Online API

Project description

qbo-cli

A command-line interface for the QuickBooks Online API. Query entities, run reports, create invoices — all from your terminal. The QuickBooks CLI that developers actually want to use.

Built for QuickBooks automation — giving AI agents (like OpenClaw) and scripts clean, direct QBO command line access without dealing with unmaintained MCP servers or raw API calls every time. Also works great as a standalone QuickBooks API Python tool for developers who live in the terminal.

Features

  • OAuth 2.0 authentication with local callback server or manual mode
  • Query entities using QBO's SQL-like syntax with automatic pagination
  • Local text search over query results (useful for fields not queryable server-side)
  • CRUD + void operations on any QBO entity (Customer, Invoice, Bill, etc.)
  • Financial reports — P&L, Balance Sheet, Cash Flow, and more
  • Raw API access for anything the CLI doesn't cover
  • Auto token refresh — access tokens refresh transparently
  • Flexible date inputYYYY-MM-DD, DD.MM.YYYY, DD/MM/YYYY with -b/-e shorthands
  • TSV and JSON output — pipe to jq, awk, spreadsheets
  • Flexible output flags — use global -f/--format or subcommand --format/-o
  • Named profiles (prod/dev) for credential isolation across environments
  • Sandbox support for development and testing
  • File-locked token storage — safe for concurrent use

Installation

uv tool install qbo-cli

Or with pip: pip install qbo-cli

Requires Python 3.9+ on macOS or Linux (uses fcntl for file locking).

Setup

1. Create an Intuit Developer App

Go to developer.intuit.com, create an app, and note your Client ID and Client Secret.

Add a Redirect URI in your app's settings. For production apps, Intuit requires HTTPS with a real domain (e.g., https://yourapp.example.com/callback). For development, https://developer.intuit.com/v2/OAuth2Playground/RedirectUrl works. Set QBO_REDIRECT_URI to match.

Tip: If you're running on a headless server, use qbo auth init --manual — the redirect doesn't need to resolve. You'll just copy the URL from your browser's address bar after authorization.

2. Configure Credentials

Quickest: Interactive setup

qbo auth setup                    # production profile (default)
qbo --profile dev auth setup      # development/sandbox profile

This prompts for your Client ID, Client Secret, and Redirect URI, then saves to ~/.qbo/config.json.

Intuit provides separate Development and Production keys per app. Use --profile dev for sandbox keys.

Option A: Environment variables (for CI/scripts)

export QBO_CLIENT_ID="your-client-id"
export QBO_CLIENT_SECRET="your-client-secret"
export QBO_PROFILE="dev"           # optional: select profile

Option B: Config file (~/.qbo/config.json)

{
  "prod": {
    "client_id": "your-production-client-id",
    "client_secret": "your-production-client-secret"
  },
  "dev": {
    "client_id": "your-development-client-id",
    "client_secret": "your-development-client-secret",
    "sandbox": true
  }
}

Environment variables take precedence over the config file.

3. Authorize

qbo auth init                     # production
qbo --sandbox auth init           # development/sandbox

This opens an OAuth flow — authorize in your browser, and tokens are saved per-profile (~/.qbo/tokens.prod.json, ~/.qbo/tokens.dev.json, chmod 600).

On headless servers, use manual mode:

qbo auth init --manual

Usage

Check auth status

qbo auth status

Query entities

# All customers
qbo query "SELECT * FROM Customer"

# Recent invoices
qbo query "SELECT * FROM Invoice WHERE TxnDate > '2025-01-01'"

# Unpaid invoices
qbo query "SELECT * FROM Invoice WHERE Balance > '0'"

# Vendors with email
qbo query "SELECT DisplayName, PrimaryEmailAddr FROM Vendor"

# Count items
qbo query "SELECT COUNT(*) FROM Item"

# TSV output (great for spreadsheets)
qbo query "SELECT DisplayName, Balance FROM Customer WHERE Balance > '0'" -f tsv

Queries automatically paginate through all results (up to 100 pages × 1000 rows).

Search text in query results (local filter)

Some QBO fields are hard or impossible to filter server-side. Use search to run a normal QBO query, then filter returned rows locally by substring match against each row's JSON.

# Find invoices containing text in any field (including nested line descriptions/memos)
qbo search "SELECT * FROM Invoice WHERE TxnDate >= '2025-01-01'" "consulting fee"

# Case-sensitive search
qbo search "SELECT * FROM Invoice" "Owner Memo" --case-sensitive

# JSON output
qbo search "SELECT * FROM Invoice" "memo" --format json

Get a single entity

qbo get Customer 5
qbo get Invoice 1042

Create an entity

echo '{
  "DisplayName": "John Smith",
  "PrimaryEmailAddr": {"Address": "john@example.com"}
}' | qbo create Customer

echo '{
  "CustomerRef": {"value": "5"},
  "Line": [{
    "Amount": 150.00,
    "DetailType": "SalesItemLineDetail",
    "SalesItemLineDetail": {"ItemRef": {"value": "1"}}
  }]
}' | qbo create Invoice

Update an entity

# Fetch, modify, and update
qbo get Customer 5 | jq '.Customer.CompanyName = "New Name"' | qbo update Customer

Delete an entity

qbo delete Invoice 1042

The CLI fetches the entity first (to get the required SyncToken), then deletes it.

Void a transaction

qbo void Invoice 1042
qbo void SalesReceipt 55

Void keeps the transaction on the books as $0 (preserving audit trail), unlike delete which removes it entirely. The CLI fetches the entity first, then posts with ?operation=void.

Run reports

# Profit and Loss for a date range
qbo report ProfitAndLoss --start-date 2025-01-01 --end-date 2025-12-31

# Balance Sheet as of today
qbo report BalanceSheet

# Using date macros
qbo report ProfitAndLoss --date-macro "Last Month"
qbo report ProfitAndLoss --date-macro "This Year"

# With extra parameters
qbo report ProfitAndLoss --start-date 2025-01-01 --end-date 2025-12-31 accounting_method=Cash

Available reports: ProfitAndLoss, BalanceSheet, CashFlow, CustomerIncome, AgedReceivables, AgedPayables, GeneralLedger, TrialBalance, and more.

Raw API access

# GET request
qbo raw GET "query?query=SELECT * FROM CompanyInfo"

# POST with body
echo '{"TrackQtyOnHand": true}' | qbo raw POST "item"

General Ledger reports (gl-report)

Hierarchical GL reports for any account and customer, with auto-discovered sub-account trees.

# Explore your chart of accounts
qbo gl-report --list-accounts

# Drill into a specific account's sub-accounts
qbo gl-report -a 125 --list-accounts

# Account tree as JSON
qbo gl-report -a 125 --list-accounts --format json

# Generate a report (text by default)
qbo gl-report -c "John Smith" -a 125

# Human-readable text with currency prefix
qbo gl-report -c "John Smith" -a 125 --currency USD

# Custom date range
qbo gl-report -c "John Smith" -a "Revenue" --start 2025-01-01 --end 2025-12-31

# Short flags and flexible date formats
qbo gl-report -c "John Smith" -a 125 -b 01.01.2025 -e 31.12.2025

# Structured JSON output
qbo gl-report -a 125 --format json

# Dates default to: first transaction → today
qbo gl-report -c "John Smith" -a 125

gl-report supports text, json, txns, and expanded. tsv is not available for this command.

Output formats

# Text output (default, human-readable table)
qbo query "SELECT * FROM Customer"

# JSON
qbo query "SELECT * FROM Customer" -f json
qbo query "SELECT * FROM Customer" --format json

# TSV (tab-separated, for spreadsheets/awk)
qbo query "SELECT * FROM Customer" -f tsv

# Pipe to jq
qbo query "SELECT * FROM Customer" -f json | jq '.[].DisplayName'

Profiles and sandbox

# Use dev profile (sandbox keys + sandbox API endpoint)
qbo --sandbox query "SELECT * FROM Customer"
qbo --profile dev query "SELECT * FROM Customer"    # equivalent

# Switch profile via env var
export QBO_PROFILE=dev
qbo query "SELECT * FROM Customer"

Configuration Reference

Setting Env Variable Config Key Default
Profile QBO_PROFILE prod
Client ID QBO_CLIENT_ID client_id
Client Secret QBO_CLIENT_SECRET client_secret
Redirect URI QBO_REDIRECT_URI redirect_uri http://localhost:8844/callback
Realm ID QBO_REALM_ID realm_id From auth flow
Sandbox mode sandbox false

Config file: ~/.qbo/config.json (profiled format, see config.json.example)

Token storage: ~/.qbo/tokens.{profile}.json (per-profile, created automatically, chmod 600)

Token Management

  • Access tokens expire every 60 minutes. The CLI refreshes them automatically before each request.
  • Refresh tokens are valid for 100 days. Each refresh extends the 100-day window (rolling expiry).
  • If you don't use the CLI for 100+ days, the refresh token expires and you need to re-authorize with qbo auth init.
  • The CLI warns you when the refresh token has fewer than 14 days remaining.
  • Token refresh uses file locking — safe to run concurrent qbo commands.

Force a manual refresh:

qbo auth refresh

CI/CD

Lint — runs on every push and PR to main:

  • ruff check (errors, warnings, import sorting)
  • ruff format --check (code style)

Tests — runs on every push and PR to main (Python 3.9 + 3.12).

Publish — auto-publishes to PyPI when you create a GitHub Release:

  1. Bump version in pyproject.toml (single source of truth — qbo_cli/__init__.py reads it via importlib.metadata)
  2. Commit and push
  3. Create a GitHub Release (tag vX.Y.Z)
  4. uv build + trusted publishing → PyPI

First-time setup: Add qbo-cli as a trusted publisher on PyPI → Your projectsPublishing → add GitHub publisher: alexph-dev/qbo-cli, workflow publish.yml, environment pypi.

Contributing

Contributions welcome. Please open an issue first to discuss what you'd like to change.

git clone https://github.com/alexph-dev/qbo-cli.git
cd qbo-cli
uv sync --extra test
uv run pytest             # run tests
uv run ruff check qbo_cli/   # lint before committing

License

MIT — see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

qbo_cli-0.9.0.tar.gz (127.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

qbo_cli-0.9.0-py3-none-any.whl (34.7 kB view details)

Uploaded Python 3

File details

Details for the file qbo_cli-0.9.0.tar.gz.

File metadata

  • Download URL: qbo_cli-0.9.0.tar.gz
  • Upload date:
  • Size: 127.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for qbo_cli-0.9.0.tar.gz
Algorithm Hash digest
SHA256 ec2f6041bcd4c3b36346fcad1b5fa15e74e6eddb05770b32ec05fb48f0dd88b3
MD5 cccbc0bba63b79f3c2f46f24eab5dec8
BLAKE2b-256 598de96cce94cdeefe7cfaf5b8ef6fa39b113a5c2d76f990489d6b648012f104

See more details on using hashes here.

Provenance

The following attestation bundles were made for qbo_cli-0.9.0.tar.gz:

Publisher: publish.yml on alexph-dev/qbo-cli

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file qbo_cli-0.9.0-py3-none-any.whl.

File metadata

  • Download URL: qbo_cli-0.9.0-py3-none-any.whl
  • Upload date:
  • Size: 34.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for qbo_cli-0.9.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d1e7356d87546b2f906bbae082e1342c937e5f6dc720084d03dae6ff4c240214
MD5 17462eb6486fb2f309a1d7e4b51b38e6
BLAKE2b-256 d401fd3b73a695c5f2662a5ed9d798705d702936ace02908f4ff774eecf9fc4d

See more details on using hashes here.

Provenance

The following attestation bundles were made for qbo_cli-0.9.0-py3-none-any.whl:

Publisher: publish.yml on alexph-dev/qbo-cli

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page