QuickBooks Sync (v1)
| Field | Value |
|---|---|
| Status | Deferred. Not included in any current tier. |
| Owner | Unassigned |
| Target repos | attunelogic-api, attunelogic-service |
| Supersedes | — |
| Related | Monetization & Tier Packaging (v1), Feature Roadmap Index |
| Last updated | 2026-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.jsexists 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: booleanrealmId: string(Intuit company ID)accessTokenCipher: string(encrypted at rest; use the existing mongoosecryptohelpers)refreshTokenCipher: stringaccessTokenExpiresAt: DaterefreshTokenExpiresAt: DatelastSyncedAt: Datescopes: string[]connectedByUserId: ObjectId
- New
QuickBooksSyncLogmodel scoped byparentCompanyrecording 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 signedstate.GET /api/v1/integrations/quickbooks/callback— Intuit redirect target; exchanges the code for tokens and persists onCustomer.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 oninvoice.created/invoice.updated.
Service web UI
Settings → Integrations → QuickBookspanel:- 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
redirectUriallowlist 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
integrationssub-document extends cleanly. - Invoice pipeline —
src/controllers/invoicesalready emits the right hooks for "invoice finalized." - Job queue — the mailer + scheduled jobs path already exists; sync jobs piggyback on it.
- Secrets handling —
config/keys.jsalready gates env-sourced secrets; only new keys are needed. - Audit log —
AuditLogmodel (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
- Stub file to replace:
attunelogic-api/src/services/quickbooks.js - Existing invoice flow:
attunelogic-api/src/controllers/invoices - Billing proposal: Monetization & Tier Packaging (v1)
- Roadmap index: Feature Roadmap Index