Skip to main content

QuickBooks Sync (v1)

FieldValue
StatusDeferred. Not included in any current tier.
OwnerUnassigned
Target reposattunelogic-api, attunelogic-service
Supersedes
RelatedMonetization & Tier Packaging (v1), Feature Roadmap Index
Last updated2026-04-23

Status banner: QuickBooks sync was removed from the public Growth tier in the v1 monetization pass. A CSV export that is QuickBooks-compatible stays on Growth; full two-way sync lives here as a roadmap item.


Why this was removed from v1

  • attunelogic-api/src/services/quickbooks.js exists today as a commented-out stub. None of the OAuth wiring, token storage, invoice push, or reconciliation is implemented.
  • Shipping this on our launch timeline would have required a production Intuit app registration, OAuth redirect URIs, a token refresh worker, and at least a one-way invoice push path. That is 2–3 weeks of dedicated work plus an Intuit review cycle.
  • Customers already have a path today: the CSV accounting export on Growth (QuickBooks Online supports IIF / CSV import of invoices and customers). This covers 80% of the integration value on day one without any external dependencies.

What shipping this requires

Data model

  • New embedded sub-document on Customer.integrations.quickbooks:
    • connected: boolean
    • realmId: string (Intuit company ID)
    • accessTokenCipher: string (encrypted at rest; use the existing mongoose crypto helpers)
    • refreshTokenCipher: string
    • accessTokenExpiresAt: Date
    • refreshTokenExpiresAt: Date
    • lastSyncedAt: Date
    • scopes: string[]
    • connectedByUserId: ObjectId
  • New QuickBooksSyncLog model scoped by parentCompany recording per-entity sync attempts (invoice push, customer upsert, payment match) with status, Intuit response id, and error payload for reconciliation UI.

API surface

  • POST /api/v1/integrations/quickbooks/connect — issues an Intuit OAuth2 authorize URL (PKCE), stores a signed state.
  • GET /api/v1/integrations/quickbooks/callback — Intuit redirect target; exchanges the code for tokens and persists on Customer.integrations.quickbooks.
  • POST /api/v1/integrations/quickbooks/disconnect — revokes tokens server-side and clears the sub-document.
  • POST /api/v1/integrations/quickbooks/sync/:invoiceId — on-demand push of a single invoice (re-tried automatically via queue).
  • GET /api/v1/integrations/quickbooks/status — health + last-sync report, used by the service UI.
  • Background worker: reuse the existing agenda/bull-based job queue (see src/services/jobs) to debounce outbound pushes on invoice.created / invoice.updated.

Service web UI

  • Settings → Integrations → QuickBooks panel:
    • Connect / Disconnect CTA.
    • Last-sync timestamp + live status pill.
    • Per-invoice sync log with retry button.
    • Mapping UI for chart-of-accounts / item types / tax codes (required; QBO refuses pushes otherwise).
  • Error banner on the invoice detail page when a push to QuickBooks failed, with a one-click retry.

OAuth + secrets

  • Register a production Intuit app (intuit.com/app/developer).
  • Store QUICKBOOKS_CLIENT_ID, QUICKBOOKS_CLIENT_SECRET, QUICKBOOKS_ENVIRONMENT (sandbox / production) via the existing keys mechanism (config/keys.js).
  • Add redirectUri allowlist to the deploy workflow environments.
  • Token refresh worker that runs every 45 minutes (Intuit access tokens expire in 60 min; refresh tokens in 100 days).
  • Encryption key rotation story for accessTokenCipher / refreshTokenCipher.

Tests

  • Unit: token refresh, state validation, reconnect with same realmId replaces the prior token cipher.
  • Integration (fetch-mocked): happy-path invoice push, 401 triggers refresh-and-retry, 429 respects Retry-After, 500 goes to dead-letter.
  • Tenant isolation: pushing an invoice from tenant A never touches tenant B's realm.

Docs

  • Customer-facing "Connect QuickBooks" walkthrough in docs/customer/integrations.
  • Developer note on token rotation + reconciliation runbook in docs/developer/operations-runbook.

Dependencies on existing infra

  • Customer model — already multi-tenant; the integrations sub-document extends cleanly.
  • Invoice pipelinesrc/controllers/invoices already emits the right hooks for "invoice finalized."
  • Job queue — the mailer + scheduled jobs path already exists; sync jobs piggyback on it.
  • Secrets handlingconfig/keys.js already gates env-sourced secrets; only new keys are needed.
  • Audit logAuditLog model (already landed) is the right home for connect/disconnect events.

Estimated timeline and risk

  • Effort: 2–3 weeks for one engineer, assuming Intuit app review does not stall.
  • Risk: Medium. QuickBooks' chart-of-accounts mapping UI is the largest unknown — most integration support tickets in the industry are mapping mismatches, not auth. Budget 30% of the timeline on that UI alone.

Open questions

  • One-way (us → QBO) only for v1, or two-way (pull payments back into AL)?
  • Do we support QuickBooks Desktop? (Requires Intuit Web Connector, a completely different integration path.)
  • How do we handle customers who invoice via AttuneLogic and ALSO directly in QBO — do we de-duplicate by invoice number, customer name, or skip?

Cross-references