Skip to main content

Clients

What this is​

This is the platform-wide client list. Use it to see each client’s status, connected accounts, assigned account managers, and recent sync warnings. It is also the starting point for opening a client’s management screen.

Common questions​

Why can’t I find an archived client?​

The list opens on Active clients. Change the status filter to Archived or All to include archived records.

What does Inactive mean?​

Inactive is the label shown for a client whose stored status is paused.

What does a sync warning mean?​

A warning means one or more connected accounts have a sync problem. The badge distinguishes permission or token problems from other sync errors; open Sync Health for the deeper connector view.

Can I start a sync from this list?​

Yes. Use the client’s sync action to start a sync and follow its progress. If no matching account can be synced, the action reports that no syncable accounts were found.

Where do I manage a client’s brands or ad accounts?​

Open the client from its row. Brands and ad accounts are managed in that client’s detail screen.

What do the summary cards at the top show?​

Total clients, active clients, connected accounts, and account managers, counted from the same list below them.

How to use it​

  1. Choose Active, Inactive, Archived, or All to narrow the list.
  2. Review the summary cards and each client’s accounts, managers, and status.
  3. Open a client row to manage its details, brands, ad accounts, users, fees, or metrics.
  4. Use the sync action only when you need to start a live sync, then watch the progress message.

Screenshot​

Admin Clients

For developers — routes, guards, and internals
  • Route: /admin/clients
  • Guard: admin-tier (isAdminTier: super_admin / admin / account_manager) via src/app/admin/layout.tsx; the backing /api/admin/* routes enforce their own guards server-side.

Cross-tenant client registry and the entry point to per-client management. Live capture: 35 total clients (7 active, 28 archived), 57 connected accounts, 6 account managers.

Features

  • Client table. Columns Client / Accounts / Managers / Status / Created / Actions, fed by GET /api/admin/clients through the useAdminApi SWR-lite hook (primitives.tsx); each row enriches the base client with its ad_accounts and assigned_managers.
  • Normalized platform naming. Connected-account chips render canonical platform names (Meta Ads, Google Ads, TikTok Ads, Shopify, Hyros, ...) via formatClientName / display helpers rather than raw connector slugs.
  • Status filter. Chips for Active (default) / Inactive / Archived / All; "Inactive" is the display label for the paused status (src/lib/client-status.ts).
  • Summary StatCards. Four KPI cards above the table: Total Clients, Active Clients, Connected Accounts, Account Managers — computed from the same payload as the table.
  • Create-new-client form. Inline form POSTs to /api/admin/clients (via jsonFetch) and confirms with an in-app toast; the list refreshes in place.
  • Per-row drill-in. Each row links to /admin/clients/[slug], the per-client management hub (see its own page doc) — including the Brands and Ad accounts tabs that replaced the retired standalone /admin/brands and /admin/ad-accounts routes (both now redirect here).
  • Sync-now with SyncProgressModal. The per-row sync action opens SyncProgressModal, which fires POST /api/admin/sync-now?client=<slug> and streams progress; a 409 means the filter matched no syncable accounts. This is a real mutation — it was deliberately not exercised during the read-only capture run.
  • Sync-error classification badges. Each account's config.last_sync_error is run through classifySyncError (src/lib/marketing/sync-activity.ts) and rolled up into per-row badges (e.g. permission/token errors vs. other sync errors), with a warning marker on rows where any connected account is failing. A missing token is classified distinctly from other errors so re-auth work is visible at a glance (the deeper per-connector view is /admin/sync-health).
  • Status labels. Display labels come from clientStatusLabel (src/lib/client-status.ts): the stored paused status renders as "Inactive"; archived clients are hidden by the default Active filter, which is why the capture shows 7 of 35 rows by default.

Key files

KindPath
Pagesrc/app/admin/clients/page.tsx
List componentsrc/components/admin/ClientsList.tsx
Shared primitivessrc/components/admin/primitives.tsx (useAdminApi, StatCard, jsonFetch)
Sync modalsrc/components/admin/SyncProgressModal.tsx
Error classifiersrc/lib/marketing/sync-activity.ts (classifySyncError)
API/api/admin/clients (GET list, POST create), /api/admin/clients/[id], /api/admin/sync-now