Marketing Leadgen Data Model

Marketing Leadgen Data Model

8 data marts70 fieldsVlad FlaksRus Obolonsky

Campaigns anchor a B2B demand-generation business, tying ad spend, web traffic and attribution to leads that move through pipeline stages to a closed deal, with accounts carrying the firmographics ABM depends on.

Overview

A B2B demand-generation and revenue-operations business modeled end to end — from ad spend and marketing campaigns, through web traffic and multi-touch attribution, to leads, sales opportunities and the pipeline stages that carry them to close. Campaigns anchor everything, paid and non-paid alike: every dollar of ad spend, every web session and every touch on the path to conversion ties back to a campaign, so channel performance can be traced cleanly from a click to a closed-won deal. Account carries the firmographics and the ABM target-list flag, since real B2B deals are won or lost at the account level, not the individual lead level.

Example Questions

  • Which channels and campaigns generate the most pipeline and revenue once ad spend, attribution credit and win rates are accounted for?
  • Where does the funnel leak most — awareness to lead, lead to MQL/SQL, or opportunity to close — and does that differ by channel?
  • Do target (ABM) accounts convert, close and win at a higher rate than the rest of the book?

Explore on canvas →

Account Data Mart

Account

One row per target company, the firmographic backbone the rest of the model hangs off. Every account carries an industry, an employee-count band and a region, plus a flag marking whether it sits on the account-based marketing (ABM) target list. Because real B2B deals belong to accounts rather than to whichever individual filled out a form, this mart is the join point that lets leads, opportunities and touchpoints be rolled up to "who is this company, and how big an opportunity is it."

Fields

ColumnTypeAliasDescription
account_idSTRINGAccount IDPK. Unique identifier for the target company.
nameSTRINGNameCompany name.
industrySTRINGIndustryIndustry the company operates in.
employee_bandSTRINGEmployee BandCompany-size bucket — the mid-market segmentation axis. Vocabulary: 1-50 / 51-200 / 201-1000 / 1000+.
regionSTRINGRegionGeographic region of the company.
is_target_accountBOOLEANIs Target AccountWhether the company is on the ABM target list. ~10–20% of accounts, skewed toward larger employee_band; target accounts show higher engagement, pipeline and win rate.

Ad Spend Data Mart

Ad Spend

Daily cost, impressions and clicks for every paid campaign, broken out by ad group. This is the cost side of the funnel — the numbers a demand-gen team reconciles against Google Ads, LinkedIn Ads and other platform reports every week to know what a click, a lead and ultimately a closed deal actually costs.

Fields

ColumnTypeAliasDescription
spend_idSTRINGSpend IDPK. Unique identifier for each spend record.
spend_dateDATESpend DateDay the spend was incurred. Within the campaign's [start_date, end_date] flight.
campaign_idSTRINGCampaign IDCampaign this spend belongs to. FK to Campaign
channelSTRINGChannelMarketing channel where the cost was spent. Denormalized copy of the campaign's channel.
ad_groupSTRINGAd GroupAd group or ad set within the campaign.
impressionsINTEGERImpressionsNumber of times ads were shown.
clicksINTEGERClicksNumber of clicks the ads received. Always ≤ impressions; CTR by channel.
costNUMERICCostMoney spent on this ad group for the day (USD). Derived from clicks × per-channel CPC.

Relationships

Related data martOnCardinalityMeaning
Campaigncampaign_id = campaign_idN:1The campaign this spend was bought for.

Campaign Data Mart

Campaign

Reference of every marketing initiative run — paid and non-paid alike, from search and social ads to email nurtures, webinars and content syndication — each carrying its channel, its objective and the UTM tags that tie it back to ad-platform and web-analytics data. This is the conformed campaign dimension of the model: every channel value that shows up on ad spend, web sessions, touchpoints or opportunities agrees with what is declared here, so a channel-level report never splits into phantom categories.

Fields

ColumnTypeAliasDescription
campaign_idSTRINGCampaign IDPK. Unique identifier for each campaign.
campaign_nameSTRINGCampaign NameHuman-readable name of the campaign.
channelSTRINGChannelMarketing channel. Controlled vocabulary: paid_search, paid_social, display, organic_search, email, webinar, content_syndication, direct, referral.
objectiveSTRINGObjectivePrimary goal. One of: awareness, lead_generation, conversion, retargeting.
utm_sourceSTRINGUTM SourceUTM source tag identifying where the traffic originates.
utm_mediumSTRINGUTM MediumUTM medium tag describing the type of traffic (e.g. cpc, email).
utm_campaignSTRINGUTM CampaignUTM campaign tag — the key ad-platform ↔ web-analytics join key.
start_dateDATEStart DateDate the campaign went live.
end_dateDATEEnd DateDate the campaign flight ended (nullable for always-on). Spend/touches fall within [start_date, end_date].

Lead Data Mart

Lead

One row per lead or contact captured by marketing, carrying where they came from, their fit-and-engagement score, the dated milestones marking when they became marketing-qualified (MQL) and sales-qualified (SQL), and firmographics that mirror the account they belong to. Because the qualification milestones are dated rather than just a current-state label, leads can be grouped into cohorts — "leads created in March" — and tracked through the funnel over time, not just counted at a single point.

Fields

ColumnTypeAliasDescription
lead_idSTRINGLead IDPK. Unique identifier for each lead or contact.
account_idSTRINGAccount IDCompany the lead belongs to; null for a lead not yet matched to an account. FK to Account
created_atTIMESTAMPCreated AtWhen the lead first entered the system (coincides with the is_lead_create touchpoint).
source_channelSTRINGSource ChannelChannel that first brought in the lead. Equals the channel of the lead's is_lead_create touchpoint. Same controlled vocabulary as Campaign.channel.
lead_scoreINTEGERLead ScoreFit + engagement score for MQL gating.
became_mql_atTIMESTAMPBecame Mql AtWhen the lead reached marketing-qualified status; null if never MQL. created_at ≤ became_mql_at.
became_sql_atTIMESTAMPBecame Sql AtWhen the lead reached sales-qualified status; null if never SQL. became_mql_at ≤ became_sql_at.
employee_bandSTRINGEmployee BandCompany-size bucket. Same vocabulary as Account.employee_band: 1-50 / 51-200 / 201-1000 / 1000+. For matched leads, agrees with the account.
industrySTRINGIndustryIndustry the lead's company operates in. For matched leads, agrees with the account.
countrySTRINGCountryCountry where the lead is located; consistent with the account's region.
lifecycle_stageSTRINGLifecycle Stagesubscriber / lead / MQL / SQL / opportunity / customer. Consistent with the became_*_at timestamps and with the existence of an Opportunities row.

Relationships

Related data martOnCardinalityMeaning
Accountaccount_id = account_idN:1The company the lead works for.

Opportunities Data Mart

Opportunities

One row per sales opportunity — the deal object, keyed to the account rather than a single lead, since real B2B buying committees involve multiple people. Each row carries the pipeline stage, the deal size (ACV), the sourcing lead and campaign, and the win/loss outcome with close date and sales-cycle length. This is where marketing-sourced pipeline turns into — or fails to turn into — closed revenue.

Fields

ColumnTypeAliasDescription
opportunity_idSTRINGOpportunity IDPK. Unique identifier for each sales opportunity.
account_idSTRINGAccount IDAccount the deal belongs to — the primary spine. FK to Account
lead_idSTRINGLead IDSourcing/converting lead the opportunity originated from; nullable. FK to Lead
primary_campaign_idSTRINGPrimary Campaign IDPrimary Campaign Source (single-touch sourcing); nullable. Equals the campaign of the sourcing lead's is_lead_create touchpoint. FK to Campaign
created_atTIMESTAMPCreated AtWhen the opportunity was created.
stageSTRINGStageCurrent pipeline stage. One of: discovery, demo, proposal, negotiation, closed_won, closed_lost.
amountNUMERICAmountACV / deal size (USD). Correlates with the account's employee_band.
close_dateDATEClose DateDate the opportunity was won or lost; null while open.
is_wonBOOLEANIs WonTrue iff stage = closed_won (then close_date is set); false with a close_date means closed_lost.
sales_cycle_daysINTEGERSales Cycle DaysDATE_DIFF(close_date, created_at, DAY) for closed deals; null while open.
ownerSTRINGOwnerSales rep who owns the opportunity (drawn from a small stable roster).

Relationships

Related data martOnCardinalityMeaning
Accountaccount_id = account_idN:1The company the deal is with.
Campaignprimary_campaign_id = campaign_idN:1The campaign credited with sourcing the deal.
Leadlead_id = lead_idN:1The lead the deal grew out of.

Stage Transitions Data Mart

Stage Transitions

One row per pipeline stage change for a sales opportunity, tracing each deal's path through discovery, demo, proposal and negotiation on the way to a win or a loss. The time spent in each stage before moving to the next is the raw material for bottleneck analysis — where deals get stuck, and for how long.

Fields

ColumnTypeAliasDescription
transition_idSTRINGTransition IDPK. Unique identifier for the stage change.
opportunity_idSTRINGOpportunity IDOpportunity that moved stage. FK to Opportunities
from_stageSTRINGFrom StageStage the opportunity left.
to_stageSTRINGTo StageStage the opportunity entered. Contiguous chain: to_stage of row n = from_stage of row n+1; the last to_stage equals Opportunities.stage.
transitioned_atTIMESTAMPTransitioned AtWhen the stage change happened. Within [Opportunities.created_at, close_date].
days_in_from_stageINTEGERDays In From StageDays spent in the previous stage — the velocity bottleneck driver. Sums (per opportunity) to the sales cycle for closed deals.

Relationships

Related data martOnCardinalityMeaning
Opportunitiesopportunity_id = opportunity_idN:1The deal that moved stage.

Touchpoints Data Mart

Touchpoints

One row per marketing touch a lead has on the way to becoming a customer, credited under a W-shaped multi-touch attribution model that splits credit 30% to the first touch, 30% to lead creation, 30% to opportunity creation and 10% across everything in between. This is the mart that answers "which channels and campaigns actually deserve credit," rather than crediting only the first click or the last one.

Fields

ColumnTypeAliasDescription
touchpoint_idSTRINGTouchpoint IDPK. Unique identifier for each marketing touch.
lead_idSTRINGLead IDLead that this touch belongs to. FK to Lead
campaign_idSTRINGCampaign IDCampaign associated with this touch. FK to Campaign
occurred_atTIMESTAMPOccurred AtWhen the touch happened.
channelSTRINGChannelChannel where the touch occurred. Equals the campaign's channel; same controlled vocabulary as Campaign.channel.
touch_typeSTRINGTouch TypeKind of interaction. One of: ad_click, form_fill, email_open, email_click, webinar_attend, content_download, demo_request. Consistent with channel (e.g. no email_open on paid_search).
touch_creditFLOATTouch CreditW-shaped credit for this touch. Sums to exactly 1.0 per lead: 30% first touch, 30% lead creation, 30% opportunity creation, 10% across middle touches.
is_first_touchBOOLEANIs First TouchTrue on the lead's earliest touch (exactly one per lead). W-shaped anchor.
is_lead_createBOOLEANIs Lead CreateTrue on the touch that created the lead (exactly one per lead; coincides with Lead.created_at). W-shaped anchor.
is_opp_createBOOLEANIs Opp CreateTrue on the touch that created the opportunity (at most one per lead; only for leads with an opportunity; coincides with Opportunities.created_at). W-shaped anchor.

Relationships

Related data martOnCardinalityMeaning
Campaigncampaign_id = campaign_idN:1The campaign that produced this touch.
Leadlead_id = lead_idN:1The lead who was touched.

Web Sessions Data Mart

Web Sessions

One row per web session, the very top of the funnel, before most visitors are ever identified as a lead. Each session records the traffic source and medium, the landing page, whether it converted into a form submission, and — for the small share of visitors identified through identity stitching — the lead that session belongs to. Because the overwhelming majority of B2B web traffic never converts or gets identified, most sessions carry no lead at all; the ones that do show exactly where anonymous traffic turns into a known contact.

Fields

ColumnTypeAliasDescription
session_idSTRINGSession IDPK. Unique identifier for the web session.
lead_idSTRINGLead IDKnown lead after identity stitching; null for never-identified visitors (the overwhelming majority). FK to Lead
started_atTIMESTAMPStarted AtWhen the session began.
campaign_idSTRINGCampaign IDCampaign that drove the session; null for organic/direct. FK to Campaign
sourceSTRINGSourceTraffic source that referred the session; consistent with the driving campaign's utm_source.
mediumSTRINGMediumMarketing medium (e.g. organic, cpc, email); consistent with the driving campaign's utm_medium.
landing_pageSTRINGLanding PageFirst page viewed in the session.
form_submitsINTEGERForm SubmitsNumber of forms submitted during the session. ≥ 1 when is_conversion is true.
is_conversionBOOLEANIs ConversionWhether the session produced a lead or demo request. Session→lead conversion ~1–3% overall, lowest on paid social.

Relationships

Related data martOnCardinalityMeaning
Campaigncampaign_id = campaign_idN:1The campaign that drove the visit.
Leadlead_id = lead_idN:1The lead this visit was later tied to.

Apply to your project

  1. 1

    Install the Import Model plugin

    One plugin, installed once, in your own OWOX workspace.

    Get the plugin →

  2. 2

    Import this model

    Point it at this bundle and it creates every data mart above, joins and all.

    Open the model →

  3. 3

    Plug in your data and destinations

    Connect your own sources and send the results where your team already works.

    Browse connectors →

4. Optional — customize as you wish. Rename a column, drop a mart, add your own: once it is imported it is yours, and nothing here syncs back.