diff --git a/.planning/quick/260712-ash-add-pax8-to-the-admin-sync-overview-page/260712-ash-PLAN.md b/.planning/quick/260712-ash-add-pax8-to-the-admin-sync-overview-page/260712-ash-PLAN.md
new file mode 100644
index 0000000..c8e1998
--- /dev/null
+++ b/.planning/quick/260712-ash-add-pax8-to-the-admin-sync-overview-page/260712-ash-PLAN.md
@@ -0,0 +1,159 @@
+---
+phase: quick-260712-ash
+plan: 01
+type: execute
+wave: 1
+depends_on: []
+files_modified:
+ - app/admin/sync/pax8/page.tsx
+ - app/admin/sync/page.tsx
+ - public/logos/pax8.ico
+autonomous: true
+requirements: [QUICK-PAX8-SYNC-UI]
+
+must_haves:
+ truths:
+ - "A PAX8 integration card appears on /admin/sync alongside the other integrations"
+ - "The PAX8 card shows last sync time plus company and subscription counts"
+ - "Clicking the PAX8 card navigates to /admin/sync/pax8"
+ - "The PAX8 detail page shows sync status, counts, and sync history"
+ - "The detail page 'Sync Now' button POSTs to /api/pax8/sync and shows loading + toast feedback"
+ artifacts:
+ - path: "app/admin/sync/pax8/page.tsx"
+ provides: "PAX8 sync detail page with Sync Now button"
+ min_lines: 100
+ - path: "app/admin/sync/page.tsx"
+ provides: "PAX8 entry in INTEGRATIONS array + summary/stats/icon logic + fetch wiring"
+ contains: "id: 'pax8'"
+ - path: "public/logos/pax8.ico"
+ provides: "PAX8 card logo asset"
+ key_links:
+ - from: "app/admin/sync/page.tsx"
+ to: "/api/pax8/sync"
+ via: "fetch in fetchAll"
+ pattern: "fetch\\('/api/pax8/sync'\\)"
+ - from: "app/admin/sync/pax8/page.tsx"
+ to: "/api/pax8/sync"
+ via: "GET for status + POST for Sync Now"
+ pattern: "/api/pax8/sync"
+---
+
+
+Add PAX8 to the admin sync overview page (`/admin/sync`) as an integration card, and give it a dedicated detail page at `/admin/sync/pax8` — matching the existing duo/sentinelone pattern. The card shows last sync time plus company/subscription counts; the detail page shows sync status, counts, history, and a manual "Sync Now" button that POSTs to the existing `/api/pax8/sync` route.
+
+Purpose: PAX8 already syncs on a daily schedule (`pax8-daily`) and exposes `/api/pax8/sync`, but has no operator-facing surface in the sync admin area. This closes that gap so a manager can see PAX8 sync state and trigger a manual sync from the same place as every other integration.
+
+Output: A new detail page, an updated overview page, and a PAX8 logo asset.
+
+
+
+@$HOME/.claude/get-shit-done/workflows/execute-plan.md
+
+
+
+@./CLAUDE.md
+
+
+
+
+
+GET /api/pax8/sync returns:
+```
+{
+ inProgress: boolean,
+ counts: { companies: number, subscriptions: number, products: number },
+ history: Array<{
+ id: string,
+ sync_type: string,
+ status: string, // 'completed' | 'running' | 'failed' (and possibly others)
+ started_at: string,
+ completed_at: string | null,
+ records_added: number,
+ records_updated: number,
+ records_deleted: number,
+ error_message: string | null,
+ triggered_by: string
+ }>
+}
+```
+NOTE: unlike the SentinelOne route, this history has NO `duration_ms` and NO
+`total_upserted` / `entity_results`. Derive total records as
+`records_added + records_updated + records_deleted`, and compute duration as
+`new Date(completed_at) - new Date(started_at)` when both are present.
+
+POST /api/pax8/sync — fire-and-forget. Body `{ triggeredBy: 'manual' }`.
+Response codes to handle:
+ - 200 `{ ok: true, message }` — sync started
+ - 403 `{ error, message }` — PAX8 disabled via /admin/integrations
+ - 409 `{ error }` — sync already in progress
+
+
+
+@app/admin/sync/sentinelone/page.tsx
+@app/admin/sync/duo/page.tsx
+@app/admin/sync/page.tsx
+
+
+
+
+
+ Task 1: Create the PAX8 sync detail page
+ app/admin/sync/pax8/page.tsx
+
+Create a `'use client'` detail page mirroring `app/admin/sync/sentinelone/page.tsx`'s structure (back link + logo/title header, stat cards, last-sync card, history list, Sync Now button), adapted to the PAX8 `/api/pax8/sync` shape from the interfaces block.
+
+Specifics:
+- Fetch status from `GET /api/pax8/sync` in a `fetchData` callback; poll every 10s via `setInterval` (match sentinelone). Use `useUserTimezone()` from `@/lib/hooks/use-user-timezone` for date formatting.
+- Header: back link to `/admin/sync`, `
`, title "PAX8 Sync", subtitle describing "Companies, subscriptions, and products synced to pax8_* tables".
+- Stat cards (shadcn Card): Companies, Subscriptions, Products — read from `data.counts`. Use `lucide-react` icons (e.g. Building2, Package, Boxes) and `Number(value ?? 0).toLocaleString()`.
+- Last-sync card: read `data.history[0]`. Show status icon (completed → CheckCircle2 green, running → RefreshCw spinning blue, else XCircle red), the formatted `completed_at || started_at`, computed duration, and total records = `records_added + records_updated + records_deleted`. Render `error_message` in a red box when present.
+- History list: iterate `data.history`, each row showing status icon, `started_at` (formatted), total records, computed duration, and a Badge with `triggered_by`. Show an empty-state line when history is empty and not loading.
+- Sync Now button: `triggerSync` sets `syncing`, calls `POST /api/pax8/sync` with header `Content-Type: application/json` and body `{ triggeredBy: 'manual' }`. On non-ok response, read the JSON and `toast.error(json.message || json.error)` (import `toast` from `sonner`) — this covers the 403 disabled and 409 in-progress cases. On ok, `toast.success('PAX8 sync started')` and poll `GET /api/pax8/sync` every 5s (like duo's poll) until `!inProgress`, then clear `syncing` and `fetchData()`. Disable the button while `syncing` or `data.inProgress`.
+Do NOT use any state library — plain `useState`/`useEffect`/`useCallback` + `fetch`, matching the sibling pages.
+
+
+ test -f /opt/stacks/pulse/app/admin/sync/pax8/page.tsx && grep -q "'/api/pax8/sync'" /opt/stacks/pulse/app/admin/sync/pax8/page.tsx && grep -q "triggeredBy" /opt/stacks/pulse/app/admin/sync/pax8/page.tsx && npx --prefix /opt/stacks/pulse tsc --noEmit --pretty -p /opt/stacks/pulse/tsconfig.json
+
+ Detail page exists, fetches GET /api/pax8/sync, has a Sync Now button POSTing with triggeredBy, handles 403/409 via sonner toast, and type-checks clean.
+
+
+
+ Task 2: Add the PAX8 card + logo to the sync overview page
+ app/admin/sync/page.tsx, public/logos/pax8.ico
+
+Add a PAX8 logo asset and wire a PAX8 card into `app/admin/sync/page.tsx`, matching the existing per-integration pattern (INTEGRATIONS entry + fetch in `fetchAll` + `getSummary` branch + `getStatusIcon` branch + a stats block in the card render).
+
+Logo: create `public/logos/pax8.ico`. Fetch the PAX8 favicon (`curl -fsSL https://www.pax8.com/favicon.ico -o /opt/stacks/pulse/public/logos/pax8.ico`). If the fetch fails or returns empty, copy an existing logo as a placeholder (`cp /opt/stacks/pulse/public/logos/itglue.ico /opt/stacks/pulse/public/logos/pax8.ico`) so the card never shows a broken image — note in the commit that a real PAX8 logo should replace the placeholder.
+
+Overview page edits (`app/admin/sync/page.tsx`):
+- Append to `INTEGRATIONS`: `{ id: 'pax8', category: 'Licensing', product: 'PAX8', description: 'Companies, subscriptions, products, and license billing', href: '/admin/sync/pax8', logo: '/logos/pax8.ico', color: 'blue' }`.
+- Add state `const [pax8Data, setPax8Data] = useState(null);` and add `fetch('/api/pax8/sync')` to the `Promise.all` in `fetchAll`, wiring its result via `if (pax8Res.ok) setPax8Data(await pax8Res.json());`.
+- Add a `pax8` branch to `getSummary`: return `{ lastSync: pax8Data?.history?.[0]?.completed_at ?? null, status: pax8Data?.history?.[0]?.status ?? null, companies: Number(pax8Data?.counts?.companies ?? 0), subscriptions: Number(pax8Data?.counts?.subscriptions ?? 0) }` (guard on `pax8Data` returning null when absent).
+- Add a `pax8` branch to `getStatusIcon`: no `lastSync` → Clock muted; `status === 'failed'` → XCircle red; else CheckCircle2 green.
+- Add a `{intg.id === 'pax8' && summary && (...)}` stats block in the card body showing "Last sync" (via `fmtDate`), "Companies", and "Subscriptions" rows — matching the existing itglue/duo stat-row markup.
+Match existing formatting/indentation. Do not alter other integrations' logic.
+
+
+ test -s /opt/stacks/pulse/public/logos/pax8.ico && grep -q "id: 'pax8'" /opt/stacks/pulse/app/admin/sync/page.tsx && grep -q "fetch('/api/pax8/sync')" /opt/stacks/pulse/app/admin/sync/page.tsx && grep -q "setPax8Data" /opt/stacks/pulse/app/admin/sync/page.tsx && npx --prefix /opt/stacks/pulse tsc --noEmit --pretty -p /opt/stacks/pulse/tsconfig.json
+
+ PAX8 card renders on /admin/sync with last sync + company/subscription counts, links to /admin/sync/pax8, a non-empty pax8.ico exists, and the page type-checks clean.
+
+
+
+
+
+- `npx tsc --noEmit --pretty` passes (no new type errors).
+- Manual smoke (developer): visit `/admin/sync` — PAX8 card is present with counts and last-sync; click it → lands on `/admin/sync/pax8`; click "Sync Now" → button shows a spinner, a sonner toast appears, and (if PAX8 is disabled in /admin/integrations) an error toast surfaces instead.
+
+
+
+- PAX8 appears as an integration card on `/admin/sync` showing last sync time + company/subscription counts.
+- The card links to a working `/admin/sync/pax8` detail page.
+- The detail page shows counts, last-sync status, history, and a functional "Sync Now" button POSTing to `/api/pax8/sync` with loading + toast feedback.
+- No new dependencies or state libraries introduced; conventions (`'use client'`, shadcn, lucide, sonner, plain fetch) followed.
+- Type check clean.
+
+
+