docs(quick-260718-9qg): complete QBO integration handoff quick task

Records the plan, execution summary, and STATE.md quick-task log entry
for the QBO_INTEGRATION_HANDOFF.md doc committed in ea8a36b.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W6RuWdiUiXrPK6FLBHjtpY
This commit is contained in:
lorentz 2026-07-18 07:08:29 -04:00
parent ea8a36b394
commit a65b29055c
3 changed files with 203 additions and 1 deletions

View file

@ -28,7 +28,7 @@ See: .planning/PROJECT.md (updated 2026-07-14)
Phase: Milestone v3.0 complete
Plan: —
Status: Awaiting next milestone
Last activity: 2026-07-18 — Completed quick task 260718-7v8: Mimecast blast-radius held-message false-positive fix
Last activity: 2026-07-18 — Completed quick task 260718-9qg: QBO integration handoff document
## Performance Metrics
@ -129,6 +129,7 @@ None yet.
| 260717-a19 | Fix phishing simulation-vendor allowlist gaps (3 missing KnowBe4 domains), auto-parse timing race (retry on ticket.update), and parseAndStoreMessage idempotency; reclassified 6 stale Seubert campaigns (all flipped UNWANTED → USER_AWARENESS) | 2026-07-17 | cf04f07 | [260717-a19-fix-phishing-simulation-vendor-allowlist](./quick/260717-a19-fix-phishing-simulation-vendor-allowlist/) |
| 260717-v6c | Add "Mark as accidental report" action to the phishing Action Area — closes out a campaign and posts a fixed customer-facing note to the reporter (distinct from the silent "Mark as false positive" action) | 2026-07-18 | 565a0c1 | [260717-v6c-add-a-mark-as-accidental-report-action-t](./quick/260717-v6c-add-a-mark-as-accidental-report-action-t/) |
| 260718-7v8 | Fix Mimecast blast-radius false positives — date-scope `getHeldMessages()` and add a sender-domain relevance guard so unrelated held mail in a recipient's queue no longer inflates held/matched counts or overwrites a genuinely delivered recipient's status | 2026-07-18 | b7d6be4 | [260718-7v8-fix-mimecast-blast-radius-held-message-f](./quick/260718-7v8-fix-mimecast-blast-radius-held-message-f/) |
| 260718-9qg | Add self-contained `QBO_INTEGRATION_HANDOFF.md` documenting Pulse's QuickBooks Online OAuth2 flow, token storage/refresh, sandbox/production API base URLs, and gotchas (deletion-diffing, CSRF state gap, NEXTAUTH_URL legacy var) for a new app's team | 2026-07-18 | ea8a36b | [260718-9qg-create-a-quickbooks-online-integration-h](./quick/260718-9qg-create-a-quickbooks-online-integration-h/) |
## Deferred Items

View file

@ -0,0 +1,109 @@
---
phase: quick-260718-9qg
plan: 01
type: execute
wave: 1
depends_on: []
files_modified: [QBO_INTEGRATION_HANDOFF.md]
autonomous: true
requirements: [DOC-QBO-HANDOFF]
must_haves:
truths:
- "A reader with NO access to the Pulse codebase can understand Pulse's QBO integration end to end from this one file"
- "The full OAuth2 authorization-code flow is documented step by step (authorize → callback → token exchange → storage → refresh)"
- "Env vars, API base URLs (sandbox vs production), scopes, and minor version are stated explicitly"
- "Known gotchas — including anything learned from the recent QBO AR diagnostics work — are captured"
artifacts:
- path: "QBO_INTEGRATION_HANDOFF.md"
provides: "Self-contained QBO integration handoff document for a new app"
min_lines: 120
key_links: []
---
<objective>
Produce a single standalone markdown document, `QBO_INTEGRATION_HANDOFF.md` at the repo root, that captures everything Pulse knows about connecting to QuickBooks Online (QBO), so a separate new application's team — who will NOT have access to the Pulse codebase — can implement their own QBO integration with a head start.
Purpose: Transfer hard-won integration knowledge (OAuth2 flow, token storage/refresh, API base URLs, scopes, gotchas) to a team building a brand-new app.
Output: `QBO_INTEGRATION_HANDOFF.md` (new file, repo root).
**Docs-only task.** No application source code may be modified. The only file written is the handoff document.
</objective>
<execution_context>
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
@$HOME/.claude/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/STATE.md
Known QBO surface in this repo (starting points for the executor's investigation — the executor MUST read these, not assume their contents):
- `lib/services/qbo-client.ts` — QBO API client (expect factory pattern per repo convention: `getQboClient()` / `isQboConfigured()`)
- `lib/services/qbo-sync-service.ts` — sync service (invoices, AR, etc.)
- `app/api/qbo/auth/route.ts` — OAuth2 connect/authorize + callback handling
- `app/api/qbo/sync/route.ts` — sync trigger endpoint
- `app/api/qbo/disconnect/route.ts` — token teardown
- `app/api/qbo/diagnose-ar/route.ts` — AR (accounts receivable) diagnostics (likely source of recently-learned gotchas)
- `migrations/051_create_qbo_tables.sql` — QBO tables (token storage, invoices, etc.)
- `migrations/088_qbo_invoices_soft_delete.sql` — soft-delete addition
- No existing QBO guide under `docs/` — this handoff is net-new.
- Note: `.env` did NOT surface `QBO_*` / `QUICKBOOKS_*` / `INTUIT_*` vars by name — the executor MUST grep the source for `process.env.` inside the QBO files to discover the ACTUAL env var names in use.
</context>
<tasks>
<task type="auto">
<name>Task 1: Investigate Pulse's QBO integration and write the self-contained handoff doc</name>
<files>QBO_INTEGRATION_HANDOFF.md</files>
<action>
First, INVESTIGATE the existing QBO integration in this repo. Do not write anything until the investigation is complete. Read (not skim) each of the following and extract the concrete details:
1. Env vars — grep the QBO files for `process.env.` to find the REAL variable names, then record each: client id, client secret, environment (sandbox vs production toggle), redirect/callback URI, realm/company id, and any discovery or token endpoint overrides. Command hint: `grep -rn "process\.env\." lib/services/qbo-client.ts lib/services/qbo-sync-service.ts app/api/qbo/`.
2. OAuth2 authorization-code flow — from `app/api/qbo/auth/route.ts` and `lib/services/qbo-client.ts`, capture: the authorize URL (Intuit `appcenter.intuit.com/connect/oauth2`), the requested scopes (e.g. `com.intuit.quickbooks.accounting`), the `state` handling / CSRF protection, the redirect URI, the token-exchange URL (`oauth.platform.intuit.com/oauth2/v1/tokens/bearer`), the token-refresh flow, and how the `realmId` (company id) is received on the callback.
3. Token storage — from `migrations/051_create_qbo_tables.sql` (and `088`), document the exact table(s) and columns used to persist access token, refresh token, realm/company id, and expiry timestamps; and from the client code, how expiry is detected and refresh is triggered (proactive vs on-401).
4. API base URLs & versioning — record the sandbox base (`sandbox-quickbooks.api.intuit.com`) vs production base (`quickbooks.api.intuit.com`), the `minorversion` query param if used, and the request path shape (`/v3/company/{realmId}/...`).
5. What QBO is used for in Pulse — read `lib/services/qbo-sync-service.ts` and the api routes to summarize the entities synced (invoices, AR, payments/deposits) and the direction of data flow.
6. Gotchas / lessons learned — read `app/api/qbo/diagnose-ar/route.ts` and run `git log --oneline -20 -- app/api/qbo lib/services/qbo-client.ts lib/services/qbo-sync-service.ts migrations/051_create_qbo_tables.sql migrations/088_qbo_invoices_soft_delete.sql` (and inspect the "QBO AR diagnostics" work referenced in the recent `chore: check in pending work` commit) to fold in any real gotchas: rate limits, token-refresh failures, sandbox-vs-production quirks, AR/invoice reconciliation issues, soft-delete handling, realm mismatch, etc. Also check the completed quick-task dirs under `.planning/quick/` matching `*qbo*` (e.g. `260519-0oz-add-qbo-createpayment-createdeposit`, `260521-fci-stopgap-nightly-reconciliation`) for SUMMARY notes worth distilling.
Then WRITE `QBO_INTEGRATION_HANDOFF.md` at the repo root as a self-contained document. It MUST be readable by someone with zero access to this codebase: inline every relevant detail; do NOT leave bare "see lib/services/qbo-client.ts" pointers — if a detail lives in a Pulse file, summarize the detail itself. Pulse file paths may appear only as parenthetical "(in Pulse this lives in …)" provenance notes, never as the sole carrier of information.
Structure the document roughly as:
- **Overview** — what QBO integration does and why (the business purpose).
- **Prerequisites** — creating an Intuit Developer app, sandbox vs production, redirect URI registration, required scopes.
- **Environment variables** — a table of every env var (real names discovered from source) with description and example/placeholder value. Do NOT copy any real secret values from `.env`; use placeholders.
- **OAuth2 connection flow** — a numbered, step-by-step walkthrough: authorize redirect → user consent → callback with `code` + `realmId` → token exchange → persistence → subsequent refresh. Include the exact Intuit URLs.
- **Token storage & refresh** — the table schema (columns + purpose) and the refresh strategy, generalized so the new app can adapt it (they may not use Postgres).
- **API usage** — base URLs (sandbox/production), path shape, minor version, an example authenticated request.
- **Gotchas & lessons learned** — the concrete pitfalls gathered in the investigation.
- **Minimal code-flow example** — a distilled, framework-agnostic pseudocode/TypeScript sketch of authorize → exchange → refresh → call (NOT a copy-paste of Pulse code; enough that a QBO-unfamiliar team can implement their own).
Write directive prose and generalized examples. Do NOT modify any application source, migrations, env files, or existing docs.
</action>
<verify>
<automated>test -f QBO_INTEGRATION_HANDOFF.md && test $(grep -v '^#' QBO_INTEGRATION_HANDOFF.md | wc -l) -ge 100 && grep -qi 'oauth' QBO_INTEGRATION_HANDOFF.md && grep -qi 'refresh' QBO_INTEGRATION_HANDOFF.md && grep -qiE 'sandbox|production' QBO_INTEGRATION_HANDOFF.md && echo OK</automated>
</verify>
<done>
`QBO_INTEGRATION_HANDOFF.md` exists at repo root, is self-contained (no information-bearing bare file-path pointers), and covers overview, prerequisites, env vars, the full OAuth2 flow, token storage/refresh, API base URLs (sandbox + production), gotchas, and a minimal code-flow example. No application source, migration, env, or existing doc files were modified.
</done>
</task>
</tasks>
<verification>
- `git status` shows exactly one new file (`QBO_INTEGRATION_HANDOFF.md`) and no modifications to existing tracked source/docs/migrations.
- The document contains no real secret values (placeholders only).
- Every major section (overview, prerequisites, env vars, OAuth2 flow, token storage/refresh, API URLs, gotchas, code example) is present and populated with concrete detail drawn from the repo investigation.
</verification>
<success_criteria>
A new-app engineer can read `QBO_INTEGRATION_HANDOFF.md` alone and understand how to register an Intuit app, run the OAuth2 authorization-code flow, store and refresh tokens, hit the correct sandbox/production API endpoints, and avoid the pitfalls Pulse already hit — without ever opening the Pulse codebase.
</success_criteria>
<output>
Create `.planning/quick/260718-9qg-create-a-quickbooks-online-integration-h/260718-9qg-SUMMARY.md` when done.
</output>

View file

@ -0,0 +1,92 @@
---
phase: quick-260718-9qg
plan: 01
subsystem: docs
tags: [quickbooks, qbo, oauth2, integration, handoff, finance]
# Dependency graph
requires: []
provides:
- "QBO_INTEGRATION_HANDOFF.md — self-contained OAuth2/token/API/gotchas reference for QuickBooks Online, written for a team with no access to Pulse's codebase"
affects: [any future work building or maintaining a QBO integration in a different app]
# Tech tracking
tech-stack:
added: []
patterns: []
key-files:
created:
- QBO_INTEGRATION_HANDOFF.md
modified: []
key-decisions:
- "Inlined every detail (env vars, exact Intuit URLs, table schemas, request/response shapes) rather than pointing at Pulse source files, per the plan's self-containment requirement"
- "Called out two real defects found during investigation as explicit gotchas rather than silently working around them: unverified OAuth `state` (CSRF gap) and the legacy `NEXTAUTH_URL` env var used for the redirect URI instead of Pulse's actual BETTER_AUTH_URL"
- "Used placeholder values for all env var examples — no real secrets existed in .env for QBO_* keys to begin with, so nothing needed redaction"
requirements-completed: [DOC-QBO-HANDOFF]
# Metrics
duration: 25min
completed: 2026-07-18
---
# Phase quick-260718-9qg Plan 01: QBO Integration Handoff Doc Summary
**Self-contained `QBO_INTEGRATION_HANDOFF.md` covering QBO's OAuth2 authorization-code flow, token storage/refresh schema, sandbox vs production API base URLs, minor-version pinning, and 11 concrete gotchas (deletion-diffing, CSRF state, report NoReportData markers, check-number normalization, idempotency, etc.) drawn from Pulse's actual QBO source and its recent AR-reconciliation fix.**
## Performance
- **Duration:** ~25 min
- **Started:** 2026-07-18T11:00:00Z (approx.)
- **Completed:** 2026-07-18T11:25:00Z (approx.)
- **Tasks:** 1
- **Files modified:** 1 (new file)
## Accomplishments
- Investigated Pulse's real QBO integration end to end: `lib/services/qbo-client.ts`, `lib/services/qbo-sync-service.ts`, all four `app/api/qbo/*` routes, `migrations/051_create_qbo_tables.sql`, `migrations/088_qbo_invoices_soft_delete.sql`, `middleware.ts` public-route list, `integration-health.ts` config check, git history (`b98c674`, `ef9b31e`, `672f17b`), and the `260519-0oz` quick-task SUMMARY for the createPayment/createDeposit work.
- Wrote `QBO_INTEGRATION_HANDOFF.md` (596 lines) at the repo root with all required sections: Overview, Prerequisites, Environment variables (table), OAuth2 connection flow (11-step numbered walkthrough with exact Intuit URLs), Token storage & refresh (generalized schema + pseudocode), API usage (base URLs, path shape, minorversion, query/write/report examples), Gotchas & lessons learned (11 concrete items), and a framework-agnostic TypeScript pseudocode code-flow example.
- Surfaced two real defects discovered during investigation as explicit gotchas for the new team to avoid repeating: (1) Pulse's OAuth `state` value is generated but never persisted/verified on callback — a real CSRF gap; (2) the redirect-URI base URL is read from `NEXTAUTH_URL`, a variable not present anywhere in Pulse's `.env`/`.env.local` (a legacy holdover from a prior auth library), distinct from the app's actual `BETTER_AUTH_URL`.
## Task Commits
Each task was committed atomically:
1. **Task 1: Investigate Pulse's QBO integration and write the self-contained handoff doc** - `ea8a36b` (docs)
**Plan metadata:** (this commit, made after SUMMARY.md)
## Files Created/Modified
- `QBO_INTEGRATION_HANDOFF.md` - Self-contained QBO integration handoff document (overview, prerequisites, env vars, OAuth2 flow, token storage/refresh, API usage, gotchas, code-flow example)
## Decisions Made
- Inlined every Pulse-derived detail directly into the doc (no bare `see lib/services/qbo-client.ts` pointers); Pulse file paths appear only as parenthetical provenance notes.
- Documented the `NEXTAUTH_URL` / `BETTER_AUTH_URL` mismatch and the unverified OAuth `state` as explicit "don't repeat this" gotchas rather than omitting them, since the plan's objective is transferring hard-won (including negative) knowledge to a new team.
- Recommended `crypto.randomUUID()` (or a signed token) plus server-side verification in place of Pulse's current `Math.random()`-based `state`, and recommended adding an explicit Intuit token-revocation call for a real "disconnect," since a new implementation should start from a stronger baseline than Pulse's current code.
## Deviations from Plan
None - plan executed exactly as written. This was a docs-only investigation-and-write task; no application source, migrations, env files, or existing docs were touched (confirmed via `git status --short`, which shows only `QBO_INTEGRATION_HANDOFF.md` as an addition).
## Issues Encountered
None.
## User Setup Required
None - no external service configuration required. This task only produced a reference document; it does not change any running behavior.
## Next Phase Readiness
- `QBO_INTEGRATION_HANDOFF.md` is ready to be handed directly to the new application's engineering team.
- No blockers. If the new team later reports gaps (e.g. QBO Payments/charge-card scope, multi-realm support), a follow-up quick task can extend this document — it was intentionally scoped to what Pulse's own integration actually covers (accounting scope only, single realm).
## Self-Check: PASSED
- `QBO_INTEGRATION_HANDOFF.md` exists at repo root — FOUND
- Automated verify command (`test -f ... && line count >= 100 && grep oauth/refresh/sandbox|production`) — PASSED (`OK`)
- Commit `ea8a36b` exists in git log — FOUND
- `git status --short` shows only `QBO_INTEGRATION_HANDOFF.md` as a tracked addition (plus the pre-existing untracked `.planning/quick/260718-9qg-.../` plan directory, unrelated to source/docs/migrations) — CONFIRMED
---
*Phase: quick-260718-9qg*
*Completed: 2026-07-18*