offdataai
Healthcare & Life Sciences · data model template

SaaS Customer Health & Success data model

Third-normal-form replica of the customer-success operational system. Each conceptual table becomes its own entity with a warehouse surrogate key plus the real source business key, and child snapshot / signal / task tables reference their parent entities via foreign keys. The composite health-score snapshot is separated from its contributing signals so trend and red-flag analysis can run at either grain. User and CSM contact details are PII-masked under SOC2 and GDPR.

Normalized replica of a SaaS customer-success operational stack (Gainsight / ChurnZero / Catalyst / Vitally): accounts, end users and CSM owners; composite health scores composed of usage, support, billing and NPS signals; risks, engagements, playbook runs and tasks; plus voice-of-customer (NPS, support summaries) — keyed by real source business identifiers and anchored to plan, product, signal-type, risk category, playbook template and CSM role reference data. It is a normalized (3NF) relational model that mirrors the source system, suited to operational databases and to landing source data faithfully.

3NFHealthcare & Life SciencesSOC2GDPR
19
tables
163
columns
21
relationships
5
PII columns

Opening the template creates a project with the ERD, platform-native DDL, a dbt project and documentation already generated (recommended platform: PostgreSQL). Edit it in plain English or on the canvas, then ship it to any supported warehouse.

Subject areas

account_teamhealthengagementvoice_of_customermaster_data

Design decisions

  • Composite health score split into snapshot header and signal detail

    Customer-success platforms compute an overall health score from many weighted input signals (usage, support, billing, NPS). Modeling the snapshot header and per-signal detail as separate entities lets trend reports run off the snapshot grain while red-flag and signal-drilldown reports run off the signal grain without exploding the snapshot row.

  • CSM caseload via a time-bounded account_assignment table with is_current

    Account ownership changes — primary CSM, onboarding specialist and renewal manager rotate over an account's life. A dedicated account_assignment entity with assigned_at / ended_at / is_current preserves history for tenure analysis while allowing a simple is_current=true filter to drive today's caseload reports.

  • Risk records separated from engagements so portfolio risk is queryable

    At-risk accounts need a first-class entity with severity, ARR-at-risk and owner so portfolio dashboards can roll up exposure without scanning engagement notes. Keeping risk_record distinct from engagement_log lets each grain stay clean and reportable.

  • Playbook runs and tasks split for cadence execution tracking

    Playbooks (onboarding-90day, renewal-prep, at-risk-rescue) are multi-step cadences. A playbook_run header records start/completion and outcome while playbook_task children record each step's status — supporting both run-level completion reporting and task-level SLA tracking.

  • NPS captured at user grain rolled to account

    NPS surveys are answered by individual users, but customer-success reporting needs per-account NPS. Storing nps_response at the user grain with both user_key and account_key preserves the source grain for verbatim analysis while allowing simple per-account rollups.

Tables in the SaaS Customer Health & Success model

Every table with its grain, columns, types and keys. PK marks a primary key, BK a business key and PII a column classified as personal data, which gets a masking strategy by default.

Tables (12)

account

Grain: one row per customer account

Customer account master (one row per company). parent_account_key is a self-reference.

ColumnTypeKeys
account_keybigintPK
plan_keybigint
account_idvarcharBK
account_namevarchar
parent_account_keybigint
industryvarchar
employee_size_bandvarchar
countryvarchar
regionvarchar
lifecycle_stagevarchar
signup_datedate
contract_start_datedate
contract_end_datedate
arr_amountdecimal
currencyvarchar
is_strategicboolean

user

Grain: one row per end user of an account

End-user identities within a customer account.

ColumnTypeKeys
user_keybigintPK
account_keybigint
user_idvarcharBK
full_namevarcharPII
emailvarcharPII
user_rolevarchar
signup_datedate
last_login_datedate
is_executive_sponsorboolean
is_decision_makerboolean
is_activeboolean

csm_user

Grain: one row per customer-success team member

Customer-success team member directory.

ColumnTypeKeys
csm_user_keybigintPK
csm_role_keybigint
csm_user_idvarcharBK
full_namevarcharPII
emailvarcharPII
caseload_sizeinteger
region_coveredvarchar
hire_datedate
is_activeboolean

account_assignment

Grain: one row per (account, csm_user, assignment_type, period)

Time-bounded mapping of accounts to CSM owners.

ColumnTypeKeys
account_assignment_keybigintPK
account_keybigint
csm_user_keybigint
assignment_idvarcharBK
assignment_typevarchar
assigned_attimestamp
ended_attimestamp
is_currentboolean

health_score_snapshot

Grain: one row per account per health-score snapshot

Composite health-score header — one per account per scoring run.

ColumnTypeKeys
health_score_snapshot_keybigintPK
account_keybigint
health_snapshot_idvarcharBK
snapshot_datedate
overall_health_scoredecimal
health_gradevarchar
trendvarchar
churn_risk_probability_pctdecimal
expansion_propensity_pctdecimal
computed_by_versionvarchar

health_signal

Grain: one row per signal contributing to a health snapshot

Per-signal detail behind each composite health snapshot.

ColumnTypeKeys
health_signal_keybigintPK
health_score_snapshot_keybigint
health_signal_type_keybigint
signal_idvarcharBK
signal_valuedecimal
normalized_scoredecimal
signal_thresholddecimal
is_red_flagboolean
observed_attimestamp

risk_record

Grain: one row per logged risk against an account

Risks logged against accounts, owned by a CSM.

ColumnTypeKeys
risk_record_keybigintPK
account_keybigint
risk_category_keybigint
csm_user_keybigint
risk_idvarcharBK
opened_attimestamp
target_resolution_datedate
resolved_attimestamp
risk_severityvarchar
risk_statusvarchar
arr_at_riskdecimal
currencyvarchar
mitigation_plan_summaryvarchar

engagement_log

Grain: one row per CSM engagement with an account

Calls, meetings, QBRs and other touchpoints between CSM and customer.

ColumnTypeKeys
engagement_log_keybigintPK
account_keybigint
csm_user_keybigint
engagement_type_keybigint
engagement_idvarcharBK
engagement_datetimestamp
duration_minutesinteger
summaryvarchar
attendee_countinteger
sentimentvarchar
was_executive_attendedboolean
next_stepsvarchar

playbook_run

Grain: one row per playbook execution

A single execution of a playbook template against an account.

ColumnTypeKeys
playbook_run_keybigintPK
account_keybigint
playbook_template_keybigint
csm_user_keybigint
playbook_run_idvarcharBK
started_attimestamp
completed_attimestamp
run_statusvarchar
outcomevarchar

playbook_task

Grain: one row per task within a playbook run

Individual tasks/checklist items within a playbook run.

ColumnTypeKeys
playbook_task_keybigintPK
playbook_run_keybigint
playbook_task_idvarcharBK
task_sequencesmallint
task_titlevarchar
due_datedate
completed_attimestamp
task_statusvarchar
assigned_to_uservarchar

nps_response

Grain: one row per NPS survey response

Individual NPS survey responses captured at the user grain.

ColumnTypeKeys
nps_response_keybigintPK
user_keybigint
account_keybigint
nps_response_idvarcharBK
survey_datedate
nps_scoresmallint
categoryvarchar
verbatimvarcharPII
csat_scoredecimal
follow_up_requiredboolean

support_ticket_summary

Grain: one row per account per support summary period

Per-account rollup of support activity over a period.

ColumnTypeKeys
support_ticket_summary_keybigintPK
account_keybigint
summary_idvarcharBK
period_startdate
period_enddate
tickets_openedinteger
tickets_closedinteger
critical_countinteger
average_resolution_hoursdecimal
csat_averagedecimal

Lookup tables (7)

product

Grain: one row per product offering

Reference list of SaaS products tracked for adoption.

ColumnTypeKeys
product_keybigintPK
product_idvarcharBK
product_namevarchar
product_familyvarchar
is_coreboolean
is_addonboolean

plan

Grain: one row per subscription plan

Subscription plan catalog (tier + price book).

ColumnTypeKeys
plan_keybigintPK
plan_idvarcharBK
plan_namevarchar
plan_tiervarchar
billing_frequencyvarchar
list_mrrdecimal
currencyvarchar

health_signal_type

Grain: one row per health signal type

Catalog of input signals that feed the composite health score.

ColumnTypeKeys
health_signal_type_keybigintPK
signal_type_idvarcharBK
signal_namevarchar
signal_categoryvarchar
weightdecimal
is_leading_indicatorboolean

risk_category

Grain: one row per risk category

Taxonomy of churn / at-risk reasons.

ColumnTypeKeys
risk_category_keybigintPK
risk_category_idvarcharBK
risk_namevarchar
is_actionableboolean

playbook_template

Grain: one row per playbook template

Reusable customer-success playbook definitions.

ColumnTypeKeys
playbook_template_keybigintPK
playbook_idvarcharBK
playbook_namevarchar
playbook_typevarchar
target_stagevarchar
expected_duration_daysinteger

csm_role

Grain: one row per CSM role definition

Roles available to customer-success team members.

ColumnTypeKeys
csm_role_keybigintPK
csm_role_idvarcharBK
role_namevarchar
tier_coveragevarchar

engagement_type

Grain: one row per engagement type

Classification of CSM-to-customer engagement modalities.

ColumnTypeKeys
engagement_type_keybigintPK
engagement_type_idvarcharBK
descriptionvarchar
is_high_touchboolean

Relationships

Each relationship is a foreign key on the first table referencing the second.

  • account.plan_key → plan (many-to-one)
  • user.account_key → account (many-to-one)
  • csm_user.csm_role_key → csm_role (many-to-one)
  • account_assignment.account_key → account (many-to-one)
  • account_assignment.csm_user_key → csm_user (many-to-one)
  • health_score_snapshot.account_key → account (many-to-one)
  • health_signal.health_score_snapshot_key → health_score_snapshot (many-to-one)
  • health_signal.health_signal_type_key → health_signal_type (many-to-one)
  • risk_record.account_key → account (many-to-one)
  • risk_record.risk_category_key → risk_category (many-to-one)
  • risk_record.csm_user_key → csm_user (many-to-one)
  • engagement_log.account_key → account (many-to-one)
  • engagement_log.csm_user_key → csm_user (many-to-one)
  • engagement_log.engagement_type_key → engagement_type (many-to-one)
  • playbook_run.account_key → account (many-to-one)
  • playbook_run.playbook_template_key → playbook_template (many-to-one)
  • playbook_run.csm_user_key → csm_user (many-to-one)
  • playbook_task.playbook_run_key → playbook_run (many-to-one)
  • nps_response.user_key → user (many-to-one)
  • nps_response.account_key → account (many-to-one)
  • support_ticket_summary.account_key → account (many-to-one)

Related templates

All templates →
Start for $1

From requirements to AI-ready context, in one platform.

Describe your business in a conversation, start from a template or connect your own database. Your first model is ready in minutes.

Get startedBook a demo

Plans from $1/month · cancel any time