Skip to Content
Living documentation — last reviewed 2026-05-28
FeaturesInsightsInsights — Data Model

Insights — Data Model

Insights are derived read projections, with no dedicated table or migration. The 20-rule catalog and its populations are defined in behavior.

Source ownership

SourceUsed forImportant fields / boundaries
subscriptionsStored debt, billing risk, expiry/credits, retention cases, member lifecyclemembership_id, status/deletion, debt amount, current period, credits, cancellation reason/date, request/notice timestamps, retention_case_kind, outcome/acknowledgement/contact
organizationsRetention SLA and org-local date boundariesretention_sla_hours, timezone
membershipsMember/roster populations and booking/task ownershipOrganization, user, role, status, source, created/deleted timestamps; scope varies by rule
usersMember samples and birthdaysNames, email, birth date; birthdays join memberships, not subscriptions
payment_transactionsFailed-charge rows and never-paid historyOrganization/membership, type, status, amount, created timestamp
tasksRefund obligations, failed join checkout, overdue workOrganization, linked_user_id, type, source, status, due date, deletion
class_sessions, class_types, programs, class_session_workoutsSessions missing workout and program-specific sample destinationsProgram organization, session status/deletion/start, attached workout existence
bookingsWaitlistMembership/session IDs, waitlisted status, deletion; membership organization/deletion
form_instances, formsMissed/reviewable check-ins and missing complianceOrganization/assignee, kind/status, scheduled/answered lifecycle, archive marker, sample form name/type
organization_leads, leads, conversationsLead attentionOrg link/archive, status, next action/contact, unread inbound direction/count; shared attention clock and predicates

Stored debt remains subscriptions.debtAmountInCents, and failed charges remain failed transaction rows. The feed introduces no receipts, MRR, refund, or accounting semantics. Reports/revenue readers retain their canonical payments/services/receipts.sql ownership separately.

Response payload

interface Insight { id: InsightId; category: 'finance' | 'members' | 'operations' | 'engagement' | 'leads'; severity: 'info' | 'warning' | 'urgent'; count: number; drilldownHref: string | null; meta?: Record<string, unknown>; }

drilldownHref: null is an intentional nonlink. No consumer should append an unsupported query parameter or treat a null value as a generic list URL.

Count units and bounded context

RulesHeadline unit / meta
members.notices_openCases, not people; { open, unacknowledged, finalWeekUncontacted, sample }. The sample is at most three distinct members with { membershipId, name, email, effectiveAt }, while all cases contribute to count/severity.
finance.outstanding_debtDistinct contributing memberships; { totalDebtInCents, members }.
finance.failed_chargesTransaction rows; { totalInCents, days: 7 }.
finance.refunds_pendingOpen task rows; no sample required.
finance.join_checkout_failedDistinct active member memberships with uncapped count; { taskIds } capped at 20 and distinct-member { membershipId, name, email } sample capped at three.
members.at_risk_billingDistinct members; { membershipId, name, email } sample capped at three.
members.expiring_memberships / members.low_creditsDistinct active member memberships. Samples retain earliest expiry / lowest eligible balance and carry endsAt / remainingCredits; meta also carries days: 7 / threshold: 2.
members.join_never_paid / members.presale_withdrawn / members.recent_churnMembership rows satisfying the existing lifecycle predicates; grace/window meta is { hours: 24 } / none / { days: 30 }.
operations.tasks_overdueOpen tasks; oldest three { id, title, dueDate } samples are informational, not task URLs.
operations.sessions_missing_workoutSession rows; three samples carry session ID/name/start/program and a real program/date/session href.
operations.waitlisted_bookingsDistinct eligible booking memberships. meta.sessions is distinct sessions; the sample contains at most three distinct eligible sessions with id, name, and context href when available.
engagement.checkin_missedDistinct active roster people; three { userId, name, membershipId } samples.
engagement.checkins_to_reviewAnswered, unarchived check-in occurrences; existing inline queue.
engagement.forms_missingDistinct active roster people; when actionable, { actionable: true, sample } carries user/membership IDs, name/email, form name/type and sent date. The earliest pending form per person is picked before the three-person cap.
engagement.birthdaysActive member memberships; three samples ordered by birthday offset carry user/membership IDs, name and birth date; { days: 7 }. No subscription is required.
members.invitations_lapsedNon-cancelled member memberships whose account status is lapsed; three { membershipId, name } samples (name falls back to email).
leads.attention_neededEligible org-linked leads; three ranked { leadId, name, attention } samples.

Samples are bounded context, not exhaustive lists or count denominators. Per-member deduplication applies only where specified, not across all rules. Current-roster scope is likewise rule-specific: there is no active/role filter on stored debt, billing-risk, or waitlist membership scope.

Dates

Missed-check-in cutoff is org-local midnight fourteen calendar dates before org-local today, converted to UTC. Overdue tasks use org-local today. Session sample dates are org-local session dates; birthdays use today plus six month-day values, with year wrap. Other rolling windows remain as described in behavior.

Locale and query properties

Display copy belongs to shared en/he/ru dictionaries under insights.items. No locale-specific server response is stored. The service recomputes its projection per request; there is no documented benchmark or assumed universal index coverage for this mixed set of aggregate, sample, and existence queries.