Stats Dashboard

Production-only executive funnel, account and agency filters, and replay links

Purpose

User Journey Stats is a compact, mobile-first view for answering three questions:

  1. How many Production journeys happened?
  2. How far did those journeys progress?
  3. Which recent journey should the team inspect in Replay?

It intentionally omits technical charts, raw IDs, event-property exploration, and an environment selector. Those workflows belong in PostHog.

Data source and access

  • The page and APIs are admin-only.
  • The API always uses READ_ONLY_PROD_DB.
  • Any environment query parameter is ignored.
  • Queries require journey_event.environment = 'production'.
  • Deleted accounts are always excluded. zQA journeys are excluded by default, with optional include-all and QA-only views.
  • High-volume email telemetry is excluded from journey calculations.

Controls

  • Period: Today, 7, 30, or 90 days
  • Scope: All accounts, a single account, or an agency roster
  • QA data: Exclude zQA (default), Include all, or Only zQA
  • Refresh: manual button, automatic every 60 seconds, and immediate refresh when returning to the tab

An account is classified as an agency when it appears as agent_account_id in at least one active account_agents relationship. The legacy agentOrAgency field is not used.

Selecting an agency includes:

  • the agency's own activity
  • all active managed accounts
  • activity tied to the owner user IDs of those accounts
  • anonymous earlier events with the same user_session_id

API contracts

Journey summary

GET /api/admin/z-agent/journey?days=7&scopeType=all|account|agency&scopeId=<id>&qa=exclude|include|only

The response is intentionally small:

{
  generatedAt: string;
  qa: "exclude" | "include" | "only";
  summary: Record<string, number>;
  funnel: Array<{
    key: string;
    label: string;
    count: number;
    percentOfStart: number;
  }>;
  recentJourneys: Array<RecentJourney>;
}

Account and agency picker

GET /api/admin/z-agent/journey/accounts?q=<name-or-slug>&qa=exclude|include|only

The response returns up to 50 non-deleted options matching the QA mode. It searches display name and slug and never exposes email addresses. A real agency with QA members remains selectable in QA-only mode. See Identity and zQA Filters for anonymous-session stitching, actor/target distinctions and differences from PostHog's broader internal-user filter.

Recent journeys

Each row shows only:

  • account name
  • last meaningful step
  • status: Completed, Needs attention, In progress, Inactive, Abandoned, or Ended
  • duration and last-seen time
  • Replay link when available

The Replay button appears only when a PostHog session ID and replay-consent metadata are present. It opens:

${POSTHOG_PROJECT_URL}/replay/{sessionRecordingId}

Missing replay metadata hides the button instead of creating a broken search link.

Session boundaries and status

Summary counts and recent rows use the same visit boundaries. An explicit user_session_id is preferred; a posthog_session_id is the fallback. Events without either are standalone observations, not a multi-day session grouped by user or account ID. A gap of 30 minutes or more splits a persisted session into a new visit. Activity after an explicit logout also starts a new visit. These derived visit keys are for Stats, not PostHog replay IDs.

StatusMeaning
In progressThe latest event is less than 30 minutes old, with no terminal outcome. Not a live-presence indicator.
InactiveNo event for at least 30 minutes, without an explicit terminal outcome. Inactivity is not proof of abandonment.
AbandonedThe latest event explicitly reports onboarding/signup abandonment. A subsequent resume can reopen the visit.
EndedThe latest event is logout.
Needs attentionThe latest event reports failure, error, or timeout. A recovered earlier error does not keep this badge forever.
CompletedThe visit contains signup completion, completed onboarding, agent creation, or an offer received, with no later terminal abandonment/logout/error.

Duration is the interval between the first and last observed events in that visit, not a timer that continues while the user is away. A standalone event has zero duration. Separate uncorrelated server events cannot be safely joined to a browser visit just because they belong to the same person.