← Back to user manual

Flow Analyzer — platform administrator manual

This guide is for the person who runs, deploys, and hardens Flow Analyzer: SaaS operators, DevOps engineers, and on-prem installers. It explains what you own on the host, how that differs from customer org admins, and where to find deeper runbooks.

Related docs: Deployment · Operator runbook · End-user SaaS manual · Organization administrator manual

In the app: sign in with the platform admin account configured via FLOW_ANALYZER_PLATFORM_ADMIN_EMAIL / FLOW_ANALYZER_PLATFORM_ADMIN_PASSWORD, then open `/admin`. The console includes dashboard, organizations, users, billing trace, support inbox, satisfaction, engagement board, public demo limits, connector environment status, and usage analytics. End-user help also lives under Help & guide in the product header.


Your role vs organization administrators

ResponsibilityPlatform administrator (you)Organization administrator (customer)
Install or upgrade the appYesNo
Postgres, TLS, reverse proxy, cron schedulersYesNo
Deployment-wide secrets (AUTH_SECRET, CLICKUP_*, ASANA_*, LINEAR_*, TRELLO_API_KEY, Jira OAuth app, cron bearers)YesNo
Register ClickUp / Atlassian / Asana / Linear OAuth apps once per deploymentYesUses the apps you registered
Create organizations (unless self-serve registration is on)SometimesSelf-serve when enabled
Add Jira, ClickUp, Asana, Linear, or Trello connections inside an orgNoYes
Pick source scope (lists, projects, boards) and workspace configNoYes
Invite teammates, run day-to-day analysisNoYes

Think of yourself as the landlord: you keep the building safe, powered, and reachable. Org admins are tenants: they furnish their floor (connections, workspaces, people) and use the product.


Prerequisites

Before you deploy Flow Analyzer for multi-tenant SaaS:

RequirementNotes
Node.jsVersion 20 or newer (see repo package.json).
PostgreSQLRequired when FLOW_ANALYZER_SAAS_ENABLED=true. Snapshots, findings, tenant jobs, and encrypted credentials live here.
Docker (optional)Recommended for production: root Dockerfile, docker-compose.yml, and deploy scripts under scripts/. You can also run Node directly on a host.
TLS terminationReverse proxy or CDN with valid certificates; forward X-Forwarded-Proto: https and X-Forwarded-Host.
Outbound HTTPSJira Cloud (*.atlassian.net), ClickUp API (api.clickup.com), Asana API (app.asana.com), Linear GraphQL (api.linear.app), Trello API (api.trello.com), optional SMTP, optional HubSpot — see Egress notes.
SchedulerCron or platform jobs for live-workspace poll, usage snapshots, and report delivery when those features are enabled.

Local development defaults: Next.js on port 4300 (npm run dev from repo root). Production Docker maps container port 3000 to a host loopback port (default 3041 via FLOW_ANALYZER_HOST_PORT).


Deployment overview

Flow Analyzer ships as a Next.js application (apps/web) with in-process API routes. Target topology:

```

Internet → TLS (Traefik / Nginx) → Next.js → PostgreSQL

optional → Redis

```

Do not duplicate full procedures here. Use these canonical references:

TopicDocument
Topology sketch, Contabo VPS, FlowOS shared stack, Redisdeployment.md
On-prem single-host Compose (docker-compose.onprem.yml)deployment.md — On-prem single-host profile
Health checks, RLS, crons, security hardening, ClickUp operator notesoperator-runbook.md
Env templateapps/web/.env.example
Docker migrate after deployscripts/docker/docker-migrate-deploy.sh (invoked by deploy scripts after docker compose up)

Typical production path:

  1. Create apps/web/.env on the server (never commit secrets).
  2. Build and start the container or Node process.
  3. Run `prisma migrate deploy` via docker-migrate-deploy.sh or your approved migration path after the app is up (deploy scripts do this automatically).
  4. Verify `GET /api/health` and `GET /api/ready`.
  5. Configure schedulers for poll and usage crons when SaaS is enabled.

On-premise deployment profile

Use this when Flow Analyzer runs inside the customer network (air-gapped or self-managed) rather than as vendor-hosted SaaS. The application is the same codebase; behavior is controlled by `FA_DEPLOYMENT_MODE=onprem`.

When to choose on-prem

ScenarioTypical choice
Jira Data Center on a private networkOn-prem Flow Analyzer on the same network (primary DC story)
Corporate policy forbids SaaS egress to work toolsOn-prem with internal IdP and optional offline license
Customer-managed Postgres and backupsOn-prem Compose profile or your own orchestration

Hosted SaaS uses `FA_DEPLOYMENT_MODE=saas` (default). Do not set per-user overrides for SSRF or TLS — deployment mode is instance-wide.

Install (Compose)

Canonical steps live in deployment.md — On-prem single-host profile. Summary:

  1. Copy apps/web/.env.example → apps/web/.env and set production secrets (AUTH_SECRET, FLOW_ANALYZER_SAAS_CREDENTIALS_KEY, public URLs).
  2. Set `FA_DEPLOYMENT_MODE=onprem` (already set in docker-compose.onprem.yml).
  3. Start: docker compose -f docker-compose.onprem.yml up -d --build
  4. Migrate: docker compose -f docker-compose.onprem.yml exec web /app/docker-migrate-deploy.sh
  5. Terminate TLS at Nginx/Traefik on loopback port `FLOW_ANALYZER_HOST_PORT` (default 3041).

The profile includes web + Postgres 18; add `--profile redis` when you set REDIS_URL. Analysis jobs run in-process inside the web container today (no separate worker service).

On-prem environment variables

VariablePurpose
FA_DEPLOYMENT_MODEonprem — relaxes SSRF for internal targets, enables license enforcement, turns product telemetry off by default
FA_LICENSE_FILEPath to vendor-signed offline license JWT (Ed25519). Required for continued new analyses on production on-prem
FA_LICENSE_PUBLIC_KEY_PEM or FA_LICENSE_PUBLIC_KEY_FILEPublic key used to verify the license file (offline; no phone-home)
FA_SSO_BOOTSTRAP_FILEOptional JSON applied on server boot to materialize instance IdP before any UI login — see SSO migration guide
FA_ALLOW_INSECURE_TLSOn-prem only: allow self-signed / internal CA for Jira DC or IdP targets (logged at startup)
FLOW_ANALYZER_PRODUCT_TELEMETRYSet true to opt in to product audit JSONL on on-prem (off by default)
FLOW_ANALYZER_ONPREM_PG_PASSWORDPostgres password for the bundled Compose profile

Signing a license (vendor / internal): use scripts/license/sign-on-prem-license.mjs with a private Ed25519 key; distribute only the .jwt file and install the matching public key on the host. See deployment.md for backup, upgrade, and sizing notes.

License lifecycle (on-prem)

StateProduct behavior
Valid licenseNormal operation — analyses, ingest, and dashboards
Expired or missing licenseRead-only grace: users can still sign in and view existing snapshots and findings; a yellow banner explains the situation; new analyses are paused (API returns a clear error; scheduled poll/webhook enqueue is skipped)
Invalid signatureSame as missing — fix the license file or public key

This is intentional: no data loss on expiry; renew the license file and restart is not required for read paths.

First-run bootstrap

On a completely empty database (after migrate), you can materialize the first organization and a break-glass org admin without using self-serve registration. The server applies this once per process on startup when FA_DEPLOYMENT_MODE=onprem and bootstrap config is present. If any company already exists, bootstrap is skipped (idempotent).

Option A — JSON file (recommended):

Set FA_ONPREM_BOOTSTRAP_FILE to a path mounted into the container, for example /etc/flow-analyzer/instance-bootstrap.json:

```json

{

"version": "1.0",

"company": {

"name": "ACME Corporation",

"billing_reference": "ACME-2026",

"max_seats": 50,

"maxsourceconnections": 5

},

"breakglassadmin": {

"email": "admin@acme.local",

"display_name": "Platform Administrator",

"password": "ChangeMeNow123!"

}

}

```

Option B — environment variables:

VariableRequiredPurpose
FA_ONPREM_BOOTSTRAP_COMPANY_NAMEYesFirst organization name
FA_ONPREM_BOOTSTRAP_ADMIN_EMAILYesBreak-glass admin email (also used for normal sign-in before SSO enforcement)
FA_ONPREM_BOOTSTRAP_ADMIN_PASSWORDYesMin. 10 characters; stored hashed; also enables break-glass login
FA_ONPREM_BOOTSTRAP_ADMIN_DISPLAY_NAMENoDefaults to the email local part
FA_ONPREM_BOOTSTRAP_MAX_SEATSNoDefault 50
FA_ONPREM_BOOTSTRAP_MAX_SOURCE_CONNECTIONSNoDefault 5

SSO IdP bootstrap from FA_SSO_BOOTSTRAP_FILE runs after instance bootstrap on the same startup. Use it to pre-configure OIDC/SAML before anyone signs in through the UI. See SSO migration guide.

Security: treat bootstrap passwords like root credentials — store them in your secret manager, rotate after first sign-in, and prefer SSO enforcement once the IdP is verified.


Environment variables by category

All variables below belong in `apps/web/.env` (or your secret manager). See apps/web/.env.example for the full list and comments.

Authentication and public URL

VariablePurpose
FLOW_ANALYZER_SAAS_ENABLEDSet true for multi-tenant mode (required for org settings, live connectors, registration).
AUTH_SECRET or NEXTAUTH_SECRETMandatory in production SaaS. Long random string for session JWTs. Missing value → 503 on all routes except health/ready.
NEXTAUTH_URLPublic origin for Auth.js callbacks (e.g. https://flowanalyzer.example.com).
FLOW_ANALYZER_PUBLIC_BASE_URLCanonical browser URL for email links, CORS, and cross-site checks.
FLOW_ANALYZER_EXTRA_ALLOWED_ORIGINSOptional comma-separated extra origins (www vs apex, staging alias).

Enterprise SSO (when your deployment enables it): identity provider configuration is stored in the database; bootstrap options are described in SSO migration guide. Org users see Continue with SSO on the sign-in page when their company has SSO configured.

Database

VariablePurpose
DATABASE_URLPostgreSQL connection for the runtime app user (subject to RLS in SaaS).
FLOW_ANALYZER_MIGRATE_DATABASE_URLOptional override for migration-only credentials when runtime user cannot run DDL.
FLOW_ANALYZER_SKIP_MIGRATE_ON_STARTSet 1 to skip docker-migrate-deploy.sh (manual migrate workflows only).

Pooler warning: Use session pooling or a direct Postgres connection. Transaction-mode poolers (e.g. PgBouncer pool_mode=transaction) can break transaction-local tenant GUCs and self-serve registration.

SaaS feature flags

VariablePurpose
FLOW_ANALYZER_DATA_SOURCEDefault empty \seed \live when no workspace context (usually seed for demos).
FLOW_ANALYZER_ALLOW_SOURCE_UItrue only in staging/demo — in-app data-source switcher. Disabled automatically in SaaS for signed-in users.
FLOW_ANALYZER_TENANT_INGEST_MAX_ISSUESCap per ingest job (default 500, max 5000).
FLOW_ANALYZER_EVALUATION_NOTES_UISet 0 to hide Evaluation notes hub.

Credential encryption (per-organization secrets at rest)

VariablePurpose
FLOW_ANALYZER_SAAS_CREDENTIALS_KEYEncrypts Jira PATs, ClickUp tokens, and OAuth bundles stored per org. Use a 64-character hex key (openssl rand -hex 32) or a strong passphrase. Rotating this key invalidates stored credentials until org admins re-save connections.

Jira OAuth (deployment-wide, optional)

Required only when org admins should connect Jira via OAuth 2.0 instead of (or in addition to) personal API tokens.

VariablePurpose
FLOW_ANALYZER_JIRA_OAUTH_CLIENT_IDAtlassian OAuth app client ID.
FLOW_ANALYZER_JIRA_OAUTH_CLIENT_SECRETClient secret (keep secure).
FLOW_ANALYZER_JIRA_OAUTH_REDIRECT_URIMust match the Atlassian app redirect, e.g. https://your-domain.com/api/auth/jira-callback.

Register the app at Atlassian Developer Console. Org admins still choose site and scope in the product; you only supply the OAuth app credentials once per deployment.

ClickUp OAuth (deployment-wide, optional)

Required when org admins should use Connect with ClickUp (OAuth). Personal API token connections do not need these variables.

VariablePurpose
CLICKUP_CLIENT_IDClickUp app client ID.
CLICKUP_CLIENT_SECRETClickUp app client secret (keep secure).
CLICKUP_OAUTH_REDIRECT_URIMust match the ClickUp app redirect, e.g. https://your-domain.com/api/integrations/clickup/oauth/callback.

See Registering ClickUp OAuth below.

Webhooks, crons, and automation

VariablePurpose
FLOW_ANALYZER_JIRA_WEBHOOK_SECRETOptional global Jira webhook signing secret (org-level secrets can also be stored per connection).
FLOW_ANALYZER_JIRA_POLL_CRON_SECRETBearer for `/api/cron/jira-live-workspaces-poll` — safety-net ingest for jira_live and clickup_live workspaces (schedule at least hourly).
FLOW_ANALYZER_USAGE_CRON_SECRETBearer for `/api/cron/saas-usage-daily`.
FLOW_ANALYZER_REPORT_CRON_SECRETBearer for scheduled report delivery cron.
FLOW_ANALYZER_RETENTION_CRON_SECRETBearer for `/api/cron/data-retention` (password-reset tokens, old tenant jobs, audit JSONL TTL, Flow Story signal age purge).
FLOW_ANALYZER_FLOW_SIGNALS_RETENTION_DAYSOptional Flow Story signal TTL by occurredAt (default 90; bounds 1–3650).
FLOW_ANALYZER_HEADLESS_API_TOKENBearer for automation (/api/analysis/jobs, findings, export) without a browser session.

Rotate cron and headless secrets in one maintenance window with your schedulers.

Email (optional)

FLOW_ANALYZER_REPORT_SMTP_* and FLOW_ANALYZER_REPORT_EMAIL_FROM power password reset, invitations (when sendEmail=true), scheduled reports, and the contact form. Without SMTP, those features degrade gracefully (tokens shown in UI for invites; password reset does not send mail).

Optional infrastructure

VariablePurpose
REDIS_URLOptional — hot snapshot L2 cache, shared API-key/MCP rate limits, tenant-job lock. Not required for single-process operation. Health check reports redis status.
FLOW_ANALYZER_SNAPSHOT_RAW_ISSUES_*Large snapshot offload to object storage (see Jira persistence allow-list).

Database migrations

Flow Analyzer uses Prisma migrations under prisma/migrations/. In production you apply them with:

```bash

prisma migrate deploy

```

In Docker production, deploy scripts run `/app/docker-migrate-deploy.sh` after the container is up (see deployment.md).

Safety rules

  1. Never run `prisma migrate reset` on production. That destroys data.
  2. Never use `db push --force-reset` on a database that holds customer data.
  3. Prefer `migrate deploy` on a reviewed migration history; follow your organization's change-control process.
  4. If migrate and runtime users differ, set `FLOW_ANALYZER_MIGRATE_DATABASE_URL` for the migrate script only.
  5. After RLS phase-2 migrations, ensure pending migrations are fully applied before declaring the site healthy — see operator runbook — PostgreSQL RLS for symptom → fix tables.

What you should expect: migrations add columns, RLS policies, and helper functions. They do not delete tenant snapshots or findings when applied correctly.


Multi-tenant security (high level)

SaaS mode stores each company's data in PostgreSQL with row-level security (RLS) on tenant tables (fa_user, fa_company, fa_workspace, snapshots, findings, jobs, connections, and related tables).

Before tenant queries, the application sets transaction-local session variables (flow_analyzer.company_id, flow_analyzer.workspace_id, flow_analyzer.app_user_id, and others) inside short Prisma transactions. That limits blast radius if application code omits a WHERE company_id = … clause.

Your responsibilities:

  • Run the app with a database user that is subject to RLS at runtime (not a superuser bypass).
  • Use a migration-capable role only for migrate deploy.
  • Terminate TLS and protect AUTH_SECRET, FLOW_ANALYZER_SAAS_CREDENTIALS_KEY, and OAuth client secrets.
  • Schedule trusted crons with bearer secrets; they use separate trusted-transaction helpers.

Not a substitute for app checks: RLS is defense in depth. Route handlers still enforce org membership and workspace scope.

Details: operator runbook — PostgreSQL RLS · SaaS Prisma tenant read scope · NFR-03 security checklist.


Registering a ClickUp OAuth app

Org admins connect ClickUp through Organization and workspaces → Source connections or the Connect ClickUp wizard. OAuth requires you to register one ClickUp app per Flow Analyzer deployment.

Steps

  1. In ClickUp, create an OAuth app (ClickUp developer settings).
  2. Set the redirect URI to your public URL plus the callback path, matching CLICKUP_OAUTH_REDIRECT_URI, for example:
  • https://flowanalyzer.example.com/api/integrations/clickup/oauth/callback
  1. Copy client ID and client secret into CLICKUP_CLIENT_ID and CLICKUP_CLIENT_SECRET in apps/web/.env.
  2. Set CLICKUP_OAUTH_REDIRECT_URI to the exact same redirect URI registered in ClickUp.
  3. Restart the app. Org admins can now choose Connect with ClickUp when adding a connection.

Notes

  • ClickUp OAuth tokens do not expire and there is no refresh token — revoked tokens show as 401 on connection health checks.
  • Personal API token path works without OAuth env vars; org admins paste a token and Team ID in the add-connection dialog.
  • Inbound ClickUp webhooks hit your deployment at a URL scoped per connection; signatures use HMAC-SHA256 (X-Signature). Outbound registration uses the stored OAuth or PAT.

Operator detail: operator runbook — ClickUp connector.


Registering Jira / Atlassian OAuth

Same pattern as ClickUp, but for Atlassian Cloud:

  1. Create an OAuth 2.0 (3LO) app at developer.atlassian.com.
  2. Set redirect URI to match FLOW_ANALYZER_JIRA_OAUTH_REDIRECT_URI, e.g. https://your-domain.com/api/auth/jira-callback.
  3. Set FLOW_ANALYZER_JIRA_OAUTH_CLIENT_ID and FLOW_ANALYZER_JIRA_OAUTH_CLIENT_SECRET.
  4. Restart the app.

Org admins can still use API token + email without OAuth env vars. Both auth methods work with the Jira configuration wizard and live ingest.

Webhook freshness: Jira webhooks and data freshness.


Registering a Linear OAuth app

Org admins connect Linear through Organization and workspaces → Source connections or the Connect Linear wizard. OAuth requires you to register one Linear OAuth app per Flow Analyzer deployment.

  1. Create a Linear OAuth app with redirect URI matching LINEAR_OAUTH_REDIRECT_URI (default path: /api/integrations/linear/oauth/callback on your public origin).
  2. Set LINEAR_CLIENT_ID, LINEAR_CLIENT_SECRET, and LINEAR_OAUTH_REDIRECT_URI in apps/web/.env.
  3. Set FLOW_ANALYZER_PUBLIC_APP_URL so webhook registration uses the correct public origin.
  4. Restart the app.

Personal API key connections work without OAuth env vars; org admins paste a key from Linear settings.

Operator detail: operator runbook — Linear connector.


Trello API key (deployment-wide)

Org admins connect Trello with a personal API token per organization. You must set the deployment Power-Up API key once:

  1. Create or reuse a Trello Power-Up at trello.com/power-ups/admin and copy the API key.
  2. Set TRELLO_API_KEY in apps/web/.env (optional TRELLO_OAUTH_SECRET reserved for future OAuth).
  3. Restart the app. Org admins paste member tokens from trello.com/app-key when adding a connection.

Inbound Trello webhooks use HEAD then POST to /api/webhooks/trello/{connectionId} on your public origin.

Operator detail: operator runbook — Trello connector.


Egress, air-gap, and SaaS-only connectors

Some connectors talk only to vendor cloud APIs. On an on-prem or air-gapped host, the app probes outbound HTTPS before validation and returns a clear `egress_required` message instead of a long timeout.

Hosts to allow (outbound HTTPS)

ConnectorEgress hostNotes
ClickUpapi.clickup.comCatalog, tasks, time entries, webhooks register
Asanaapp.asana.comREST API (/api/1.0)
Jira Cloud*.atlassian.netPer site URL
Linearapi.linear.appGraphQL
Trelloapi.trello.comREST
ScrumDesk Startapi.scrumdesk.comCloud API only

Jira Data Center on your LAN does not require internet egress when Flow Analyzer is on-prem on the same network.

Jira Data Center connectivity and egress

Self-hosted Jira uses HTTPS REST v2 on your base URL (context paths and ports 443 / 8443 on SaaS). Three topologies are supported — see deployment.md — Jira Data Center connectivity:

TopologyWhen to useOperator notes
On-prem Flow Analyzer (recommended)Jira and Flow Analyzer share the customer LANSet FA_DEPLOYMENT_MODE=onprem — SSRF guard relaxed for internal targets; webhooks from Jira to Flow Analyzer work when inbound HTTPS reaches the app
SaaS + customer-exposed JiraHosted Flow Analyzer, Jira published to the internetAllowlist Flow Analyzer egress IPs (FA_SAAS_EGRESS_IPS or runbook list); only HTTPS ports 443 and 8443; private/metadata IPs blocked
Private link / VPNSales-assisted enterpriseMay still fail SSRF if Jira resolves to RFC1918 from the SaaS egress path — validate with a test connection

TLS for internal CAs: on-prem installs may trust a custom CA via NODE_EXTRA_CA_CERTS or per-connection CA material in providerConfigJson. FA_ALLOW_INSECURE_TLS=true is on-prem only and logs a warning at every process start — it is not available in SaaS mode.

Unreachable targets: connection validation returns topology guidance linking to deployment.md — not a generic 502. Org admins see the same copy in the UI when a DC URL cannot be reached, with links to this section and Organization and workspaces → Enterprise readiness.

Procurement pack: enterprise-procurement-pack.md — topologies, egress, SSO, version matrix, offline license, clean-room checklist.

Auth stored: PAT and Basic credentials follow the same encryption rules as other source connections (AES-256-GCM at rest, never logged).

Inbound HTTPS (webhooks and OAuth)

Allow inbound HTTPS to your public origin for:

  • OAuth callbacks (ClickUp, Asana, Jira Cloud, Linear, …).
  • Provider webhooks (POST to your deployment's per-connection webhook URLs).
  • Jira Data Center webhooks when Flow Analyzer is on-prem and Jira can reach your internal or published app URL.

If your network blocks arbitrary egress, document the outbound allow-list for security review. Org admins who try ClickUp or Asana on a host without egress see a message like: "This connector requires internet egress to api.clickup.com" — that is expected; either open the firewall or choose Jira DC / internal sources only.

ClickUp-specific operator notes

From the app host or container, allow outbound HTTPS to:

  • https://api.clickup.com/api/v2 — catalog, tasks, bulk time-in-status, time entries, webhook register/delete.

Rate limiting: the connector defaults to 80 requests/minute per connection (headroom under ClickUp's plan limits). 429 responses trigger bounded retries with Retry-After / X-RateLimit-Reset.

Operator detail: operator runbook — ClickUp connector.

Monitoring and operations

Health endpoints

EndpointUse
GET /api/healthProcess up; includes optional redis status.
GET /api/readySnapshot resolution succeeded — use after deploy when config is stable.

Connection and job health (operator view)

  • Structured logs: successful analysis runs emit flow_analyzer.analysis_run_completed JSON lines (job id, duration, finding counts). See operator runbook — Structured logs.
  • Security audit: SaaS sensitive actions append to audit/saas-security.jsonl under the data directory.
  • Tenant jobs: live ingest and rules runs are tracked as FaTenantAnalysisJob rows; org admins see connection-level errors in Organization settings; you can correlate with DB and logs.

Org-facing connection columns (status, last validated, last webhook) are described in the organization administrator manual.

Performance regression gate

Before major connector or ingest releases, run the NFR harness:

```bash

npm run perf:nfr01

```

Targets and connector regression rules: performance and scalability (NFR).

Scheduled jobs you should configure (SaaS)

JobSchedule (typical)Secret
Live workspace pollEvery 30–60 minutesFLOW_ANALYZER_JIRA_POLL_CRON_SECRET
Daily usage snapshotsOnce per UTC dayFLOW_ANALYZER_USAGE_CRON_SECRET
Report deliveryPer product docFLOW_ANALYZER_REPORT_CRON_SECRET
Data retentionOnce per UTC day (recommended)FLOW_ANALYZER_RETENTION_CRON_SECRET

Example poll call (replace host and secret):

```bash

curl -sS -X GET 'https://your-host.example/api/cron/jira-live-workspaces-poll' \

-H 'Authorization: Bearer YOURJIRAPOLLCRONSECRET'

```

Release checklist (short)

  • npm run lint and relevant tests on the release branch.
  • npm run security:audit for dependency advisories.
  • Apply migrations on staging before production.
  • Verify health, sign-in, and one live ingest path (Jira or ClickUp) after deploy.


Platform admin console (/admin)

After Slice 2, operators with env-configured platform admin credentials can use:

RoutePurpose
/adminCross-tenant dashboard (orgs, users, workspaces, subscriptions)
/admin/analyticsUsage trends (registrations, jobs, logins), activation funnel, rule family noise, and in-product funnel

Scenario prediction calibration (JSON, #174): GET /api/admin/analytics/scenario-calibration?days=90 — platform-admin only; PII-free cohort % inside_band for D5 (≥80% goal). See `doc/quality/scenario-prediction-calibration.md`.

Activation funnel (SaaS onboarding)

For who: Platform operators and customer-success staff using /admin.

Why: Daily registration totals alone do not show where new organizations stall after sign-up. The activation funnel answers “which orgs registered this period never connected a live source / never ran analysis / never got a finding?” so you can outreach before churn.

Where: Open `/admin/analytics`. The Activation funnel panel shares the same 7 / 30 / 90 day preset as usage growth charts. It is separate from the In-product funnel panel (authenticated product events).

What the five stages measure (org cohort = companies with createdAt in the selected range):

  1. Registered — company row exists in the cohort.
  2. First workspace — at least one non-deleted workspace.
  3. First live connection — at least one source connection with lastValidatedAt set (seed-only without validation does not advance).
  4. First analysis job — at least one tenant analysis job (any status).
  5. First finding — at least one persisted AnalysisFinding via workspace snapshots.

Each stage shows org count and conversion % from the previous stage. Click a stage with count > 0 to open a sortable org list; org names deep-link to /admin/organizations/[id]?tab=… (workspaces for stages 2–3, overview for stages 4–5). Zero-count stages show explanatory empty copy (not an error).

Org overview health: On organization detail Overview, the health strip shows member count, validated connection summary (and last error snippet when present), last analysis job status, and a sortable recent-jobs table. Use `?tab=billing` to open the billing tab directly.

Rule family noise (diagnosis quality)

For who: Platform operators and customer-success staff using /admin (not a customer Findings surface).

Why: After coaches dismiss noisy findings with a reason, operators still could not answer “which catalogue families fire the most and get dismissed the most across tenants?” This panel turns that into aggregate evidence for diagnosis-quality work and catalogue tuning — without reading tenant PII.

Where: Open `/admin/analytics`. The Rule family noise panel sits between usage/activation charts and the In-product funnel. It has its own 7 / 30 / 90 day preset chips and optional organization / workspace filters. It does not change the Activation or In-product funnel panels.

What the metrics mean:

MetricDefinition
WorkspacesNon-deleted workspaces whose latest persisted snapshot persistedAt falls in the selected range (after optional org/workspace filters).
FiresAnalysisFinding rows on those latest snapshots, rolled up by ruleFamilyFromRuleId (prefix before -).
DismissalsActive finding-triage rows with status wont_fix for the same workspace scope.
Dismissal ratedismissalCount / fireCount (shown as “—” when fires are zero).
Top 10 tableFamilies ranked by fire volume (ties: higher dismissal rate, then family code). Optional dismiss-reason counts only.

Privacy: Aggregate family codes, labels, counts, rates, and reason-code counts only. The UI and GET /api/admin/analytics/rule-family-noise JSON never include issue keys, people labels, emails, or dismiss notes. Use Copy JSON / Download JSON for the same payload as the table.

Empty / error: No snapshots in scope → explanatory empty copy. Load failure → retry message (no stack traces). Non–platform-admin callers receive 401/403.

/admin/supportLive support inbox (org users via /api/support/conversations)
/admin/satisfactionIn-app survey responses and NPS-style charts
/admin/engagementCustomer-success Kanban by lifecycle stage
/admin/public-tryAnonymous demo workspace caps and audit
/admin/connectorsDeployment OAuth env status (Jira, ClickUp, Asana, Linear, Trello)

Apply migration `20260725130000_platform_admin_slice2` before using support/survey tables (operator approval required). For self-serve registration billing profile inserts, also apply `20260725140000_registration_billing_profile_insert`.

Freemium organizations only run the curated rule subset documented in `doc/reference/FREEMIUM_RULE_TIER.md`; consultant and enterprise plans use the full catalogue at analysis time.


What platform administrators do not do

To set expectations with customers:

  • You do not pick ClickUp lists, Jira projects, or JQL scope — org admins do that in wizards and workspace settings.
  • You do not run analysis for end users or interpret findings — that is delivery/coaching work inside the org.
  • You do not manage org membership (except when you operate a single-tenant internal pilot).
  • You do not store org API tokens in tickets or chat — org secrets stay encrypted in Postgres; deployment secrets stay in your secret manager.

When someone reports "my workspace has no findings," triage connection health and failed jobs with them using the organization administrator manual; escalate to engineering only after confirming deployment version, migrations, and cron health.


AudienceDocument
Customer org adminsorganization-administrator-manual.md
All SaaS users (coaches, analysts)user-manual-saas.md
Deploy mechanicsdeployment.md
Day-2 operationsoperator-runbook.md
Tenant job runnersaas-tenant-analysis-job-runner.md

Organization billing reference (#196)

Who: Platform operators matching invoices to CRM / AP records.

Why: Org admins can leave an optional purchase-order or customer reference at Create organization or under Organization and workspaces → Organization.

Where: Organization detail → Overview (/admin/organizations/[companyId]) → Organization profile → Billing reference. The value is the same company field (not a Stripe customer id); edit with the existing details save.


Billing dunning and soft org suspension (#153)

Who: Platform operators and customer success using `/admin`.

Why: Surface overdue invoices and pause unpaid orgs without hard-blocking login (abuse path stays separate).

Where: Organization detail → Billing tab (/admin/organizations/[companyId]?tab=billing).

ActionEffect
Overdue alertShows when any invoice’s effective status is overdue (count, oldest due date, last billing audit event or empty copy).
Suspend (AlertDialog)Sets billing profile subscriptionStatus to suspended. Members can still sign in; a persistent banner explains limited access. Analysis enqueue and seat-consuming invites/SSO JIT return 403 with error_code: organization_suspended. Existing product data remains readable. Does not set FaCompany.isBlocked.
Restore (AlertDialog)Restores prior subscription status from the last suspend audit metadata. Banner clears on the next navigation; analysis enqueue works again (subject to other entitlements).
AuditEach suspend/restore writes FaBillingEvent (billing.access.suspended / billing.access.restored) and a platform-admin audit row. Soft-suspend never deletes workspace snapshots or invoices.

Contrast — hard block: Organization Block on the overview/details form sets isBlocked and denies credentials login (/account-blocked). Use hard block for abuse/security; use soft-suspend for unpaid billing.

Operator checklist (non-production first): Suspend a test org → confirm member banner + analysis 403 → Restore → confirm banner gone and enqueue allowed.


Schedule organization deletion (#265)

Who: Platform operators on `/admin`.

Why: Offboard customers, remove demo tenants, or honour erasure requests without joining the org as admin or running manual SQL.

Where: Organization detail → Overview tab (/admin/organizations/[companyId]) → Delete organization danger zone.

StepWhat happens
Open danger zoneRead grace-period copy: workspaces, connections, and billing data are removed after the grace window; member user accounts are kept.
Confirm in AlertDialogType the organization name exactly, then confirm.
After successYou return to the organization list; the org no longer appears among active tenants.

Semantics: Same soft-delete as org-admin self-serve (Privacy & data → Delete organization): deleted_at, purge_after, and is_blocked are set; open invoices are voided; active Stripe subscriptions are canceled when billing is enabled. Hard purge runs via the data-retention cron after the grace period (default 90 days). See operator-runbook.md — organization soft-delete.

Operator checklist (test org only): Delete a disposable org → verify it disappears from /admin/organizations → confirm deleted_at / purge_after on the company row if you have read-only DB access.