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 and qbo_cli/__init__.py
  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.8.0.tar.gz (109.5 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.8.0-py3-none-any.whl (25.0 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: qbo_cli-0.8.0.tar.gz
  • Upload date:
  • Size: 109.5 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.8.0.tar.gz
Algorithm Hash digest
SHA256 355ae221f168a20421f11b8a012e69bba7b331f8011fcca35703b49fe7c23bec
MD5 d1edc8390117c51c9ccb962d2edae2a4
BLAKE2b-256 e3aa8f4929568e8cc1e684f2400f5d91eac456cba9ad5c9a3d6aa45a6360f501

See more details on using hashes here.

Provenance

The following attestation bundles were made for qbo_cli-0.8.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.8.0-py3-none-any.whl.

File metadata

  • Download URL: qbo_cli-0.8.0-py3-none-any.whl
  • Upload date:
  • Size: 25.0 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.8.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b5beb49824484e9654c4cae35419d00f77fae4a1dcb20950f9e881c4880c6b48
MD5 61de381f5b6718994d55e7ddc6a0f3fd
BLAKE2b-256 726d81d25cddac75673fada79e72973d5005ac2e90ffd3acd181cca42d5d05e5

See more details on using hashes here.

Provenance

The following attestation bundles were made for qbo_cli-0.8.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