offdataai
Sales & CRM · data model template

CRM Operational data model

Normalized (3NF) operational CRM application schema. Account/contact backbone, lead capture and conversion, opportunity -> line -> quote pipeline, campaign membership, activity logging, and case management. Person PII carries GDPR classification with masking and consent flags. To replicate an existing Salesforce org as a landing layer, use the Salesforce Core Objects (3NF Source Replica) template instead.

A vendor-neutral operational CRM application schema for sales and service: accounts and their contacts, inbound leads, the opportunity pipeline with line items and quotes, the product catalog, marketing campaigns and members, logged activities, and support cases. Contact and lead PII is classified for GDPR. Designed for building a CRM database, not replicating an existing SaaS CRM. It is a normalized (3NF) relational model that mirrors the source system, suited to operational databases and to landing source data faithfully.

3NFSales & CRMGDPRCCPA
13
tables
120
columns
18
relationships
9
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

accounts_and_peoplepipelinemarketingservice

Design decisions

  • Lead distinct from contact/account

    Unqualified leads carry their own PII and lifecycle before conversion; keeping them separate (with a converted-account reference) mirrors how CRM systems handle lead conversion and avoids polluting the account graph with prospects.

  • Opportunity / line / quote normalization

    Deals carry multiple product lines and may generate multiple quotes; modeling lines and quotes as children of the opportunity keeps amounts auditable and avoids repeating product data.

  • Polymorphic campaign membership via nullable FKs

    A campaign member can be either a lead or a contact; two nullable FKs on the junction keep referential integrity without a circular lead<->contact dependency.

  • Contact/lead PII masked with consent flag

    Email is hashed and other contact fields partially masked; a do_not_contact flag records GDPR/CCPA consent so downstream outreach can honor it.

Tables in the CRM Operational 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 (11)

account

Grain: one row per customer/prospect organization

ColumnTypeKeys
account_keybigintPK
account_numbervarcharBK
account_namevarchar
parent_account_numbervarchar
industryvarchar
account_typevarchar
annual_revenuedecimal
employee_countinteger
billing_countryvarchar
websitevarchar
owner_user_idvarchar
is_activeboolean

contact

Grain: one row per person associated with an account

Person-level PII classified for GDPR.

ColumnTypeKeys
contact_keybigintPK
account_keybigint
contact_external_idvarcharBK
first_namevarcharPII
last_namevarcharPII
emailvarcharPII
phonevarcharPII
mobile_phonevarcharPII
job_titlevarchar
mailing_countryvarchar
do_not_contactboolean
is_primaryboolean

lead

Grain: one row per unqualified inbound lead

Person-level PII classified for GDPR.

ColumnTypeKeys
lead_keybigintPK
lead_source_keybigint
campaign_keybigint
lead_external_idvarcharBK
first_namevarcharPII
last_namevarcharPII
emailvarcharPII
phonevarcharPII
company_namevarchar
statusvarchar
ratingvarchar
converted_account_numbervarchar
created_attimestamp

product

Grain: one row per sellable product/SKU

ColumnTypeKeys
product_keybigintPK
product_codevarcharBK
product_namevarchar
product_familyvarchar
list_pricedecimal
currency_codevarchar
is_activeboolean

opportunity

Grain: one row per sales opportunity (deal)

ColumnTypeKeys
opportunity_keybigintPK
account_keybigint
primary_contact_keybigint
campaign_keybigint
opportunity_external_idvarcharBK
opportunity_namevarchar
stagevarchar
amountdecimal
currency_codevarchar
probability_pctdecimal
close_datedate
is_closedboolean
is_wonboolean
owner_user_idvarchar
created_attimestamp

opportunity_line

Grain: one row per product line on an opportunity

ColumnTypeKeys
opportunity_line_keybigintPK
opportunity_keybigint
product_keybigint
quantitydecimal
unit_pricedecimal
line_totaldecimal
discount_pctdecimal

quote

Grain: one row per quote issued for an opportunity

ColumnTypeKeys
quote_keybigintPK
opportunity_keybigint
quote_numbervarcharBK
statusvarchar
total_amountdecimal
currency_codevarchar
valid_untildate
issued_attimestamp

campaign

Grain: one row per marketing campaign

ColumnTypeKeys
campaign_keybigintPK
campaign_codevarcharBK
campaign_namevarchar
campaign_typevarchar
statusvarchar
start_datedate
end_datedate
budgeted_costdecimal
actual_costdecimal

campaign_member

Grain: one row per contact/lead per campaign (junction)

ColumnTypeKeys
campaign_member_keybigintPK
campaign_keybigint
contact_keybigint
lead_keybigint
member_statusvarchar
responded_attimestamp

activity

Grain: one row per logged activity (call/email/meeting/task)

ColumnTypeKeys
activity_keybigintPK
account_keybigint
contact_keybigint
opportunity_keybigint
activity_external_idvarcharBK
activity_typevarchar
subjectvarchar
statusvarchar
due_datedate
completed_attimestamp
owner_user_idvarchar

case

Grain: one row per support case raised by a customer

ColumnTypeKeys
case_keybigintPK
account_keybigint
contact_keybigint
case_priority_keybigint
case_numbervarcharBK
subjectvarchar
descriptionvarchar
statusvarchar
originvarchar
opened_attimestamp
closed_attimestamp
owner_user_idvarchar

Lookup tables (2)

lead_source

Grain: reference list of lead/marketing sources

ColumnTypeKeys
lead_source_keybigintPK
source_codevarcharBK
source_namevarchar
channelvarchar

case_priority

Grain: reference list of case priorities

ColumnTypeKeys
case_priority_keybigintPK
priority_codevarcharBK
priority_namevarchar
sort_ordersmallint

Relationships

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

  • contact.account_key → account (many-to-one)
  • lead.lead_source_key → lead_source (many-to-one)
  • lead.campaign_key → campaign (many-to-one)
  • opportunity.account_key → account (many-to-one)
  • opportunity.primary_contact_key → contact (many-to-one)
  • opportunity.campaign_key → campaign (many-to-one)
  • opportunity_line.opportunity_key → opportunity (many-to-one)
  • opportunity_line.product_key → product (many-to-one)
  • quote.opportunity_key → opportunity (many-to-one)
  • campaign_member.campaign_key → campaign (many-to-one)
  • campaign_member.contact_key → contact (many-to-one)
  • campaign_member.lead_key → lead (many-to-one)
  • activity.account_key → account (many-to-one)
  • activity.contact_key → contact (many-to-one)
  • activity.opportunity_key → opportunity (many-to-one)
  • case.account_key → account (many-to-one)
  • case.contact_key → contact (many-to-one)
  • case.case_priority_key → case_priority (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