- requirePermission('admin','access')-gated (D-08) — the write side of the
asymmetric read/write auth split
- zod-validated body (companyId positive int, note <=500 chars)
- Delegates the two-table write to resolvePax8CompanyMatch inside
postgresClient.transaction(); maps ResolveResult codes to HTTP status
(ok->200, not_found->404, already_resolved->409, company_not_found->400)
- GET /api/pax8/companies with requireAuth gate (D-07)
- Whitelisted sort columns, parameterized search/limit/offset
- Joins pax8_companies to companies for matched name + active subscription count
- requireAuth-gated (D-07) list of unresolved pax8_company_match_review rows
- Bulk-fetches candidate Autotask company names in one ANY($1::bigint[]) query
- Returns items with pax8 company + zipped candidates (id/name/confidence)
- Return 403 when integration_settings.key='pax8' has disabled=true
- Check runs as the first statement, before isSyncInProgress()
- GET handler unchanged; no new imports (postgresClient already imported)
- 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