Skip to Content
Apps & PortalsManageScreens — Manage App

Screens — Manage App

Audience: Super admins — platform operations, tenant management, global configuration Platform: Web only (Next.js) Auth: super_admin role required

Status notation used throughout this doc:

  • [Live] — built and working in manage-dev.leadmetrics.ai (verified 2026-03-31)
  • [To Build] — specified but not yet implemented; part of the agent architecture layer

Collapsible — toggle button (< / > chevron) collapses to icon-only mode (w-16); expanded width is w-56. State is client-side only (resets on reload). Implemented in apps/manage/src/components/manage-sidebar.tsx — a "use client" component; NAV array lives there, not in the layout.

Dashboards ▾ ← topmost group (Apr 2026) Platform Overview → /dashboards/overview [Live] Execution Queue → /dashboards/execution-queue [Live] Finance → /dashboards/finance [Live] AI Performance → /dashboards/ai [Live Apr 2026] Tenant Health → /dashboards/tenant-health [Live Apr 2026] Content Pipeline → /dashboards/content-pipeline [Live Apr 2026] Channel Health → /dashboards/channel-health [Live Apr 2026] Goal & Strategy → /dashboards/goal-strategy [Live Apr 2026] Tenants → /tenants [Live] Users → /users [Live] Agents → /agents [Live] (tabs: All Agents | Analytics) Agent Chat → /chat [Live] Skills → /skills [Live] Content ▾ Blog Posts → /blog-posts [Live] Social Posts → /social-posts [Live] Templates ▾ Email → /templates/email [Live] Telegram → /templates/telegram [Live] Billing ▾ Invoices → /invoices [Live] Plans → /plans [Live] ← full CRUD incl. create/edit/delete Offerings → /plans/offerings [Live] ← create/edit/delete offerings Regions → /plans/regions [Live] ← create/edit/delete regions Credits & Usage → /usage [Live] LLM Costs → /costs [Live] Transactions → /transactions [Live] Backlink Directories → /directories [Live] Push Notifications → /push-notifications [Live] Audit Logs → /audit-logs [Live] System ▾ RAG & AI Config → /system/rag-config [To Build] Autonomy → /system/deliverable-settings [Live] ← per-content-type DM review toggles + activity volume cap Important Days → /system/important-days [Live] Help Center → /help [Live]

Top bar [Live]: theme toggle (icon cycles light → dark → system) · notification bell (violet unread badge, full dropdown panel) · profile avatar dropdown (initials, name, email, “Super Admin” badge, Profile Settings link, Sign out). All in apps/manage/src/components/topbar.tsx.

Notification Dropdown [Live]

  • Fetches from GET /api/admin/notifications on mount (once per page load); no Socket.IO real-time (manage namespace does not broadcast per-tenant notification events)
  • Shows last 50 notifications across all tenants (super admin cross-tenant view)
  • “Mark all read” → POST /api/admin/notifications/read-all

API routes (Next.js proxy):

  • GET /api/admin/notifications → proxies to GET /admin/v1/notifications
  • POST /api/admin/notifications/read-all → proxies to POST /admin/v1/notifications/read-all

Backend routes (/admin/v1/, requireSuperAdmin):

  • GET /admin/v1/notifications — last 50 notifications across all tenants, newest first
  • POST /admin/v1/notifications/read-all — marks all unread notifications as read platform-wide

Screen M1a — Platform Overview (/dashboards/overview) [Live]

Purpose: Real-time pulse of the entire platform — agent activity, LLM spend, queue health. Moved from /overview to /dashboards/overview (Apr 2026); old URL redirects.

┌─────────────────────────────────────────────────────────────┐ │ Platform Overview Super Admin │ ├──────────┬──────────┬──────────┬───────────────────────────┤ │ Tenants │ Active │ Total │ System Health │ │ Total │ Agents │ LLM Spend│ ✅ All services OK │ │ 24 │ 47 live │ $1,204 │ Queue: 12 jobs pending │ ├──────────┴──────────┴──────────┴───────────────────────────┤ │ Tenant Activity (last 24h) Spend by Tenant │ │ ───────────────────────── ───────────────────── │ │ Acme Corp 14 activities Acme ████ $412 │ │ Globex 9 activities Globex ██ $204 │ │ Initech 3 activities Initech █ $88 │ └─────────────────────────────────────────────────────────────┘

Live data: Agent counts and queue depths via SSE. Spend totals via polling.

Clicking a tenant row navigates to Tenant Detail (M3).


Screen M1b — Execution Queue (/dashboards/execution-queue) [Live]

Purpose: Cross-tenant, cross-agent log of every agent run with full debug details — input prompt, output, execution transcript, token usage, cost.

List page (/dashboards/execution-queue)

Header: “Execution Queue” heading + total run count badge.

Filter bar:

  • Agent role dropdown (all roles)
  • Tenant selector
  • Status filter (pending / generating / done / failed)
  • Date range picker

Table columns: Agent · Tenant · Status · Model · Tokens (in/out) · Cost · Duration · Started At

UX: IntersectionObserver infinite scroll (no pagination buttons). Row click navigates to detail page.

API: GET /admin/v1/runs with cursor + filters → proxy at apps/manage/src/app/api/runs-proxy/route.ts

Detail page (/dashboards/execution-queue/[runId])

Sections (top to bottom):

  1. Header — agent name, run ID, status badge, back button
  2. Stats bar — model, duration, cost, token counts (input / output)
  3. Input Prompt — full text of the prompt sent to Claude (from AgentRun.inputPrompt)
  4. Output — Preview (rendered markdown) / Source (raw) tabs
  5. Execution LogTranscriptViewer component; shows every turn of the Claude Code session
  6. Run Metadata — skills array, tenant ID, timestamps

Transcript entries (TranscriptViewer in [runId]/TranscriptViewer.tsx):

  • assistant — violet, expanded by default
  • tool_use — amber, collapsed; header shows tool name
  • tool_result — emerald, collapsed
  • user — blue, collapsed
  • system — gray, hidden by default (toggle to show)
  • error — red, expanded by default

API: GET /admin/v1/runs/:runId → proxy at apps/manage/src/app/api/runs-proxy/[runId]/route.ts


Screen M1c — Finance (/dashboards/finance) [Live]

Purpose: CFO-level financial health view — revenue, collections, costs, and outstanding debt in one page. All data computed live via direct DB queries (no API hop). For trend charts and gross margin, see Option B in docs/reports/cfo.md.

File: apps/manage/src/app/(manage)/dashboards/finance/page.tsx (async server component, force-dynamic)

Sections

Revenue KPIs (7 stat cards in a responsive grid):

  • Monthly Recurring Revenue — sum of active subscription plan prices, normalised to monthly (annual ÷ 12), grouped by currency
  • Annual Run Rate (ARR) — MRR × 12
  • Collected This Month — sum of grossAmount for invoices with paidAt in the current UTC calendar month
  • Collection Rate — collected ÷ total issued this month; color-coded green ≥90%, amber ≥70%, red <70%
  • Outstanding AR — sum of netPayable for all pending + overdue invoices, grouped by currency
  • Overdue Invoices — count of overdue invoices across all time
  • LLM Cost (30 days) — sum of AgentRun.costUsd (USD float, not smallest unit)

Revenue by Plan Tier: Table — Plan (Starter/Professional/Agency/Enterprise) · Tenants · Monthly Revenue · Share of MRR · mini bar

Top 5 Tenants by Revenue + Top 8 Tenants by LLM Cost (30d): Side-by-side tables, each row links to /tenants/:id

Outstanding Invoices: Table — Tenant · Outstanding amount · Status badge (Pending amber / Overdue red) · Due date · Age in days (red when overdue). Sorted by amount descending, top 10 tenants.

Data Model Gotchas

  • Invoice grossAmount / netPayable / Plan.priceAmount — all in smallest currency unit (paise/cents), divide by 100 to display
  • AgentRun.costUsdUSD float (e.g. 0.025), do NOT divide by 100
  • Multi-currency: MRR/AR displayed as separate figures per currency, e.g. “₹4,99,000 + $200” — gross margin % intentionally omitted (requires forex rate)
  • Month start uses UTC string ${y}-${m}-01T00:00:00.000Z (not new Date(y,m,1)) to avoid timezone shift

Future: Option B

docs/reports/cfo.md specifies the PlatformFinancialSnapshot model and daily scheduler job that will unlock 12-month MRR trend charts and gross margin % once implemented.


Screen M1d — AI Performance (/dashboards/ai) [Live Apr 2026]

Purpose: Operational health of the AI layer — model usage, agent success/failure rates, cost efficiency, and token consumption. Cross-tenant, 30-day rolling window. All data from the AgentRun table; no new schema required.

File: apps/manage/src/app/(manage)/dashboards/ai/page.tsx (async server component, force-dynamic) Nav: Fourth child of Dashboards NavGroup (BrainCircuit icon)

Sections

Header KPIs (6 stat cards):

CardValueSource
Total Runs (30d)countagentRun.count
Overall Success Ratecompleted / total %groupBy(status)._count
Total LLM Cost (30d)sum costUsd USDagentRun.aggregate._sum.costUsd
Avg Cost per Runtotal cost / run countderived
Total Tokens (30d)sum inputTokens + outputTokensagentRun.aggregate._sum
Models in Usecount of distinct model valuesgroupBy(model)

Success rate color-coded: ≥ 95% green, ≥ 85% amber, < 85% red.

Runs over Time — 30-day bar chart:

  • Fetch { startedAt, status } for all runs last 30d, bucket by UTC date in JS
  • Stacked bars: completed (violet) + failed (red) per day
  • Shows daily run volume and failure spikes at a glance

Performance by Agent Role (table):

ColumnSource
RoleagentRole
Runs (30d)_count
Success Ratecompleted count / total %
Avg Cost / Run_avg.costUsd
Avg Duration_avg.durationMs formatted as s/m
Avg Tokens_avg.inputTokens + _avg.outputTokens

Sorted by run count descending. Each row links to /dashboards/execution-queue?agentRole={role}.

Model Distribution (table):

ColumnSource
Modelmodel
Runs_count
Total Cost_sum.costUsd
Avg Cost / Run_avg.costUsd
% of Runscount / total

Adapter Health (table, only rendered if > 1 distinct adapter):

ColumnSource
Adapteradapter
Runs_count
Failedfailed count
Failure %failed / total — red when > 5%

DB Queries (all AgentRun, 30d window, parallel)

const [ statusBreakdown, // groupBy(status) → _count byRole, // groupBy(agentRole, agentName) → _count, _avg.costUsd, _avg.durationMs, _avg.inputTokens, _avg.outputTokens + failed sub-count byModel, // groupBy(model) → _count, _sum.costUsd, _avg.costUsd byAdapter, // groupBy(adapter) → _count + failed sub-count costAgg, // aggregate → _sum.costUsd, _sum.inputTokens, _sum.outputTokens, _count runsForChart, // findMany({ select: { startedAt, status } }) → bucketed by day in JS ] = await Promise.all([...]);

byRole requires two queries: one groupBy for averages, one filtered where: { status: "failed" } groupBy for failure counts (Prisma groupBy cannot filter within groups).

Distinctions from /agents?tab=analytics

/agents?tab=analytics/dashboards/ai
Per-agent config view with analytics tabPure metrics view, no config
Period picker (7/30/90d)Fixed 30d rolling window
Adapter/model breakdown chartsSame + agent role performance table
No failure rate per roleFailure rate prominently surfaced
No daily run volume chart30-day bar chart

Screen M1e — Tenant Health (/dashboards/tenant-health) [Live Apr 2026]

Purpose: Operational health view across all tenants — who is active and engaged, who is stalled, who is locked out, and how onboarding is progressing. Gives a single-screen signal for churn risk and expansion opportunities.

File: apps/manage/src/app/(manage)/dashboards/tenant-health/page.tsx (async server component, force-dynamic) Nav: Fifth child of Dashboards NavGroup (HeartPulse icon)

Sections

Overview KPIs (top row, 5 stat cards):

CardValueSource
Total Tenantsall statusestenant.count
Activestatus = “active”groupBy(status)
Onboardingstatus = “onboarding”groupBy(status)
Locked Outsubscription.isLockedOut = true AND subscription.status = "active"subscription.count
New This MonthcreatedAt >= UTC month starttenant.count

Engagement KPIs (second row, 2 stat cards):

CardValueColor rule
Engaged (30d)active tenants with ≥ 1 agent run in last 30d, shown as N (X%)≥ 70% green, ≥ 50% amber, < 50% red
Stalledactive tenants with NO agent run in last 30d> 0 = amber; > 10 = red

Onboarding Pipeline (table):

Tenants where status = "onboarding", ordered by createdAt asc (oldest first — most at-risk).

ColumnSource
Tenantname, link to /tenants/{id}
SourcecreatedVia — “self-signup” or “admin”
Days in Onboarding(now - createdAt) in days
Onboarding Completedtick if onboardingCompletedAt != null; date if set, ”—” if not

Empty state: “No tenants in onboarding”.

Stalled Tenants (table):

Active tenants whose most recent agentRun.startedAt is either null (never ran) or older than 30 days. Ordered by last run date asc (longest-stalled first).

ColumnSource
Tenantname, link to /tenants/{id}
Plansubscription → plan.name (active subscription, latest)
Last Runformatted date, or “Never”
Days Stallednow - lastRun in days, or ”—” for never-ran

Show max 20 rows. If > 20 stalled, show count badge: “Showing 20 of N — view all in Tenants”.

Empty state: “All active tenants have run agents in the last 30 days ✓”.

Locked Out Subscriptions (table):

subscription.isLockedOut = true AND subscription.status = "active". Ordered by lastLockedAt asc.

ColumnSource
Tenanttenant.name, link to /tenants/{id}
Planplan.name
Locked AtlastLockedAt formatted
ReasonlockoutInfo.reason (from Json field)
Link”View →” to /tenants/{id}

Empty state: “No subscriptions are currently locked out ✓”.

Plan Distribution (table):

All plans with at least 1 active subscription, grouped by tier.

ColumnSource
Planplan.name
Tierplan.tier (0–3)
Active Subscriptions_count of subscriptions where status = "active"

Ordered by plan.tier asc then name asc.


DB Queries (7 parallel, force-dynamic)

const now = new Date(); const last30d = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000); const y = now.getUTCFullYear(); const m = String(now.getUTCMonth() + 1).padStart(2, "0"); const monthStart = new Date(`${y}-${m}-01T00:00:00.000Z`); const [ statusBreakdown, // tenant.groupBy(status)._count newThisMonth, // tenant.count({ createdAt >= monthStart }) lockedOutSubs, // subscription.findMany({ isLockedOut: true, status: "active" }) + tenant select onboardingTenants, // tenant.findMany({ status: "onboarding" }) ordered by createdAt asc activeTenants, // tenant.findMany({ status: "active" }) + latest active subscription → plan lastRunPerTenant, // agentRun.groupBy(tenantId)._max.startedAt — used for stalled detection planDistribution, // plan.findMany with _count.subscriptions where status="active" ] = await Promise.all([...]);

JS derivations after queries:

// Last run lookup const lastRunMap = new Map(lastRunPerTenant.map(r => [r.tenantId, r._max.startedAt])); // Engagement split const engagedTenants = activeTenants.filter(t => { const last = lastRunMap.get(t.id); return last !== undefined && last !== null && last >= last30d; }); const stalledTenants = activeTenants .filter(t => { const last = lastRunMap.get(t.id); return !last || last < last30d; }) .sort((a, b) => { const la = lastRunMap.get(a.id)?.getTime() ?? 0; const lb = lastRunMap.get(b.id)?.getTime() ?? 0; return la - lb; // oldest first });

Critical data model notes:

  • Tenant status values: "onboarding" | "active" | "suspended" | "churned" (NOT “locked” — lockout is on Subscription via isLockedOut)
  • Use subscription.isLockedOut for “locked out” metric, NOT tenant.status
  • Multiple subscriptions per tenant — always filter subscription.status = "active" and take orderBy: { createdAt: "desc" }, take: 1 for the current plan
  • lockoutInfo is Json? — access (sub.lockoutInfo as { reason?: string })?.reason with a type cast

Screen M1f — Content Pipeline (/dashboards/content-pipeline) [Live Apr 2026]

Purpose: Delivery team view — tracks how much content is being produced, where it is in the approval pipeline, and how social publishing is performing. Cross-tenant.

File: apps/manage/src/app/(manage)/dashboards/content-pipeline/page.tsx (async server component, force-dynamic) Nav: Sixth child of Dashboards NavGroup (Layers icon)

Sections

This Month KPIs (5 cards):

CardValueColor rule
Content Createdtotal blog + social + newsletter createdAt >= monthStartneutral
Published / Sentblog publishedAt + social publishedAt + newsletter sentAt >= monthStartgreen if > 0
DM Review Backlogsum of dm_review status across all 3 typeswarn if > 0, danger if > 20
Client Review Backlogblog client_review + social client_reviewwarn if > 0, danger if > 10
Publish Failuressocial publish_failed current countdanger if > 0

Pipeline Status by Type (table):

One row per content type. Status buckets:

StageBlogSocialNewsletter
DM Reviewdm_reviewdm_reviewdm_review
Client Reviewclient_reviewclient_review— (N/A)
In Progressdm_approved + client_approveddm_approved + client_approved + active + scheduled + publishingdraft + approved + sending
Live / Sentpublishedpublishedsent
Rejected / Failedrejectedrejected + publish_failedfailed

Content Created — 30-day bar chart:

  • findMany({ createdAt >= last30d, select: createdAt }) for all 3 types
  • Stacked bars per day: blog (emerald, bottom) · social (violet, middle) · newsletter (sky, top)
  • Same UTC-day bucketing pattern as AI Performance dashboard

Social Publishing Health (table, last 30d):

  • socialPost.groupBy(platform, status) where status in [published, publish_failed] AND publishedAt >= last30d
  • Columns: Platform | Published | Failed | Failure Rate (amber ≤5%, red >5%)
  • Only rendered if any data exists

DB Queries (13 parallel)

const [ blogByStatus, // blogPost.groupBy(status)._count socialByStatus, // socialPost.groupBy(status)._count newsletterByStatus, // emailNewsletter.groupBy(status)._count blogCreatedMonth, // blogPost.count({ createdAt >= monthStart }) socialCreatedMonth, // socialPost.count({ createdAt >= monthStart }) newsletterCreatedMonth, // emailNewsletter.count({ createdAt >= monthStart }) blogPublishedMonth, // blogPost.count({ publishedAt >= monthStart }) socialPublishedMonth, // socialPost.count({ status:"published", publishedAt >= monthStart }) newsletterSentMonth, // emailNewsletter.count({ status:"sent", sentAt >= monthStart }) socialPlatformHealth, // socialPost.groupBy(platform, status) published/failed last 30d blogForChart, // blogPost.findMany({ createdAt >= last30d, select: createdAt }) socialForChart, // socialPost.findMany({ createdAt >= last30d, select: createdAt }) newsletterForChart, // emailNewsletter.findMany({ createdAt >= last30d, select: createdAt }) ] = await Promise.all([...]);

Key Status Values

BlogPost: dm_reviewdm_approvedclient_reviewclient_approvedpublished (or rejected) SocialPost: dm_reviewdm_approvedclient_reviewclient_approvedactivescheduledpublishingpublished / publish_failed (or rejected) EmailNewsletter: draftdm_reviewapprovedsendingsent (or failed); no client_review stage


Screen M1g — Channel Health (/dashboards/channel-health) [Live Apr 2026]

Purpose: Ops view of connected channel coverage across all tenants — broken connections, low health scores, and channel type coverage at a glance. Supports per-tenant filtering.

File: apps/manage/src/app/(manage)/dashboards/channel-health/page.tsx (async server component, force-dynamic) Nav: Seventh child of Dashboards NavGroup (Signal icon)

Sections

Overview KPIs (5 cards):

CardValueColor
ConnectedisConnected = truegreen if > 0
DisconnectedisConnected = false AND lastConnectedOn != null (was connected, now broken)danger if > 0
Never ConnectedisConnected = false AND lastConnectedOn = nullneutral
At-Risk Channelslatest healthScore < 50 (channels with a completed insight)danger if > 0
Avg Health Scoremean of all latest health scoresgreen ≥ 70, amber ≥ 50, red < 50

When not filtering by tenant: show additional “Tenants with No Connected Channel” card (active tenants with zero isConnected channels).

Coverage by Channel Type (table):

Derived from allChannels grouped by type in JS.

ColumnSource
Channel Typeformatted type string
Totalcount of channels of this type
Connectedcount where isConnected = true
With Insightscount with a completed insight
Avg Health Scoremean healthScore for this type

Disconnected Channels (table — only if any exist):

isConnected = false AND lastConnectedOn != null, sorted by lastConnectedOn asc (longest disconnected first). Capped at 20.

Columns: Tenant | Channel | Type | Last Connected | Days Disconnected

Low Health Score Channels (table — only if any exist):

Channels whose latest completed insight has healthScore < 50, sorted by score asc.

Columns: Tenant | Channel | Type | Health Score | Last Insight


DB Queries (3 parallel)

const [ allTenants, // tenant.findMany({ status: in [active, onboarding] }) — dropdown + coverage calc allChannels, // connectedChannel.findMany({ tenantId: tId }) + tenant.name + tenant.status latestInsights, // channelInsight.findMany({ status:"done", tenantId:tId, distinct:["connectedChannelId"], orderBy:{completedAt:"desc"} }) ] = await Promise.all([...]);

All JS derivations from two in-memory arrays — no extra queries.

healthScore color rules: ≥ 70 green · ≥ 50 amber · < 50 red


Screen M1h — Goal & Strategy Performance (/dashboards/goal-strategy) [Live Apr 2026]

Purpose: Exec and delivery view — are tenants progressing through planning, and are their goals being tracked and achieved? Supports per-tenant filtering.

File: apps/manage/src/app/(manage)/dashboards/goal-strategy/page.tsx (async server component, force-dynamic) Nav: Eighth child of Dashboards NavGroup (Target icon)

Sections

Overview KPIs (5 cards):

CardValueColor
Active PlansdeliverablePlan.status = "approved"green if > 0
Plans in Reviewstatus in ["pending_review", "dm_approved"]warn if > 0
Active Goalsgoal.status = "active"neutral
Goals Being Trackedgoals with at least 1 non-baseline GoalSnapshotneutral
Tenants with Approved Plandistinct tenantIds across approved plansgreen if > 0

When not filtering by tenant: also show “Tenants Without Approved Plan” count (active tenants with no approved plan — red if > 0).

Plan Pipeline (table):

| Status | Count | | pending_review | N | | dm_approved | N | | approved | N | | archived | N |

Goal Progress Distribution (table):

For goals in approved plans that have at least one snapshot, compute metricStatus in JS:

function computeMetricStatus(current, baseline, target): string { if (!target || !baseline) return "pending"; const progress = (current - baseline) / (target - baseline); if (progress >= 1.0) return "achieved"; if (progress >= 0.75) return "on_track"; if (progress >= 0.5) return "tracking"; if (progress >= 0.25) return "behind"; return "at_risk"; }

Displays a count row per status: achieved (green) · on_track (emerald) · tracking (blue) · behind (amber) · at_risk (red) · pending (gray).

Strategy Status (table):

strategy.groupBy(status)._count — shows how many tenants have a draft vs approved strategy.

Tenants Without Approved Plan (table — hidden when filtering by tenant, capped at 20):

Active tenants with no deliverablePlan at status = "approved". Sorted by createdAt asc (oldest first). Columns: Tenant | Joined | Days Since Joining | Link.


DB Queries (6 parallel)

const [ allTenants, // tenant.findMany({ status: in [active, onboarding] }) — dropdown + coverage plansByStatus, // deliverablePlan.groupBy(status)._count — pipeline state approvedPlanData, // deliverablePlan.findMany({ status:"approved", select:{tenantId} }) — tenant coverage strategiesByStatus, // strategy.groupBy(status)._count activeGoals, // goal.findMany({ status:"active" }) — select id, tenantId, targetNumericValue, baselineValue + plan status latestGoalSnapshots, // goalSnapshot.findMany({ isBaseline:false, distinct:["goalId"], orderBy:{snapshotDate:"desc"} }) ] = await Promise.all([...]);

metricStatus color rules: achieved (emerald) · on_track (green) · tracking (blue) · behind (amber) · at_risk / no-snapshot (red) · pending (gray)


Screen M2 — Tenants (/tenants) [Live]

Purpose: Full list of all tenants. Currently the landing page after login.

  • Heading: “Tenants”
  • Stat cards: Total · Active · Onboarding · Inactive (with counts)
  • “Deleted Tenants” link button (→ /tenants/deleted) — neutral style, left of “Create Tenant”
  • ”+ New” button (→ /tenants/create)
  • Filter panel toggle

Table columns

ColumnNotes
Tenant NameAvatar initials + name
StatusActive / Onboarding / Inactive badge
SourceAdmin (manually created) · register (self-service) · Self
Country / Region
Created At
Actions... menu

Row actions (...)

  • View — navigate to Tenant Detail (M3)
  • Suspend — confirmation dialog; blocks tenant access
  • Change plan — plan change modal
  • Impersonate — opens Dashboard as that tenant user for support debugging; action written to audit_logs

Filter panel

Filters: Status · Source · Date range (created).


Screen M2d — Deleted Tenants (/tenants/deleted) [Live]

Purpose: Audit trail of all tenant deletion jobs — in-progress and completed.

Layout

"use client" page with IntersectionObserver infinite scroll (no page-number pagination).

Table columns

ColumnNotes
TenantName
Requested ByName of super admin who triggered deletion
StatusColour badge: pending (yellow) · running (blue) · completed (green) · completed_with_errors (orange) · failed (red)
ProgressX / 9 steps completed counter
RequestedISO date of the deletion request
CompletedISO date of completion, or

Row click → /tenants/deleted/[deletionId] (M2d-detail).

Proxy route: GET /api/admin/tenant-deletionsGET /admin/v1/tenant-deletions?limit=30&cursor=.


Screen M2d-detail — Deletion Detail (/tenants/deleted/[deletionId]) [Live]

Purpose: Step-by-step log of a single tenant deletion job.

Server-rendered page; data fetched server-side via the API.

Header card

  • Tenant name (h1) + Tenant ID in mono
  • Status badge + “Run Cleanup” button (shown only when status === "failed" | "completed_with_errors")
  • 4 info tiles: Requested By · Requested date · Duration · Job ID (last 8 chars)
  • Red error banner if errorMessage is set

Records Snapshot section

Grid of tiles (count + label) showing the record state at the time deletion was triggered. Keys: blogPosts, socialPosts, keywords, agentRuns, contacts, leads, reports, etc.

Deletion Steps table

ColumnNotes
#Step icon: pending · running · completed · failed · skipped
StepLabel (bold) + step key below in mono
StatusColour badge
RecordsCount of records affected (or )
DurationXms or X.Xs
DetailHuman-readable result message (e.g. “3 jobs removed”, “42 files deleted”)

Run Cleanup button (DeletionCleanupButton.tsx): "use client" component. POSTs to /api/admin/tenant-deletions/[id]/cleanup, then calls router.refresh(). Only visible for failed or completed_with_errors status. Re-runs all 9 steps from scratch (deletes old step rows, creates fresh ones, re-enqueues job).

Proxy routes:

  • GET /api/admin/tenant-deletions/[id]GET /admin/v1/tenant-deletions/:id
  • POST /api/admin/tenant-deletions/[id]/cleanupPOST /admin/v1/tenant-deletions/:id/cleanup

Screen M3 — Tenant Detail (/tenants/[tenantId]) [Live]

Purpose: Full visibility and control for a single tenant.

URL pattern: /tenants/[tenantId] (active tab stored in URL hash, e.g. #invoices)

Layout: Vertical sidebar nav on the left (w-44) + content panel (flex-1) — 19 tabs total.

Tabs

#Tab keyContent
1progressMonth Progress panel
2pipelinePipeline status panel
3contextRead-only client context viewer; version picker (when >1 version); timeline + details sidebar; PDF download. Fetched from GET /api/admin/tenants/[tenantId]/context
4strategyRead-only strategy viewer; same layout as context with purple gradient. Fetched from GET /api/admin/tenants/[tenantId]/strategy
5strategy_configPer-tenant strategy writer input toggles. Fetched from GET/PUT /api/admin/tenants/[tenantId]/strategy-config
6deliverable_configPer-tenant activity planner overrides (max activities, require approval). Fetched from GET/PUT /api/admin/tenants/[tenantId]/deliverable-config
7subscription_settingsMulti-row subscriptions table; per-row Edit, Lock/Unlock, Cancel; ”+ Add Subscription” button
8invoicesPaginated invoice list (25/page) for this tenant; click row → /invoices/[id]; “Raise Invoice” slide-over; fetched from GET /api/admin/tenants/[tenantId]/invoices
9usageCredits & LLM tab — credit balance + adjust form + LLM spend breakdown + ledger table
10activitiesPaginated activity log (50/page) with Label, Type, Status, Agent Queue, Due Date, Created columns; fetched from GET /api/admin/activities?tenantId=
11goalsSummary stat cards (Total/Ahead/On Track/Behind) + goal cards with progress bars; inline edit on hover; fetched from GET /api/dm/goals?tenantId=
12usersAll members of this tenant (via TenantMember); ”+ Add User” modal (Existing User search or New User form); static prop from server component
13deliverablesPeriod picker + expandable deliverable type rows with item grid; fetched from GET /api/dm/deliverables?tenantId=&period=YYYY-MM
14keywordsInfinite-scroll keyword list; fetched from GET /api/admin/tenant-keywords
15keyword_groupsInfinite-scroll keyword groups; fetched from GET /api/admin/tenant-keyword-groups
16reportsInfinite-scroll reports list; fetched from GET /api/admin/tenant-reports
17audit_logsPaginated audit trail (50/page); fetched from GET /api/admin/audit-logs?tenantId=
18settingsGeneral Settings form — Company (name, website, address, industry, country, timezone, currency) + Point of Contact + Automation (backlink auto-send toggle)
19context_settingsPer-tenant context-file-writer input toggles — see Context Settings tab detail below
20design_intelligenceDesign Intelligence — 6 per-feature AI layer toggles; auto-saves on each toggle

Tenant header

  • Status badge — clickable dropdown (via TenantStatusChanger) allowing transitions: onboarding→[suspended, cancelled]; active→[suspended, cancelled]; suspended→[active, cancelled]; cancelled→[active, suspended]. Status changes are written to audit log (tenant.status_changed).
  • Locked Out badge — shown if any subscription is locked out.
  • Activate button — shown only when status is onboarding; triggers the activation flow: sets tenant to active, stamps billingActivatedOn / startDate on the subscription (billing cycles start from this date), and writes a tenant.activated audit log entry. No invoice is created here — the first invoice was already raised at tenant creation.
  • Delete Tenant button (DeleteTenantButton.tsx) — red-bordered button in the top-right of the header card. Disabled (shows “Deleting…”) when tenant.status === "deleting". Click opens a confirmation modal requiring the user to type the exact tenant name. On confirm, POSTs to /api/admin/tenants/[tenantId]/delete, which triggers the 9-step background deletion job (see Screen M2d-detail). Redirects to /tenants/deleted/[deletionId] on success. The deletion is irreversible — all DB records, files, vectors, and search index documents are removed.

Subscriptions tab detail

Table columns: Name / Plan | Region | Price | Status | Start Date | Next Billing | Actions

Per-row actions:

  • Edit — opens a right slide-over form (Name, Plan picker, Pay without TDS, Payment due days); writes subscription.updated audit log
  • Lock — inline lock confirm row with optional reason field; writes subscription.locked audit log
  • Unlock — confirm dialog; writes subscription.unlocked audit log
  • Cancel — confirm dialog; sets status to cancelled; writes subscription.cancelled audit log

Add Subscription — right slide-over with Name (required, e.g. “SAAS”, “Hosting”, “Managed Services”), Plan picker, Pay without TDS, Payment due days. Writes subscription.created audit log.

A tenant can have multiple active subscriptions simultaneously (e.g. SAAS + Hosting). Invoices can be raised against any specific active subscription.

Billing lifecycle: The first invoice is created at tenant creation (status pending, due now + paymentDueDays). The tenant stays in onboarding until manually activated via the Activate button. On activation, billingActivatedOn and startDate are set on the subscription — the billing server picks these up to generate subsequent invoices from the next cycle onwards.

General Settings tab detail

Company section fields: Company Name (required) · Website · Address (multiline textarea) · Industry · Country · Timezone · Currency. Point of Contact section fields: Name · Email · Phone.

All four pickers (Industry, Country, Timezone, Currency) use a custom SearchableSelect component — click to open dropdown with inline search input, type to filter, click to select. Outside-click closes.

Automation section (added April 2026):

  • Backlink Auto-Send — toggle switch (backlinkAutoSend boolean). When ON, outreach emails drafted by the backlink-outreach-writer agent are sent automatically without requiring DM review. Default: OFF (DM reviews first).

Underlying Tenant model fields: name, website, address, pocName, pocEmail, pocPhone, timezone, country, currency, industry, backlinkAutoSend. (pocPhone, country, currency added April 2026; backlinkAutoSend added April 2026.)

API: PATCH /admin/v1/tenants/:tenantId — sparse update (only provided fields are changed). Server action: updateTenantSettings in actions.ts.

Context Settings tab detail

Controls which inputs the AI setup chain uses when building the tenant’s Client Context File. All four inputs default to true for every tenant. Changes are non-destructive — disabling an input does not delete data; it affects only the next generation run.

Stored as: Tenant.contextConfig Json? — a TenantContextConfig object. Parsed via resolveContextConfig() in setup.worker.ts, which fills in true defaults for any missing keys.

API: PATCH /admin/v1/tenants/:tenantId with { contextConfig: { ... } }. Server action: updateTenantSettings in actions.ts (passes Prisma.DbNull when null to satisfy Prisma’s nullable JSON constraint).

ToggleFieldDefaultEffect when disabled
Live website crawlenableWebCrawltrueClient-researcher uses only the RAG knowledge base; no live crawling. Use for pre-launch tenants, JS-heavy sites, or clients with no public website.
Competitor researchenableCompetitorResearchtrueCompetitor-researcher step is skipped entirely. Context file Competitive Landscape section will note that competitor research is not configured.
Brand voice sectionenableBrandVoiceSectiontrueBrand voice block is not appended to the context file after generation. Use when the tenant has not yet completed brand voice setup.
Key pages sectionenableKeyPagesSectiontruePublished blog posts and landing pages are not appended to the context file. Use for early-stage clients with no published content.

Help topic: context-settings — opened via <HelpTrigger slug="context-settings" /> in ContextSettingsTab.tsx.

Goals tab detail — inline editing

Super admins can edit any goal directly without triggering approval resets. Hover a goal card → pencil icon appears → click to expand inline edit form with fields: Title · Target Value · Metric · Timeframe (months) · Channel · Deliverable Types (comma-separated). Save calls PATCH /admin/v1/goals/:goalId via updateGoal server action; local state updates optimistically.

Users tab detail — Add User modal

”+ Add User” button opens a modal with two modes:

  • Existing User — searchable list of all platform users fetched from GET /api/admin/users; select a user and role, calls assignExistingUser server action (PATCH /admin/v1/users/:userId with { tenantId }).
  • New User — form with Name / Email / Password / Role; calls createUserForTenant server action (POST /admin/v1/users).

Data source: The users list is sourced from TenantMember, not User.tenantId. DM reviewers and other cross-tenant users only appear if they have a TenantMember record for this tenant. User.tenantId reflects their primary/registration tenant and must never be used for membership queries.

Context / Strategy tabs detail

Both tabs are read-only (no approve or revise actions). Layout mirrors dashboard context/strategy pages:

  • Hero card with gradient (blue = context, purple = strategy) and MarkdownRenderer content
  • Version picker buttons (shown only when >1 version exists)
  • Right sidebar with two sub-tabs: Timeline (log entries with actor + message) and Details (version history list)
  • Download PDF button — uses marked to render markdown → custom branded HTML → window.open() + window.print()

Proxy routes: GET /api/admin/tenants/[tenantId]/context and GET /api/admin/tenants/[tenantId]/strategy. Upstream API: GET /admin/v1/tenants/:tenantId/context and GET /admin/v1/tenants/:tenantId/strategy — include logs and versions via Prisma include.

Strategy Config tab

[Live Apr 2026] Controls which data sources are fed to the strategy writer when generating or revising a strategy for this tenant. All flags default to ON — disable only when the specific input is known to be inaccurate or irrelevant.

Changes take effect on the next strategy run (generate or revision). They do not retroactively affect existing strategy versions.

ToggleWhat it controlsWhen to turn off
Baseline performance dataInjects manually entered metrics (monthly visitors, organic clicks, ad spend per platform, social followers) into the promptThe client entered rough estimates or placeholder numbers during onboarding that would skew the strategy’s goal-setting
Knowledge base: brand docsRuns a RAG search against brand voice, messaging, target audience, and product documentsNew tenant with no uploaded brand documents — empty RAG results add noise
Knowledge base: competitor researchRuns a RAG search against competitor analysis and market positioning documentsThe client is sensitive about competitor mentions appearing in their strategy document
Connected channel contextFetches all OAuth-connected channels (isConnected: true) and tells Claude to prioritise them; also instructs Claude to flag any recommended channels that aren’t connected yetChannels haven’t been connected yet but you want to run the strategy now — prevents Claude from noting missing connections as warnings

Data flow: StrategyConfigTab.tsxPUT /api/admin/tenants/[tenantId]/strategy-config proxy → PUT /admin/v1/tenants/:tenantId/strategy-config Fastify route → upserts TenantStrategyConfig row → strategy-writer worker reads flags at job start.

Where to find it: Manage → Tenants → [Tenant name] → Strategy Config (in the left tab nav, below Strategy).

Help topic: strategy-config — wired via <HelpTrigger slug="strategy-config" /> in StrategyConfigTab.tsx; covers all 4 toggles, their impact, and when to change each setting. Defined in apps/manage/src/app/(manage)/help/_data/index.ts.

Deliverable Config tab

[Live May 2026] Per-tenant overrides for the activity planner. Fields left blank inherit the global value from Manage → System → Deliverable Settings (which in turn falls back to the code default).

FieldDB columnBehaviour when setBehaviour when empty
Activity pipeline approvalrequire_activity_approvalOverrides global requireActivityApproval for this tenant onlyInherits global PlatformSetting (default true)
Max activities per periodactivity_planner_max_activitiesCaps how many activities the planner may generate for this tenantInherits global activityPlannerMaxActivities (default 300)

Resolution chain (both fields): TenantDeliverableConfig value → PlatformSetting global value → code default (300 / true).

Data flow: DeliverableConfigTab.tsxPUT /api/admin/tenants/[tenantId]/deliverable-config proxy → PUT /admin/v1/tenants/:tenantId/deliverable-config Fastify route → upserts TenantDeliverableConfig row → activity planner worker reads at job start.

Where to find it: Manage → Tenants → [Tenant name] → Deliverable Config (in the left tab nav, below Strategy Config).

DB model: TenantDeliverableConfigtenant_deliverable_config table; 1:1 with Tenant; nullable fields; onDelete: Cascade.


Planned tabs to add [To Build]

  • Config — plan, feature flags, allowed LLM providers, spending limits, deployment mode
  • Agents — agent configs for this tenant; inherits from master (M5) with overrides highlighted
  • Knowledge Base — dataset summary per tenant: file count, chunk count, embedding model, status; trigger re-index / clear

Knowledge Base tab detail (planned)

DatasetFilesChunksEmbedding ModelStatus
Client Documents329text-embed-3-small
Website Content142 pages1,840text-embed-3-small✅ weekly crawl
Published Content24312text-embed-3-small✅ auto
Competitor Research18210nomic-embed-text✅ local

Screen M4 — Global Invoices (/invoices) [Live]

Purpose: Invoice list across all tenants platform-wide.

Stats bar

Four stat cards at the top: Total Invoices · Pending · Paid · Overdue (counts from current filtered set).

Table columns

ColumnNotes
Name”TenantName – Mon YYYY” (period-based). Invoice number in mono below. Clicking navigates to detail page.
Net PayableFormatted currency amount
Due DateFormatted date
StatusPill badge with dot (draft / pending / paid / overdue / void)
Billed OnInvoice issued date
Type”Subscription” or “One-time”
ActionsEye icon (→ detail page) · Edit icon (→ status/notes slide-over)

Search: by invoice number or tenant name. Filters: Status dropdown · Type dropdown.

Create Invoice button

”+ Create Invoice” button navigates to the dedicated Invoice Create page (M4c).


Screen M4c — Invoice Create (/invoices/create) [Live]

Purpose: Full-page form to create a new manual invoice for any tenant.

Breadcrumb: Invoices / Create Invoice

Sections

General Details

FieldNotes
TenantRequired dropdown; auto-fills subscription billing info on selection
CurrencyAuto-filled from subscription (default INR)
Issued DateDefaults to today
Due DateDefaults to today + paymentDueDays from subscription
Period Start / EndDate pickers
Tax applicable?Toggle; when on, shows Tax Type (Inclusive / Exclusive)
NotesOptional textarea

Billing Address — pre-filled from subscription billing fields if set; editable inline. Name · Address · City · State · Country · Postal.

Line Items — same Add Item modal as M4b (Pre-defined / Custom with Qty, Unit Price, SGST %, CGST %, Discount fields).

  • Live summary: Subtotal → GST → Gross Amount → TDS → Net Payable
  • Amounts entered in major units (₹), stored in minor units (paise × 100)

Subscription picker: When the selected tenant has multiple active subscriptions, a subscription dropdown is shown in General Details so the invoice can be associated with the correct subscription.

Submit: “Create Invoice” button — validates tenant + billing fields + ≥1 line item, generates invoice number (INV-YYYY-MM-XXXX, zero-padded sequence), creates DB record, redirects to M4a (detail page).

Validation errors appear inline. “This tenant has no subscription” shown if selected tenant is not yet active.


Screen M4a — Invoice Detail (/invoices/[invoiceId]) [Live]

Purpose: Full document-style view of a single invoice with timeline and reminder history.

Layout

Two-column: left = invoice document card; right = Details info + Timeline.

Breadcrumb: ← Invoices / INV-XXXX / Details

Top bar: Status pill · Print / PDF button · Reminders button (badge with count) · Edit Invoice link (→ M4b) · Cancel Invoice button (red, hidden if already void/paid).

Outstanding banner: For pending/overdue invoices — shows net payable amount + days remaining/overdue.

Invoice document card

  • Invoice number heading + status badge
  • Tenant name + billing period line
  • Bill To section: billing address from subscription
  • Plans table: Item / Amount / Tax / Total columns; line items rendered as rows
  • Tax summary (right-aligned): Total → GST (%) → Gross Total → TDS (%) → Net Payable
  • Notes (if set)

Details card (right)

Offering / Plan · Region · Issued date · Due date · Paid date (if paid).

Timeline card (right, data-testid="invoice-timeline")

Chronological events: Invoice created · Invoice issued · Payment due · Marked overdue (if applicable) · Payment received. Color-coded dots (green = paid, red = overdue, amber = due).

“Print / PDF” button calls window.print(). Sidebar, top bar, outstanding banner, and right panel are hidden via print:hidden Tailwind classes. Invoice document card renders full-width with no borders/shadows for a clean PDF.

Cancel Invoice

“Cancel Invoice” button (shown only if status is not void or paid). Clicking opens a confirmation dialog:

  • “This will permanently void the invoice. This cannot be undone.”
  • “Yes, Cancel Invoice” / “Keep Invoice” buttons.
  • On confirm: sets status to void, writes invoice.cancelled audit log entry.

Reminders modal

Opened via “Reminders” button. Shows:

  • Last Reminder card: date + time of most recent reminder sent
  • Next Reminder card: scheduled next reminder date (or status message)
  • Reminder History list: all reminders with type, channel, sent time, relative timestamp
  • Send Now button: manually triggers a reminder email immediately (not shown if paid/void)

Screen M4b — Invoice Edit (/invoices/[invoiceId]/edit) [Live]

Purpose: Full edit form for an invoice — dates, billing address, line items, tax settings.

Breadcrumb: Invoices / INV-XXXX / Edit

Sections

General Details

FieldNotes
Invoice NumberRead-only
StatusDropdown: draft / pending / paid / overdue / void
Issued DateDate picker
Due DateDate picker
Period Start / EndDate pickers
Tax (GST) %Number input
TDS %Number input
NotesTextarea

Billing Address — updates subscription billing fields: Name · Address · City · State · Country · Postal.

Line Items — table with Description / Qty / Unit Price / Amount rows.

  • “Add Item” button opens modal.
  • Each row has a × remove button.
  • Live amount summary (Subtotal → GST → Gross → TDS → Net Payable) updates as items change.

Add Item modal Toggle: Pre-defined (list of common item types) | Custom (free-text description). Fields: Qty · Unit Price · SGST % · CGST %.

Save Changes button: recalculates all amounts and redirects to detail page.


Screen M4p — Plans (/plans) [Live]

Purpose: List and manage all billing plans across offerings and regions.

Header

  • Stat cards: Total Plans · Active Plans · Custom Plans · Total Tenants
  • Buttons: Offerings (→ /plans/offerings) · Regions (→ /plans/regions) · + New Plan (→ /plans/new)
  • Filters: Offering dropdown · Region dropdown · “Custom plans only” checkbox

Table columns

ColumnNotes
PlanAvatar initial + name; green if Custom offering, violet otherwise
OfferingOffering name
RegionRegion name
PriceFormatted in major currency units + /mo or /yr
TierT{n} {label} badge (T0 Starter / T1 Pro / T2 Agency / T3 Enterprise)
TrialTrial days or ”—“
TenantsCount with user icon
StatusActive / Inactive badge

Row click navigates to plan detail.


Screen M4p-detail — Plan Detail (/plans/[planId]) [Live]

Purpose: Full details for a single billing plan including tenants on that plan.

Header

  • Avatar initial + plan name + Custom badge (if applicable) + Active/Inactive badge + Tier badge
  • Price in major units + billing cycle (right-aligned)
  • Edit button → /plans/[planId]/edit
  • Delete button — two-click confirm; disabled (with tooltip) if any tenants are subscribed

Info cards

Offering · Region · Trial days · Tenant count

Features section

Rendered as a grid of key/value pairs from plan.features JSON.

Tenants table

All tenants currently subscribed to this plan: Tenant name (link to detail) · Status · Subscription status badge · Start Date · Next Billing · View link.


Screen M4p-new — New Plan (/plans/new) [Live]

Purpose: Create a new billing plan.

Fields

FieldNotes
OfferingDropdown of all offerings
RegionDropdown of all regions
NameText input
DescriptionOptional textarea
PriceNumber input in major units (paise/fils handled automatically: stored × 100)
Billing Cyclemonthly / annual
Tier0 = Starter / 1 = Professional / 2 = Agency / 3 = Enterprise
Trial DaysNumber, default 0
ActiveCheckbox, default true

Features section

Structured inputs for common feature keys:

  • Max Users, Max Agents (numbers)
  • Credits / Month (number — drives monthly credit allocation via plan.features.creditsPerMonth)
  • Highlights (dynamic list — add / remove string items)

Screen M4p-edit — Edit Plan (/plans/[planId]/edit) [Live]

Same fields as New Plan. Offering and Region are shown as read-only labels (cannot be changed after creation).


Screen M4p-offerings — Offerings (/plans/offerings) [Live]

Purpose: List and manage plan offerings (e.g. “Starter”, “Professional”, “Custom”).

Table: Name · Description · Plans count · Active status · Edit link · Delete button. Delete is blocked if offering has any plans.

Sub-pages: /plans/offerings/new (create) · /plans/offerings/[offeringId]/edit (edit). Fields: Name (unique), Description, Active toggle.


Screen M4p-regions — Regions (/plans/regions) [Live]

Purpose: List and manage billing regions (e.g. “India”, “Middle East”).

Table: Code · Name · Currency · Tax% · TDS% · Active status · Edit link · Delete button. Delete is blocked if region has any plans.

Sub-pages: /plans/regions/new (create) · /plans/regions/[regionId]/edit (edit). Fields: Name (unique), Code (2–5 chars, uppercase, immutable after creation), Currency, Currency Symbol, Tax %, TDS %, Active toggle.


Screen M5 — Global Users (/users) [Live]

Purpose: All human users across the entire platform.

Table of all users across all tenants with roles, tenant association, and status.


Screen M6 — Global Deliverables (/deliverables) [Live]

Purpose: Deliverable management across all tenants.

Table of all deliverables platform-wide.


Screen M7 — Master Agent Configs (/agents) [Live]

Purpose: Platform-wide agent configurations and run analytics. The page has two tabs: All Agents (config list) and Analytics (platform-wide run metrics).

All Agents tab (/agents):

  • Stat cards: Total Agents · Free Tier · Pro Tier · Active
  • Grouped by category: Setup / Strategy / Orchestration / SEO / Content / Reporting / Research / Reactive
  • Columns: Agent name + description · Model · Plan badge · Status indicator · View link

Agent detail page (/agents/[agentId]):

  • Header: name · plan badge · active status
  • Active run banner (if agent is currently running — shows tenant + start time)
  • Detail cards: Adapter (label + model ID) · Category · Role Identifier · Tenant Configs count
  • Activity OverviewAgentRunStats component: 4 stat cards (Total Runs · Total Time · Total Cost · Avg Duration) + 30-day bar chart. Data from GET /admin/v1/agents/:agentId/run-stats (cross-tenant aggregate). Bars: violet = completed only, amber = has failures, grey = no activity. Hover tooltip per day.
  • Mapped Skills section — shows skills currently mapped to this agent; “Add Skill” button opens a modal to search and select from the global skills library; individual skills can be removed with the × button
  • System Prompt section (scrollable pre block)
  • Run History table (last 10 runs with duration, cost, tools, adapter/model, status)
  • “View all runs” link → /agents/[agentId]/runs
  • Edit button → /agents/[agentId]/edit

Agent edit page (/agents/[agentId]/edit):

  • Fields: Name · Description · Adapter (dropdown: Claude Code / Codex / Gemini CLI) · Model (dropdown, auto-populated per adapter) · System Prompt · Plan Tier · Active toggle
  • Changing adapter auto-resets model to the first model of the new adapter
  • Adapter + model saved to agent_config.adapter + agent_config.model in DB

Analytics tab (/agents?tab=analytics) [Live]:

  • Period selector: Today (1 day) / 7 Days / 30 Days / 90 Days
  • Row 1 stat cards: Total Runs · Completed (with success %) · Failed (with running count) · Total Cost
  • Row 2 stat cards: Total Time · Avg Duration · Active Agents in period · Avg Cost per Run
  • Activity chart: bar chart per day over period (violet = clean, amber = has failures, grey = no activity); hover tooltips
  • Adapter Usage breakdown: horizontal bar per adapter (Claude Code / Codex CLI / Gemini CLI) with run count, cost, % share
  • Model Usage breakdown: horizontal bar per model with run count, cost, % share (up to 10 models)
  • Top Agents table: Agent name + role · Runs · Completed · Failed · Success % (color-coded green/amber/red) · Total Cost · Avg Duration
  • Data from GET /api/admin/analytics/agents?period= → proxies to GET /admin/v1/analytics/agents?period=

Agent runs page (/agents/[agentId]/runs):

  • Filter bar: Status · Tenant search · From/To date · Sort (newest/oldest)
  • Paginated table with cursor-based “Load more”: Tenant · Status · Task · Duration · Cost · Tools · Skills (⚡ N count) · Adapter / Model · Started
  • URL-synced filter state
  • Skills column shows for runs predating the skills logging feature
  • Adapter / Model column shows adapter label (Claude Code / Codex / Gemini CLI) with model ID below in mono; for older runs without adapter data

Agent run detail (/agents/[agentId]/runs/[runId]):

  • Stat cards: Duration · Cost · Tool Calls
  • Input section: task summary · dynamic context · system prompt
  • Skills Injected card — chips showing each skill’s name, filename, and category badge; hidden when no skills were logged
  • Error banner (if failed)
  • Full output in scrollable pre block

Screen M8 — Global Skills (/skills) [Live]

Purpose: Platform-wide skill library. Skill files are written to the agent’s temp skill directory at execution time and passed to Claude Code via --add-dir.

Route: /skills (sidebar entry: Skills)

Skills list page (/skills):

  • Stat cards: Total Skills · Active · Archived · Platform Defaults
  • Filter bar: search (name/description/filename) · Category · Status · File Type · Sort · Order
  • Table columns: Name (with default badge) · File (.md/.js/.sh badge + filename) · Category · Agents (count) · Status · Updated date · Edit link

Create page (/skills/new):

  • Fields: Name · Category · Description · Filename (auto-generated from name) · File Type (.md / .js / .sh) · Content (monospace textarea, 24 rows) · Platform default checkbox
  • Filename extension auto-updates when File Type changes

Edit page (/skills/[skillId]/edit):

  • Same form pre-filled
  • Archive / Restore toggle button (soft delete — sets status to “archived”, does not delete row)

Data model:

skill: id · name · description · filename · fileType · content · category · isDefault · status · createdAt · updatedAt agent_skill: id · agentConfigId · skillId · createdAt [unique on (agentConfigId, skillId)]

Execution flow: At agent job start, createSkillsDir(tenantId, agentRole) queries agent_skill for active skills mapped to that role and writes each file (filename + content) to the temp dir alongside the always-present search_knowledge.js.

Platform-seeded default skills (15 total):

SkillCategoryMapped to
Research Methodology GuideResearchclient-researcher, competitor-researcher, topic-researcher, research-note-writer
Competitor Analysis FrameworkResearchcompetitor-researcher
Context File StructureGeneralcontext-file-writer
Marketing Strategy FrameworkStrategystrategy-writer
Deliverable Types ReferenceStrategydeliverable-planner, activity-planner
SEO Writing Best PracticesSEOblog-writer, keyword-researcher, content-brief-writer, landing-page-writer
Keyword Research MethodologySEOkeyword-researcher, content-brief-writer
Content Brief TemplateSEOcontent-brief-writer, blog-writer
Social Media Platform GuideContentsocial-post-writer, social-calendar-planner
Ad Copy FrameworksContentgoogle-ads-writer, meta-ads-writer
Email Copywriting FrameworkContentemail-writer
Landing Page Conversion PrinciplesContentlanding-page-writer
GBP Post Best PracticesContentgbp-post-writer
Reporting Standards GuideReportingreport-writer, ads-analyst, anomaly-detector
Review Response GuideGeneralreview-response-writer

Tenant-specific skills are [To Build] — planned for the tenant Settings screen.


Screen M9 — LLM Providers (/providers) [To Build]

Purpose: Platform-wide LLM provider management and health monitoring.

ProviderStatusAPI KeyTotal Cost (Month)Actions
Anthropic✅ OKsk-…abc$892Configure
OpenAI✅ OKsk-…xyz$312Configure
Ollama✅ Local$0Configure

Per-provider detail panel:

  • Rate limit status (requests/min, tokens/min) — live
  • Error rate (last 1h, last 24h)
  • Model availability (which models are enabled platform-wide)
  • Toggle enable/disable per plan tier (e.g. disable Claude Opus for Free plan tenants)

Screen M10 — Email Templates (/templates/email) [Live]

Purpose: Manage system email templates used for notifications and transactional emails.

Header

  • “Templates (51)” — 51 templates in dev
  • Filter button · ”+ Add” button

Table columns

ColumnNotes
NameTemplate key (camelCase, e.g. invoiceDraftStatusMail)
TitleHuman-readable subject/title
TypeAlways email
Created OnDate
Actions... menu

Observed templates (sample)

invoiceDraftStatusMail · clientReviewMail · clientRejectedMail · clientApprovedMail · autoApprovedMail · credentialMail · register · welcomeMailSelf · welcomeMailAdmin · userWelcomeMail · newLeadMail

Interactions

  • Click row → right slide-over with template details and body preview
  • /templates/email/[name] direct URL returns 404 — not yet implemented; use slide-over

Screen M11 — Activity Templates (/templates/activity) [Live — partial]

Purpose: Platform-wide activity templates used to generate recurring activities for tenants.

Current state [Live]

  • “Activity Templates (1703)” — flat table
  • Columns: Channel · Title · Description · Type · Frequency · Due Days · ... actions
  • Filter button · Import button
  • Titles and descriptions use {{VariableName}} placeholder syntax

Planned enhancement [To Build]

Split into two tabs matching the full pipeline architecture:

Pipeline Templates tab

Template NameDeliverable TypeStepsTenants UsingActions
Standard Blog Post Pipelineblog_posts414Edit / Clone / Archive
Standard Backlinks Pipelinebacklinks511Edit / Clone / Archive
GBP Posts Pipelinegbp_posts48Edit / Clone / Archive

Visual pipeline editor: Each step as a card — name, assignee type (agent or human), agent role, is approval gate. Drag to reorder. “Save as new version”. “Push to tenants” (confirmation shows affected count).

Recurring Task Templates tab

Template NameTriggerPeriodicityAssigneeVariablesTenants
Update GBP “always open”on_onboardingone_timeHumangbp_url14/14
Request Google reviewscronmonthlyHumangbp_url14/14
NAP consistency checkcronquarterlySEO Specialist agentbusiness_name, address9/14

Template editor fields: Name · description · assignee (agent role or human role) · periodicity · trigger · cron expression with human-readable preview · prompt template with {{variable}} syntax · variable definitions table.

Variable substitution preview: Select a tenant from dropdown to preview with real data.

On save: New tenants get this template automatically. Existing tenants are not updated unless they adopt it (show non-adopting tenant count).


Screen M12 — Request Queue (/request-queue) [Live]

Purpose: Manage incoming blog content creation requests from digital marketers.

Header

  • “Request Queue — Manage incoming blog creation requests from digital marketers”
  • ”+ Add Request” button

Stat cards

CardCount (observed)
Pending Requests29
My Active16
Needs Action26
In Review12

Tabs

All (63) · Pending · In Progress · Needs Action

Filters / view options

  • My Assignments toggle
  • Filter button · Sort button
  • View switcher: List · Grid · Table · Kanban

Request card fields (List view)

FieldNotes
TenantAvatar + name
Requested ByDM who submitted
TitleBlog title (linked)
KeywordsPrimary keyword chips
Request IDREQ[Date]#[Hash] format
CreatedCreation date
Due DateRed + “OVERDUE” if past due
Blog MonthTarget publication month
Assigned ToTeam member or Blog AI Agent
Status badgeDM Review · Pending · In Progress · Needs Action

Pending / Unassigned cards additionally show a Move to In Progress button.

Pagination: 10/page configurable. 63 requests → 7 pages observed.


Screen M13 — Request Detail (/request-queue/[requestId]) [Live]

Purpose: Full details and actions for a single blog request.

Header bar

← Back to Queue | [Request ID] | [Status badge] | v[N]

Title section

Tenant avatar + name · blog title (h1) · action buttons.

Action buttons

  • Copy Details — copies request info to clipboard
  • Copy Image Prompt — copies image generation prompt
  • Blog AI Agent (dropdown):
    • Reassign to Team Member: Assign to Me · Blog AI Agent · [named team members]
    • Move to On Hold
    • Move to Pending

Request Information section

FieldNotes
Primary KeywordsChip(s)
Required DateRed + “OVERDUE” if past due
Blog MonthTarget month
Requested ByDM name
Assigned ToAgent or team member
CreatedRelative time
Secondary KeywordsAdditional keyword chips

Feedback & Comments section

Threaded comments. Empty state: “No comments found”.


Screen M14 — All Blogs (/all-blogs) [Live]

Purpose: View and manage all generated blog posts across all lifecycle stages.

Header

  • “All Blogs — View and manage all blog posts across all stages”
  • Filter button · Sort button

Tabs

All (18) · DM Review · Client Review · Approved · Published · On Hold

Table columns

ColumnNotes
Title / KeywordBlog title + primary keyword chip
ClientTenant avatar + name
Submitted for ReviewDate + time
StatusDM Review · Client Review · Approved · Published · On Hold
Versionv1, v2, v3… (increments on revision)
ActionsEye icon → slide-over

Blog detail slide-over (row click or eye icon)

Right panel: Status · Version · Submitted for Review · Last Updated · Requested By · Assigned To · Keywords · Submission History timeline.


Screen M15 — System Health (/system) [To Build]

Purpose: Infrastructure and operational health monitoring.

  • Service status cards: Next.js (Dashboard / DM Portal / Manage) · Fastify API · PostgreSQL · MongoDB · Redis · Ollama — green/red with uptime %
  • Queue depths: BullMQ queue size per agent type · currently processing · failed jobs in DLQ
  • Database metrics: active connections · slow queries (> 500ms) · replication lag
  • Recent errors: last 20 system-level errors with service, message, timestamp
  • Active SSE connections: live client count per app

Quick actions:

  • Drain / pause a specific BullMQ queue
  • Clear DLQ (with confirmation)
  • Force re-sync token refresh for all tenants
  • Trigger manual DataForSEO backlink check run

Purpose: Platform-wide directory database management. Superadmins curate the list of business directories used by the opportunity-matcher agent when finding submission opportunities for tenants.

List page (/directories)

Stats row (5 tiles): Total · Active · Free · Dofollow · Agent Ready (violet — count of isAutoSubmittable = true entries).

Filters:

  • Search input — client-side; matches name, category, subcategory.
  • “Agent Ready” toggle button — filters list to isAutoSubmittable = true only (violet when active).

Table columns: Name · Category/Subcategory · DR · Link type · Difficulty · Regions · Free/Paid · Active · →

Name cell: Directory name + optional violet Bot badge (shown when isAutoSubmittable = true; title tooltip “Agent can auto-submit”) + external Globe link icon.

Row click → /directories/[id].

Detail / Edit page (/directories/[id])

Basic Info card fields: Name · URL · Category · Subcategory · Domain Rating · Link Type · Is Free · Is Paid · Paid Price (USD) · Regions · Difficulty · Estimated Minutes · Is Active · Is Auto-Submittable (checkbox) · Submission Notes (textarea — admin notes on CAPTCHA/login requirements for this directory).

Steps card: Ordered list of steps (title, description, URL, estimated minutes) for manual submission; also used by the directory-submitter agent as the navigation hint for step 1.

API: PATCH /api/admin/directories/[id]PATCH /admin/v1/backlink-directories/:id.

Create page (/directories/new)

Same fields as detail page. POST /api/admin/directoriesPOST /admin/v1/backlink-directories.

isAutoSubmittable behaviour

When isAutoSubmittable = true, the directory-submitter Playwright agent will attempt to autonomously fill and submit the listing form when a DM sets an opportunity backlink to in_progress. The agent detects CAPTCHA, login walls, and OTP fields and marks submissionStatus = "failed" with a reason if it can’t proceed — the human then follows the manual steps[] guide.


Help Center (/help + /help/*) [Live]

Purpose: In-app documentation for all Manage portal features, accessible to super admins.

Index page (/help):

  • Directory of 15 help articles grouped by category (Tenants & Users · AI Agents & Skills · Billing · Notifications & Templates · Content Management · System)
  • Client-side full-text search across all article content

Topic pages:

RouteTopic
/help/tenantsTenants — lifecycle, detail tabs, creating tenants
/help/usersUsers — roles, inviting admins and DMs
/help/agentsAgents — registry, categories, runs, analytics tab
/help/skillsSkills — types, assigning to agents
/help/invoicesInvoices — lifecycle, fields
/help/plansPlans — subscription tiers, multi-subscription
/help/usageCredits & Usage — consumption, balance adjustments
/help/costsLLM Costs — model/agent/tenant breakdown
/help/templates/emailEmail Templates — variables, editing
/help/templates/telegramTelegram Templates — format, event-driven
/help/blog-postsBlog Posts — cross-tenant view, status flow
/help/social-postsSocial Posts — cross-tenant view, status flow
/help/push-notificationsPush Notifications — broadcast, FCM
/help/audit-logsAudit Logs — fields, search, export
/help/rag-configRAG & AI Config — datasets, adapters, re-indexing

Architecture: Data in apps/manage/src/app/(manage)/help/_data/index.ts; shared HelpPage/HelpCenter components from @leadmetrics/ui.


Screen M17 — Important Days (/system/important-days) [Live]

Purpose: Platform-wide catalogue of public holidays, observances, and cultural occasions used to trigger occasion post suggestions for tenants.

List page (/system/important-days) [Live]

Filters (left sidebar):

  • Region dropdown — “All”, or ISO-3166 country code (IN, US, AU, GB, etc.)
  • Month dropdown — “All”, Jan – Dec
  • Active only toggle

Table columns: Name · Date · Scope (Global / country codes) · Platforms · Active toggle

Row click → opens edit modal (inline edit, not a separate page).

“Add Day” button (top-right) — opens create modal.

Create / Edit modal

Fields:

  • Name (text) — required
  • Description (textarea) — optional
  • Month (1–12) / Day (1–31) — required
  • Year — optional; null = recurring annually
  • Is Global checkbox — if checked, applies to all tenants regardless of country
  • Countries multi-select — ISO-3166 codes (only relevant when not global)
  • Platforms checkboxes — default platforms to suggest for this occasion (instagram, linkedin, facebook, google_business_profile, x)
  • Active toggle

Seeded occasions

15 occasions pre-loaded in packages/db/src/seed.ts:

OccasionDateScope
New Year’s DayJan 1Global
Republic DayJan 26IN
Valentine’s DayFeb 14Global
Women’s DayMar 8Global
Earth DayApr 22Global
Labour DayMay 1Global
Independence DayAug 15IN
Gandhi JayantiOct 2IN
Children’s DayNov 14IN
ChristmasDec 25Global
New Year’s EveDec 31Global
(+ 4 more India-specific)variesIN

API

All manage routes require super_admin role.

MethodRouteDescription
GET/admin/v1/important-daysList with optional ?month=&country=&active= filters
POST/admin/v1/important-daysCreate a new day
PATCH/admin/v1/important-days/:idUpdate fields
DELETE/admin/v1/important-days/:idDelete a day

Files

FileNotes
apps/manage/src/app/(manage)/system/important-days/page.tsxServer component, fetches list
apps/manage/src/app/(manage)/system/important-days/ImportantDaysClient.tsxClient component, modals, table
apps/manage/src/app/actions/important-days.tsServer actions (create/update/delete)
apps/api/src/routers/admin/important-days.tsFastify CRUD routes
packages/db/prisma/schema.prismaImportantDay model

Screen M18 — Autonomy (/system/deliverable-settings) [Live — May 2026]

Purpose: Global toggles that control which content types require a DM human review step before proceeding to the next pipeline stage. All toggles default to ON (approval required). Disabling a toggle lets the agent pipeline skip straight to the next automated stage without waiting for a human reviewer.

Nav label: “Autonomy” (under System group, Package icon).

Toggles

SectionPlatformSetting keyDefaultSkips to (when OFF)
Activity Pipeline — require DM approvalrequireActivityApprovaltrueAgents dispatch immediately after planning
Activity Pipeline — max activities per periodactivityPlannerMaxActivities300(number cap, not a toggle)
Blog PostsrequireBlogApprovaltrueclient_review
Social Posts (FB / IG / LI / GBP)requireSocialApprovaltrueclient_review
Landing PagesrequireLandingPageApprovaltrueclient_review
Email NewslettersrequireEmailApprovaltrueapproved

Behaviour note for social posts: when requireSocialApproval = false, posts skip DM review AND the design step (Social Post Designer) since that step requires a DM-selected channel. The post lands directly in client_review as text-only copy.

Data flow

  • Settings stored in PlatformSetting table (key-value, value is Json).
  • Workers read their key via db.platformSetting.findUnique at job execution time — no restart needed.
  • Default logic in workers: approvalSetting?.value !== false → approval ON when no DB row exists.

API

  • GET /admin/v1/deliverable-settings — returns all 6 keys merged with defaults
  • PATCH /admin/v1/deliverable-settings — upserts each provided key; writes audit log

Key files

FileRole
apps/manage/src/app/(manage)/system/deliverable-settings/page.tsxServer component, fetches settings
apps/manage/src/app/(manage)/system/deliverable-settings/DeliverableSettingsClient.tsxClient, toggle + number input UI
apps/manage/src/app/actions/deliverable-settings.tsServer actions (getDeliverableSettings, saveDeliverableSettings)
apps/api/src/routers/admin/deliverable-settings.tsFastify GET + PATCH routes
packages/agents/src/workers/activity.worker.tsReads requireActivityApproval
packages/agents/src/workers/blog-writer.worker.tsReads requireBlogApproval
packages/agents/src/workers/social-post-writer.worker.tsReads requireSocialApproval
packages/agents/src/workers/gbp-post-writer.worker.tsReads requireSocialApproval
packages/agents/src/workers/landing-page-writer.worker.tsReads requireLandingPageApproval
packages/agents/src/workers/email-writer.worker.tsReads requireEmailApproval

Background agents (not screens)

Two system agents appear in Manage app context but run in the background:

AgentRole
Blog AI AgentGenerates blog drafts for Request Queue items; shows as assignee in M12/M13
Billing AgentAuto-creates invoices; shows as actor in invoice audit trail

© 2026 Leadmetrics — Internal use only