← all adapters

qbo-style

simulates QuickBooks Online API v3

restadapter v0.1.011 routes

verification

VMVM tier — no SDK suite yet· surface 11 · not implemented 6 · differs 4

use it

# add to stunt.yaml
services:
  qbo:
    adapter: embedded:qbo-style

# then
$ stunt up

The embedded: source extracts this adapter from the binary — no git clone, fully offline.

how to mock the QuickBooks Online API locally

Install stunt, paste the service block above into stunt.yaml, run stunt up — then point your client at localhost instead of QuickBooks Online API. It works offline, keeps state across requests and restarts, and every response is synthetic: the route shapes, status codes, pagination, and webhook delivery follow the real QuickBooks Online API v3 surface — the data is fake, the behavior is real.

backing stores

oauth_codesaccess_tokensrefresh_tokenscustomersinvoices

api surface (11)

generated from the adapter manifest — exactly what boots, nothing more

11 routes
GET/oauth/v2/authorize
POST/oauth/v2/tokens/bearer
GET/v3/company/{realmId}/query
POST/v3/company/{realmId}/query
GET/v3/company/{realmId}/customer
POST/v3/company/{realmId}/customer
GET/v3/company/{realmId}/customer/{id}
DELETE/v3/company/{realmId}/customer/{id}
POST/v3/company/{realmId}/invoice
GET/v3/company/{realmId}/invoice/{id}
DELETE/v3/company/{realmId}/invoice/{id}

VM-verified behaviors (23)

  • authorize without client_id/redirect_uri/state is invalid_request
  • authorize 302s back to the redirect_uri with code, state and a fresh realmId
  • the auth code exchanges once into a token pair; reuse is invalid_grant
  • refresh rotates the refresh token and kills the old one (the QBO churn)
  • unknown grants and unknown codes are OAuth 400s
  • v3 calls demand a live bearer: absent, unknown and expired tokens are 401 fault 32001
  • the bearer is realm-bound: another company's path is 401
  • DisplayName is required: a 400 fault 610
  • customer create assigns Id, SyncToken 0 and QBO defaults
  • reads by path id and ?id= round-trip; unknown ids are 404 fault 620
  • POST with Id updates: a sparse merge bumps SyncToken and leaves unspecified fields alone
  • DELETE deactivates: the record stays readable by id but leaves the active list
  • invoice create prices its Line items into TotalAmt/Balance and mints a DocNumber
  • a Line is required: a 400 fault 610
  • void flips status Voided and zeroes Balance, keeping the record
  • DELETE is a hard delete: the record is gone afterwards
  • entity detection follows the FROM token, so CustomerRef.value reads Invoices
  • WHERE filters by equality, LIKE and IN over stored fields
  • ORDER BY DESC with MAXRESULTS caps the page and reports maxResults
  • the POST query endpoint takes the statement from the body
  • default reads hide deactivated customers; WHERE Active = False surfaces them
  • an unknown entity answers an empty QueryResponse; a statement without one is a 400 fault
  • STARTPOSITION pages with MAXRESULTS: row 2 of the sorted set

Not implemented (6)

  • Only Customer and Invoice — no Payment, Estimate, Item, Vendor, etc.
  • No QSQL aggregates (COUNT, SUM, MAX, MIN, AVG)
  • No batch endpoint (POST /v3/company/{realmId}/batch)
  • No CDC endpoint (GET /v3/company/{realmId}/cdc)
  • No reports endpoints (Profit and Loss, Balance Sheet, Trial Balance)
  • No Intuit webhooks (QBO event notifications)

Differs from the real API (4)

  • Malformed FROM tokens fall back to substring entity matching instead of erroring
  • Full (non-sparse) update merges like sparse; real QBO replaces and clears omitted fields
  • SyncToken optimistic concurrency is not enforced — stale tokens still update
  • Data is not realm-partitioned: one realm's token reads another realm's records
want more of the QuickBooks Online API surface? adapters are YAML + Starlark — the authoring guide covers adding routes, fixtures, and state machines. Open an issue for anything big first, or just file a PR — merged routes ship in the next release.