- Replace stale unused Pax8Order/Pax8OrderItem stubs with live-verified
Pax8Invoice (header) and Pax8InvoiceItem (per-company line item) types
- Field shapes sourced from 12-RESEARCH.md live PAX8 API verification
- Enable pg_trgm extension for fuzzy company-name matching
- Add per-company id, billing period, and dual-cost columns to pax8_order_items
- Add auto-match columns (autotask_company_id, match_confidence, match_method, matched_at) to pax8_companies
- Applied to dev DB and verified idempotent
Confirmed via direct service call (session-gated route bypassed for
verification): 118 companies, 445 subscriptions with dual cost, 46
referenced-only products with readable name+category, 0 unreferenced
products, stable soft-delete on re-run, zero PAX8 write calls.
- POST returns 409 if a sync is already in progress, otherwise starts
Pax8SyncService.fullSync() without awaiting and returns immediately
- GET reports inProgress, non-deleted row counts across the three PAX8
tables, and the last 10 sync_history rows for entity_type='pax8'
- Route stays behind the session-cookie check (not added to
middleware.ts's public allowlist) — matches itglue/veeam sync routes
- fullSync() orchestrates companies -> subscriptions -> referenced-only
products, each with independent try/catch returning Pax8EntitySyncResult
- Referenced product catalog resolved in a single pass: listAllProducts()
fetched once, filtered in-memory to subscription-referenced ids (D-01/D-02)
- Each entity's UUID-array tombstone soft-deletes rows PAX8 no longer
returns (is_deleted=true, deleted_at set), skipped when zero ids seen
- sync_history row (entity_type='pax8', sync_type='full') tracks
started/completed/failed with base columns only
- No PAX8 writes: only Pax8Client's read methods are called
- listAllCompanies/listAllSubscriptions/listAllProducts multi-page concat
- size=200 param assertion, single-page no-infinite-loop case
- Assert every request is GET with Authorization: Bearer header
- ALTER TABLE pax8_subscriptions to add price/partner_cost/currency
- Additive, idempotent (IF NOT EXISTS), matches currency convention from 091
- Applied to dev DB via docker exec (Postgres init won't re-run on existing volume)
Pattern mapper output for Phase 11 was missing from the initial plan
commit. Decision coverage gate also flagged D-02/D-07/D-08 as
implemented-but-uncited in 11-02-PLAN.md's must_haves.truths block —
added explicit citations so the translation gate passes cleanly.
- scripts/verify-pax8-auth.ts loads .env.local, calls getPax8Client().listCompanies(0,1), logs only counts/status (never token/secret)
- CLAUDE.md External integrations table gains a PAX8 row (PAX8_* prefix)
- INTEGRATIONS.md does not exist at repo root; edit skipped per plan instructions (noted in SUMMARY)
- isPax8Configured(): both PAX8_CLIENT_ID and PAX8_CLIENT_SECRET required
- getPax8Client(): throws exact error naming both env vars when missing;
caches singleton Pax8Client instance
- _resetPax8Client(): test seam to clear the cached singleton
- follows appgate-factory.ts / 10-RESEARCH.md Pattern 2 verbatim