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
| Source | Used for | Important fields / boundaries |
|---|---|---|
subscriptions | Stored debt, billing risk, expiry/credits, retention cases, member lifecycle | membership_id, status/deletion, debt amount, current period, credits, cancellation reason/date, request/notice timestamps, retention_case_kind, outcome/acknowledgement/contact |
organizations | Retention SLA and org-local date boundaries | retention_sla_hours, timezone |
memberships | Member/roster populations and booking/task ownership | Organization, user, role, status, source, created/deleted timestamps; scope varies by rule |
users | Member samples and birthdays | Names, email, birth date; birthdays join memberships, not subscriptions |
payment_transactions | Failed-charge rows and never-paid history | Organization/membership, type, status, amount, created timestamp |
tasks | Refund obligations, failed join checkout, overdue work | Organization, linked_user_id, type, source, status, due date, deletion |
class_sessions, class_types, programs, class_session_workouts | Sessions missing workout and program-specific sample destinations | Program organization, session status/deletion/start, attached workout existence |
bookings | Waitlist | Membership/session IDs, waitlisted status, deletion; membership organization/deletion |
form_instances, forms | Missed/reviewable check-ins and missing compliance | Organization/assignee, kind/status, scheduled/answered lifecycle, archive marker, sample form name/type |
organization_leads, leads, conversations | Lead attention | Org 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
| Rules | Headline unit / meta |
|---|---|
members.notices_open | Cases, 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_debt | Distinct contributing memberships; { totalDebtInCents, members }. |
finance.failed_charges | Transaction rows; { totalInCents, days: 7 }. |
finance.refunds_pending | Open task rows; no sample required. |
finance.join_checkout_failed | Distinct active member memberships with uncapped count; { taskIds } capped at 20 and distinct-member { membershipId, name, email } sample capped at three. |
members.at_risk_billing | Distinct members; { membershipId, name, email } sample capped at three. |
members.expiring_memberships / members.low_credits | Distinct 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_churn | Membership rows satisfying the existing lifecycle predicates; grace/window meta is { hours: 24 } / none / { days: 30 }. |
operations.tasks_overdue | Open tasks; oldest three { id, title, dueDate } samples are informational, not task URLs. |
operations.sessions_missing_workout | Session rows; three samples carry session ID/name/start/program and a real program/date/session href. |
operations.waitlisted_bookings | Distinct 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_missed | Distinct active roster people; three { userId, name, membershipId } samples. |
engagement.checkins_to_review | Answered, unarchived check-in occurrences; existing inline queue. |
engagement.forms_missing | Distinct 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.birthdays | Active member memberships; three samples ordered by birthday offset carry user/membership IDs, name and birth date; { days: 7 }. No subscription is required. |
members.invitations_lapsed | Non-cancelled member memberships whose account status is lapsed; three { membershipId, name } samples (name falls back to email). |
leads.attention_needed | Eligible 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.