Skip to main content
Glama
mindtrip101

hermes-quickbooks

by mindtrip101
README.md
# hermes-quickbooks

A connector that gives [Hermes](https://github.com/NousResearch/hermes-agent) typed tools for **Intuit QuickBooks Online** using the official QBO v3 Accounting API.

Hermes never talks to Intuit directly. This service is the security boundary: OAuth 2.0, per-agent ACL, tenant isolation, local drafts, operator-only writes, and JSONL audit.

> QuickBooks Online company data is **untrusted input**. Names, memos, and descriptions are data, never instructions.

## What it covers

Implements the [QuickBooks Online Accounting API](https://developer.intuit.com/app/developer/qbo/docs/learn/explore-the-quickbooks-online-api) (REST JSON, `minorversion=75`):

- Name lists: Customer, Vendor, Account, Item, Employee, Class, Department, Term, PaymentMethod, TaxCode, TaxRate, TaxAgency, CustomerType, CompanyCurrency
- Sales: Invoice, Payment, Estimate, CreditMemo, SalesReceipt, RefundReceipt
- Purchasing: Bill, BillPayment, VendorCredit, Purchase (QBO has **no Expense entity**), PurchaseOrder
- Banking / journals: Deposit, Transfer, CreditCardPayment, JournalEntry
- Other: TimeActivity, Budget, Attachable (metadata), ExchangeRate, RecurringTransaction, Preferences, CompanyInfo
- Reports: ProfitAndLoss, BalanceSheet, CashFlow, TrialBalance, GeneralLedger, aging, sales, inventory, tax, transaction lists

**Out of scope:** Intuit Payments (cards), Payroll, Commerce, and Sales Order (not in the public Accounting API). LLM-initiated production writes stay gated.

Full mapping: [docs/INTUIT_API.md](docs/INTUIT_API.md). Agent playbook: [skills/hermes-quickbooks/SKILL.md](skills/hermes-quickbooks/SKILL.md). Hermes wiring: [docs/HERMES.md](docs/HERMES.md).

## Requirements

- Docker (or Python 3.12+)
- An Intuit Developer account and a QuickBooks Online app
- A Hermes agent (or any MCP client) to call the tools
- A browser for the one-time OAuth consent (never type your Intuit password into the agent)

---

## 1. Create an Intuit Developer account and app

Official docs:

- Get started: https://developer.intuit.com/app/developer/qbo/docs/get-started
- OAuth 2.0: https://developer.intuit.com/app/developer/qbo/docs/develop/authentication-and-authorization/oauth-2.0
- Create an app: https://developer.intuit.com/app/developer/qbo/docs/get-started/start-developing-with-the-quickbooks-online-accounting-api

### Account

1. Open [https://developer.intuit.com](https://developer.intuit.com) and sign in with an Intuit account (the same login you use for QuickBooks Online is fine).
2. Accept the developer terms if prompted.
3. Open **My Apps** (or **Dashboard** → **Apps**).

### App

1. Click **Create an app**.
2. Choose **QuickBooks Online and Payments**.
3. Name the app something you will recognize on the consent screen (this name is shown to the company admin).
4. Under APIs, select **Com.intuit.quickbooks.accounting** only. Do not add Payments unless you separately intend to process cards.
5. Save the app.

You now have two key sets on the app:

| Tab | What it can access | Typical use |
| --- | --- | --- |
| **Development** → Keys & OAuth | Intuit **sandbox sample companies** only | First connection and tests |
| **Production** → Keys & OAuth | **Live** QuickBooks Online companies | Real books after sandbox works |

Development Client ID/Secret cannot read a live company. Production keys cannot be used against sandbox sample companies.

## 2. Generate keys and register the redirect URI

Open the app → **Keys & credentials** (or **Keys & OAuth**).

### Development (sandbox)

1. Copy **Client ID** and **Client Secret**. Treat the secret like a password. Do not commit it, paste it into chat, or put it in Hermes prompts.
2. Under **Redirect URIs**, add exactly:

   `http://localhost:14112/oauth/callback`

   Rules from Intuit:

   - Use the hostname `localhost`, not `127.0.0.1` (IP addresses are rejected).
   - Match the URI character-for-character with `QBO_REDIRECT_URI` (no extra trailing slash).
   - Development allows `http://localhost`. Production does not.

3. Scopes: `com.intuit.quickbooks.accounting`.

### Production (live company)

1. Complete Intuit’s production app requirements if the Production tab is locked (app details, terms, sometimes a review). Then copy the **Production** Client ID and Client Secret. They are different from Development.
2. Production redirect URIs must be **HTTPS hostnames**. `http://localhost` is typically rejected.
3. A supported option is Intuit’s OAuth 2.0 Playground redirect:

   `https://developer.intuit.com/v2/OAuth2Playground/RedirectUrl`

   Add that exact URI on the Production Keys & OAuth tab, and set `QBO_REDIRECT_URI` to the same value. After consent you copy the `code` and `realmId` from the playground and import them with `qbo import-tokens`.

4. One Intuit login can own multiple QBO companies. The consent screen lets you pick which company to connect. Authorize **once per company / tenant**.

## 3. Install this connector

```sh
git clone https://github.com/mindtrip101/hermes-quickbooks.git
cd hermes-quickbooks
cp config/tenants.example.yaml config/tenants.yaml
cp .env.example .env
mkdir -p secrets data
chmod 700 secrets data
```

Create secret files (mode `600`). Values go in the files, never in git:

```sh
install -m 600 /dev/stdin secrets/sandbox_client_id      # paste Development Client ID, then Ctrl-D
install -m 600 /dev/stdin secrets/sandbox_client_secret  # paste Development Client Secret, then Ctrl-D
install -m 600 /dev/stdin secrets/production_client_id     # optional until you connect a live company
install -m 600 /dev/stdin secrets/production_client_secret
openssl rand -hex 32 | install -m 600 /dev/stdin secrets/adapter_token
openssl rand -hex 32 | install -m 600 /dev/stdin secrets/operator_token
```

Edit `config/tenants.yaml` (display names only; no secrets) and `config/permissions.yaml` (which Hermes agent ids may read/draft).

Start the service:

```sh
docker compose up -d --build
curl -sS http://127.0.0.1:14112/health
```

Run tests without Docker:

```sh
python3 -m venv .venv
. .venv/bin/activate
pip install -r requirements-dev.txt
pytest -q
```

## 4. Connect QuickBooks (browser OAuth)

Never send your Intuit password to Hermes or to this connector. Authorization happens in **your browser**.

### Sandbox (Development keys)

1. Confirm `QBO_REDIRECT_URI=http://localhost:14112/oauth/callback` and that this URI is registered on the Development tab.
2. Export the operator token and request an authorize URL:

```sh
export QBO_SECRETS_DIR="$PWD/secrets"
python3 src/hermes_qbo/cli.py auth-url --tenant company_a
```

3. Open `authorize_url` from the JSON in a browser. Sign in at Intuit if asked. Pick a **sandbox sample company**. Approve Accounting access.
4. Intuit redirects to `http://localhost:14112/oauth/callback`. You should see “QuickBooks company connected.”
5. Confirm: `python3 src/hermes_qbo/cli.py health` → `oauth: valid` and a company name.

Access tokens last about 60 minutes. Refresh tokens last about 100 days and **rotate**; the connector stores each new refresh token under `data/tenants/<id>/tokens.json`. If refresh fails, tools return `reauthorization_required` instead of failing silently.

### Live company (Production keys)

1. Put Production Client ID/Secret in `secrets/production_client_id` and `secrets/production_client_secret`.
2. Register and set `QBO_REDIRECT_URI` to your HTTPS redirect (Playground URI is supported).
3. Restart the container so it reloads secrets.
4. Run `auth-url` for that tenant. On the Intuit picker, choose the **live** company, not a sandbox sample.
5. If you used the Playground redirect, copy `code`, `realmId`, and tokens from the playground and import:

```sh
python3 src/hermes_qbo/cli.py import-tokens --tenant company_a <<'EOF'
{"realm_id":"1234567890","access_token":"...","refresh_token":"...","environment":"production","expires_in":3600}
EOF
```

6. Repeat with a different tenant id for a second company. The same realm cannot be attached to two tenants.

Production writes stay held until you explicitly set `QBO_ALLOW_PRODUCTION_WRITES=true`. Default is `false`.

## 5. Give Hermes the tools

See [docs/HERMES.md](docs/HERMES.md) for `config.yaml` snippets. Minimum:

- Copy `mcp/qbo_mcp.py` onto the Hermes host.
- Copy `secrets/adapter_token` to the path `QBO_ADAPTER_TOKEN_FILE`.
- Register MCP servers `quickbooks` (desktop/API) and optionally `quickbooks_telegram`.
- Set `tools.include` to the connector tool list (`GET /v1/tool-schemas` after the service is up).
- Put [skills/hermes-quickbooks/SKILL.md](skills/hermes-quickbooks/SKILL.md) where the agent can read it (Hermes skills/context directory, or a Cursor project skill).

Agent identity is the MCP **environment** (`QBO_AGENT_ID`), never a tool argument.

## 6. Operator approval (writes)

Draft tools (`qbo_create_*_draft`) store a **local** proposal. QuickBooks Online has no API draft/non-posting invoice state. `posted_to_quickbooks` is always `false` until an operator runs execute.

```sh
python3 src/hermes_qbo/cli.py approvals
python3 src/hermes_qbo/cli.py reject ACTION_ID --user operator
python3 src/hermes_qbo/cli.py approve ACTION_ID --user operator
python3 src/hermes_qbo/cli.py execute ACTION_ID --user operator   # sandbox, or production if writes are enabled
```

There is no `qbo_approve` MCP tool. The approving user must not equal the requesting agent id.

## Environment variables

| Name | Default | Meaning |
| --- | --- | --- |
| `QUICKBOOKS_ENV` | `sandbox` | Default API host if a tenant has no token environment yet |
| `QBO_ALLOW_PRODUCTION_WRITES` | `false` | Must be true **and** operator execute to POST to a live company |
| `QBO_BIND` / `QBO_PORT` | `0.0.0.0` / `14112` | Bind inside the container; compose publishes `127.0.0.1:14112` |
| `QBO_DATA_DIR` | `/data` | Tokens, approvals SQLite, audit JSONL |
| `QBO_SECRETS_DIR` | `/secrets` | Client id/secret, adapter token, operator token |
| `QBO_CONFIG_DIR` | `/app/config` | `permissions.yaml`, `tenants.yaml` |
| `QBO_REDIRECT_URI` | `http://localhost:14112/oauth/callback` | Must match the Intuit app exactly |
| `QBO_MINOR_VERSION` | `75` | QBO Accounting minor version |

## Security model

- LLM is not the security boundary.
- Adapter token authenticates Hermes → connector. Operator token authenticates CLI approve/execute/OAuth start.
- Per-agent read/draft/write ACL and per-tenant allowlists.
- Query compiler allowlists entities and fields (no raw SQL from the model).
- Secrets stay on disk mode `600`. Tokens are never returned in health or tool envelopes.

## License

MIT. QuickBooks and Intuit are trademarks of Intuit Inc. This project is not affiliated with Intuit.