Nexus
Documentation / Connectors

QuickBooks Online

Connect QuickBooks Online accounting data to Nexus over the v3 REST API with read, write, and schema discovery. Filter and order-by push down into the QBO query language; aggregation runs in the Nexus engine.

The QuickBooks Online connector (bundle version 2.0.1) gives Nexus read, write, and discovery access to your QBO company over the Intuit Accounting API v3. Finance and operations teams can query accounting entities, automate record creation, and integrate QBO into cross-system pipelines without exporting CSVs or embedding credentials in code.

This page is for engineers wiring QBO into Nexus. It covers OAuth token handling, the exact pushdown boundary (filter and order push down; aggregation does not), the supported entities and operators, write semantics including the entities QBO will not let you hard-delete, and the failure modes you should design around.

The pushdown boundary, up front

QBO's query language supports WHERE and ORDERBY but has no GROUP BY, JOIN, or aggregate surface. The adapter therefore declares filter and order pushdown only:

  • Your WHERE conditions and ordering are rendered into the QBO query string and evaluated by Intuit.
  • Any operator QBO cannot express is re-applied in the Nexus engine as a safety net (the clean fallback described in Operators & pushdown), so results are never silently wrong.
  • Aggregation and GROUP BY always run in the Nexus engine, after rows are fetched, bounded by the engine's post-process row budget. Filter down to the smallest useful set before aggregating.

Do not assume the uniform SQL pushdown you get from the database connectors — QBO is a filter-and-order surface with in-engine aggregation.

Authentication

QuickBooks Online uses OAuth 2.0 with a rotating refresh token. There is no API key option. You authorize a QBO company once through the Intuit OAuth flow, which produces a refresh token. From then on, Nexus:

  • Mints short-lived access tokens (1-hour TTL) automatically.
  • Persists each new refresh token back to Key Vault after every rotation, so the connection survives restarts and replica changes without manual re-seeding.
  • Serializes concurrent refresh calls so two replicas cannot double-rotate and orphan each other's token.

Required secrets

Secret name Description
quickbooks.client_id Client ID from your Intuit developer app
quickbooks.client_secret Client Secret from your Intuit developer app
quickbooks.refresh_token Seed refresh token from the OAuth consent flow
quickbooks.realm_id Company ID (realmId) of the QBO company
quickbooks.__opt_environment production (default) or sandbox
quickbooks.__opt_minor_version API minor version; defaults to 75

One bridge = one company. To connect multiple QBO companies, create one bridge per company. All secrets live in your tenant Key Vault; see Secrets.

Connecting a QuickBooks company

What you need

  • An Intuit developer app with the Accounting scope (com.intuit.quickbooks.accounting). Create one at developer.intuit.com if your organization does not already have one.
  • Admin access to the QuickBooks company you want to connect.
  • A decision on Production vs Sandbox — the setup steps are identical for both.

Step 1: Get a refresh token and realm ID

The fastest path is the Intuit OAuth 2.0 Playground:

  1. Select your app from the dropdown.
  2. Tick the com.intuit.quickbooks.accounting scope and choose your environment.
  3. Click Get authorization code and authorize the target company.
  4. Click Get tokens. Copy the Refresh Token and the Realm ID shown on the page.

Do this immediately before the next step. The refresh token rotates the moment anything exchanges it (including clicking "Refresh token" again in the Playground), so do not generate it and leave it idle.

Step 2: Create a QuickBooks bridge in Nexus

In the Nexus admin console or management API, create a new QuickBooks bridge and supply the six secrets above. Set quickbooks.__opt_environment to sandbox if you authorized a sandbox company. Nexus stores these in Key Vault and manages all subsequent token rotation automatically.

Step 3: Verify the connection

Run a discovery or a simple query (for example, list a few customers) to confirm the connection is live. A successful result means Nexus has obtained a working access token from your stored refresh token.

Supported entities

The connector exposes all standard QuickBooks Online entities for reading, including Account, Bill, BillPayment, Customer, Deposit, Employee, Estimate, Invoice, Item, JournalEntry, Payment, Purchase, PurchaseOrder, SalesReceipt, Vendor, and more. Schema discovery lists the full set available in your company.

Capabilities

Capability Supported
Read Yes
Write (create, update, delete) Yes
Merge (upsert) No — insert/update/delete only
Schema discovery Yes
WHERE filter pushdown Yes — rendered into the QBO query language
Order-by pushdown Yes — rendered as ORDERBY
Aggregation / GROUP BY pushdown No — aggregate in the Nexus engine

Supported filter operators

$eq, $ne, $gt, $gte, $lt, $lte, $like, $in, $not_in, $between, $empty, $contains, $starts_with, $ends_with, $expr

QBO's query language natively supports =, IN, <, >, <=, >=, and LIKE. Operators that map to constructs QBO does not accept (such as $ne) are either evaluated in-engine or rejected at the API level — keep filters within the natively supported set for server-side evaluation.

Example: read open invoices

The adapter translates a UQL read into the QBO query string format. For open invoices ordered by date, it emits:

SELECT * FROM Invoice WHERE Balance > 0 ORDERBY TxnDate DESC STARTPOSITION 1 MAXRESULTS 100

QBO uses ORDERBY as a single word (not ORDER BY) and STARTPOSITION/MAXRESULTS for pagination. The adapter emits these forms automatically; you do not write them yourself.

Writing records

Operation Behavior
Create POST a full QBO object. Supply all fields the entity type requires.
Update Sparse update: provide the record ID and only the fields to change. The adapter fetches SyncToken automatically.
Delete Supported for transaction entities (invoices, bills, payments). SyncToken is auto-fetched.
Merge (upsert) Not supported. Read the record first, then branch to create or update.

Name-list entities cannot be hard-deleted

QuickBooks Online does not allow permanent deletion of Customers, Items, Accounts, or Vendors. To deactivate one, use a sparse update with Active = false:

await ctx.adapters.quickbooks.update(
    collection="Customer",
    record_id="42",
    set={"Active": False},
)

CompanyInfo and Preferences are read-only collections; write attempts against them are rejected by the adapter's writability gate before any request is sent, independent of the caller's RBAC grants.

Limits & behavior notes

  • Aggregation is in-engine. Any GROUP BY/SUM/COUNT runs in Nexus after fetch — bound the fetched set with a tight WHERE and limit.
  • No upsert. There is no atomic merge; emulate it with a read-then-write branch.
  • Refresh-token drift is the main failure mode. A refresh token unused for ~100 days, or copied and then rotated out from under Nexus, becomes invalid — re-seed via Step 1.
  • Pagination is STARTPOSITION/MAXRESULTS. Large result sets page automatically; very large scans are still subject to Intuit's API throttling.
  • HTTP 429 backoff. The adapter backs off on Intuit 429 responses; this is adapter-level HTTP backoff, not pipeline-run retry (Nexus does not auto-retry a failed run).

Troubleshooting

Symptom Fix
invalid_grant: incorrect or invalid refresh token The token rotated after you copied it, or was unused for over 100 days. Redo Step 1 and update quickbooks.refresh_token.
Redirect URI mismatch during authorization Add the URI shown by the Playground to your app's Redirect URIs, save, and retry.
Queries return no rows, no error Confirm quickbooks.realm_id and quickbooks.__opt_environment match the company you authorized.
400 on a query QBO rejected the syntax. Its query language supports WHERE, ORDERBY, and comparisons but not GROUP BY, JOIN, or !=.
Fails after a long idle period Re-seed quickbooks.refresh_token via Step 1 — a restart during an idle rotation window can leave the persisted token out of sync.

See also