npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@hanzo/quickbooks

v1.0.0

Published

Hanzo AI for QuickBooks Online — AI over your books: summarize financials, explain reports, draft invoice line descriptions, categorize transactions, and ask about the books. A web panel plus an Intuit OAuth2 + Accounting API v3 proxy, built on @hanzo/ai

Readme

Hanzo AI for QuickBooks Online

AI over your books, in plain language. @hanzo/quickbooks reads your QuickBooks Online financials and answers questions about them — summarize financials, explain a report, draft invoice/estimate line descriptions, categorize / explain transactions, and a freeform ask about the books.

Two surfaces, one shared core:

  • Panel (dist/index.html) — a web page (hosted behind hanzoai/ingress at quickbooks.hanzo.ai) that runs the AI actions over a QuickBooks report (Profit & Loss, Balance Sheet), an entity query (Invoices / Bills / Transactions / Customers / Accounts), or plain figures pasted into it.
  • Service (dist/server.js) — a small Node service that handles the Intuit OAuth2 flow and a QBO API proxy (/v1/analyze) so the Intuit client_secret and the OAuth tokens stay server-side: it fetches the report or runs the query against the Accounting API v3, assembles it into analysis text, and summarizes it via api.hanzo.ai.

Both are built on the shared Hanzo foundation — @hanzo/ai (the published headless OpenAI-compatible model client) and @hanzo/iam (Hanzo identity) — and speak the gateway's /v1 wire protocol, identical to @hanzo/docusign, @hanzo/pdf, and @hanzo/meetings.

Analysis is informational, grounded strictly in your QuickBooks data — not tax, audit, or accounting advice. The system prompt forbids inventing accounts, amounts, dates, or trends.

Architecture

src/
  config.ts        env boundary: gateway, model, Intuit OAuth + QBO API hosts, scopes, server config
  qbo-oauth.ts     Intuit OAuth2 Authorization Code Grant request shaping + callback/token parsing (pure)
  qbo-api.ts       Accounting API v3 request wrappers (report/query/get/create) + response parsing (pure)
  finance.ts       QBO report/entity JSON → readable analysis text + truncation to a budget (pure)
  hanzo.ts         prompt assembly + the single `ask` path over the @hanzo/ai SDK (pure/injected)
  actions.ts       the AI actions as prompts over `ask` (one code path to the model)
  qbo-analysis.ts  the pipeline: fetch report/query → assemble context → run action (injected fetch)
  panel.ts         pure panel logic (launch-context parse, interpret pasted data → FinanceContext)
  app.ts           browser panel: DOM glue over the pure modules
  server.ts        Node service: /oauth/install, /oauth/callback, /v1/analyze, /v1/companies, /healthz
  index.html       panel markup   styles.css   panel styles

Everything logic-heavy is pure and unit-tested; app.ts (browser) and server.ts (Node) are the only impure entry points. The gateway is reached through one shape — the published createAiClient().chat.completions.create() from @hanzo/ai — never a hand-rolled client.

Build & test

pnpm --filter @hanzo/quickbooks install
pnpm --filter @hanzo/quickbooks test        # vitest — 91 tests
pnpm --filter @hanzo/quickbooks typecheck    # tsc --noEmit, clean
pnpm --filter @hanzo/quickbooks build        # esbuild -> dist/

build.js bundles the panel (app.js, with @hanzo/ai inlined for the browser), copies index.html + styles.css, and bundles the Node service (server.js, with @hanzo/ai left external and loaded from node_modules).

Create an Intuit developer app

  1. Sign in at the Intuit Developer portal and Create an appQuickBooks Online and Payments.
  2. Under Keys & OAuth, note the Client ID and Client Secret for each environment (Development / Production). The secret is server-only — it lives in KMS and is read by server.ts; it is never bundled or sent to the browser.
  3. Add the Redirect URI: https://quickbooks.hanzo.ai/oauth/callback (and a local one, e.g. http://localhost:8790/oauth/callback, for dev).
  4. The app requests these OAuth scopes:
    • com.intuit.quickbooks.accounting — the Accounting API.
    • openid — returns the id_token (identity) at token exchange.
  5. Sandbox company: the developer account comes with a free sandbox company. Use QBO_ENV=sandbox (API host sandbox-quickbooks.api.intuit.com) against it before going to production (quickbooks.api.intuit.com).

OAuth2 flow

| Endpoint | Host | | --- | --- | | Authorize | https://appcenter.intuit.com/connect/oauth2 | | Token (exchange + refresh) | https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer | | Revoke (disconnect) | https://developer.api.intuit.com/v2/oauth2/tokens/revoke |

The authorize + token hosts are the same for sandbox and production; the environment selects only the Accounting API host. Token exchange uses HTTP Basic auth (base64(client_id:client_secret)) and a form body that includes redirect_uri. On approval Intuit redirects to the callback with code, state, and — critically — realmId (the company id), which scopes every API path and keys the server's stored authorization. Access tokens live ~1 hour (/v1/analyze refreshes transparently on a 401); refresh tokens rotate ~daily and live ~100 days.

Reports and entities used

Everything is on the Accounting API v3 root https://{host}/v3/company/{realmId}/… with a pinned minorversion:

| Data | Request | | --- | --- | | Profit & Loss | GET /reports/ProfitAndLoss?start_date=…&end_date=… | | Balance Sheet | GET /reports/BalanceSheet | | Cash Flow / Aged Receivables / Aged Payables | GET /reports/{name} | | Invoices / Bills / Customers / Accounts / Transactions | GET /query?query=SELECT * FROM {Entity} … | | A single record | GET /{entity}/{id} | | Light write-back (create/update invoice, add a memo) | POST /{entity} |

Reports come back as a nested Header / Columns / Rows tree (sections with Summary subtotals); finance.ts flattens that into an indented, readable statement the model reasons over. Queries wrap an entity array under QueryResponse.{Entity}; each record is projected to its accounting-relevant fields.

Write-back (gated)

The one write path is qbo-api.create(env, realmId, token, entity, payload) — a POST /{entity}. Primary value is read + analyze; write-back (create/update an Invoice, add a memo) is deliberately behind an explicit action and is off by default. Never wired to an AI action that runs without a human confirming the payload.

The summarize-financials flow

The canonical path, end to end (qbo-analysis.analyzeReport):

  1. FetchGET /v3/company/{realmId}/reports/ProfitAndLoss with the period params + minorversion, Authorization: Bearer <access_token>.
  2. Renderfinance.renderReport walks the Rows tree into an indented statement: section headers, line items with their period columns, section subtotals (Total Income, Total Expenses), and the top-level Net Income.
  3. Windowfinance.buildReportContext caps the rendered statement at the character budget on a line boundary, flagging truncation honestly.
  4. Assemblehanzo.buildMessages fences the statement as data under a grounded system prompt (work only from the figures shown; cite lines by name; not accounting advice).
  5. Summarize — one non-streaming completion via the published @hanzo/ai createAiClient().chat.completions.create() against api.hanzo.ai.

In the panel, the user pastes the report JSON and the same pure finance.ts / hanzo.ts produce the answer directly against the gateway with their pasted hk-… key — zero backend. In the service, /v1/analyze runs the whole flow server-side so the Intuit token and the Hanzo key never leave the server.

Deploy over hanzoai/ingress

The panel is static (dist/index.html, app.js, styles.css) served by hanzoai/static; the service (dist/server.js) is a small container behind hanzoai/gateway + hanzoai/ingress at quickbooks.hanzo.ai. Secrets come from KMS (kms.hanzo.ai) synced via KMSSecret CRDs — never in manifests, never in env files committed to git.

Required environment (see config.readServerConfig):

| Var | Notes | | --- | --- | | INTUIT_CLIENT_ID | OAuth2 client id (public). | | INTUIT_CLIENT_SECRET | OAuth2 secret — server only, from KMS. | | INTUIT_REDIRECT_URI | https://quickbooks.hanzo.ai/oauth/callback. | | QBO_ENV | sandbox (default) or production. | | HANZO_API_KEY | Optional — enables the /v1/analyze proxy; without it the panel uses its own pasted key. | | PORT | Default 8790. |

The server exposes GET /healthz for readiness. Run: node dist/server.js.

Intuit App Store path

To list publicly on the QuickBooks App Store:

  1. Move the app to Production keys and pass Intuit's technical + security review (OAuth2, token handling, Disconnect via the revoke endpoint, least-privilege scopes — this app requests only accounting + openid).
  2. Complete the app listing (categories, screenshots, description) and the security questionnaire.
  3. Intuit reviews and publishes. Until then the app runs against the sandbox and any development companies you connect directly.

License

MIT © Hanzo AI