Marketing Leadgen Data Model
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.
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
campaignsgenerate the most pipeline and revenue oncead spend, attribution credit and win rates are accounted for? - Where does the funnel leak most — awareness to
lead, lead to MQL/SQL, oropportunityto close — and does that differ by channel? - Do target (ABM)
accountsconvert, close and win at a higher rate than the rest of the book?
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
| Column | Type | Alias | Description |
|---|---|---|---|
account_id | STRING | Account ID | PK. Unique identifier for the target company. |
name | STRING | Name | Company name. |
industry | STRING | Industry | Industry the company operates in. |
employee_band | STRING | Employee Band | Company-size bucket — the mid-market segmentation axis. Vocabulary: 1-50 / 51-200 / 201-1000 / 1000+. |
region | STRING | Region | Geographic region of the company. |
is_target_account | BOOLEAN | Is Target Account | Whether 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
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
| Column | Type | Alias | Description |
|---|---|---|---|
spend_id | STRING | Spend ID | PK. Unique identifier for each spend record. |
spend_date | DATE | Spend Date | Day the spend was incurred. Within the campaign's [start_date, end_date] flight. |
campaign_id | STRING | Campaign ID | Campaign this spend belongs to. FK to Campaign |
channel | STRING | Channel | Marketing channel where the cost was spent. Denormalized copy of the campaign's channel. |
ad_group | STRING | Ad Group | Ad group or ad set within the campaign. |
impressions | INTEGER | Impressions | Number of times ads were shown. |
clicks | INTEGER | Clicks | Number of clicks the ads received. Always ≤ impressions; CTR by channel. |
cost | NUMERIC | Cost | Money spent on this ad group for the day (USD). Derived from clicks × per-channel CPC. |
Relationships
| Related data mart | On | Cardinality | Meaning |
|---|---|---|---|
| Campaign | campaign_id = campaign_id | N:1 | The campaign this spend was bought for. |
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
| Column | Type | Alias | Description |
|---|---|---|---|
campaign_id | STRING | Campaign ID | PK. Unique identifier for each campaign. |
campaign_name | STRING | Campaign Name | Human-readable name of the campaign. |
channel | STRING | Channel | Marketing channel. Controlled vocabulary: paid_search, paid_social, display, organic_search, email, webinar, content_syndication, direct, referral. |
objective | STRING | Objective | Primary goal. One of: awareness, lead_generation, conversion, retargeting. |
utm_source | STRING | UTM Source | UTM source tag identifying where the traffic originates. |
utm_medium | STRING | UTM Medium | UTM medium tag describing the type of traffic (e.g. cpc, email). |
utm_campaign | STRING | UTM Campaign | UTM campaign tag — the key ad-platform ↔ web-analytics join key. |
start_date | DATE | Start Date | Date the campaign went live. |
end_date | DATE | End Date | Date the campaign flight ended (nullable for always-on). Spend/touches fall within [start_date, end_date]. |
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
| Column | Type | Alias | Description |
|---|---|---|---|
lead_id | STRING | Lead ID | PK. Unique identifier for each lead or contact. |
account_id | STRING | Account ID | Company the lead belongs to; null for a lead not yet matched to an account. FK to Account |
created_at | TIMESTAMP | Created At | When the lead first entered the system (coincides with the is_lead_create touchpoint). |
source_channel | STRING | Source Channel | Channel that first brought in the lead. Equals the channel of the lead's is_lead_create touchpoint. Same controlled vocabulary as Campaign.channel. |
lead_score | INTEGER | Lead Score | Fit + engagement score for MQL gating. |
became_mql_at | TIMESTAMP | Became Mql At | When the lead reached marketing-qualified status; null if never MQL. created_at ≤ became_mql_at. |
became_sql_at | TIMESTAMP | Became Sql At | When the lead reached sales-qualified status; null if never SQL. became_mql_at ≤ became_sql_at. |
employee_band | STRING | Employee Band | Company-size bucket. Same vocabulary as Account.employee_band: 1-50 / 51-200 / 201-1000 / 1000+. For matched leads, agrees with the account. |
industry | STRING | Industry | Industry the lead's company operates in. For matched leads, agrees with the account. |
country | STRING | Country | Country where the lead is located; consistent with the account's region. |
lifecycle_stage | STRING | Lifecycle Stage | subscriber / lead / MQL / SQL / opportunity / customer. Consistent with the became_*_at timestamps and with the existence of an Opportunities row. |
Relationships
| Related data mart | On | Cardinality | Meaning |
|---|---|---|---|
| Account | account_id = account_id | N:1 | The company the lead works for. |
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
| Column | Type | Alias | Description |
|---|---|---|---|
opportunity_id | STRING | Opportunity ID | PK. Unique identifier for each sales opportunity. |
account_id | STRING | Account ID | Account the deal belongs to — the primary spine. FK to Account |
lead_id | STRING | Lead ID | Sourcing/converting lead the opportunity originated from; nullable. FK to Lead |
primary_campaign_id | STRING | Primary Campaign ID | Primary Campaign Source (single-touch sourcing); nullable. Equals the campaign of the sourcing lead's is_lead_create touchpoint. FK to Campaign |
created_at | TIMESTAMP | Created At | When the opportunity was created. |
stage | STRING | Stage | Current pipeline stage. One of: discovery, demo, proposal, negotiation, closed_won, closed_lost. |
amount | NUMERIC | Amount | ACV / deal size (USD). Correlates with the account's employee_band. |
close_date | DATE | Close Date | Date the opportunity was won or lost; null while open. |
is_won | BOOLEAN | Is Won | True iff stage = closed_won (then close_date is set); false with a close_date means closed_lost. |
sales_cycle_days | INTEGER | Sales Cycle Days | DATE_DIFF(close_date, created_at, DAY) for closed deals; null while open. |
owner | STRING | Owner | Sales rep who owns the opportunity (drawn from a small stable roster). |
Relationships
| Related data mart | On | Cardinality | Meaning |
|---|---|---|---|
| Account | account_id = account_id | N:1 | The company the deal is with. |
| Campaign | primary_campaign_id = campaign_id | N:1 | The campaign credited with sourcing the deal. |
| Lead | lead_id = lead_id | N:1 | The lead the deal grew out of. |
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
| Column | Type | Alias | Description |
|---|---|---|---|
transition_id | STRING | Transition ID | PK. Unique identifier for the stage change. |
opportunity_id | STRING | Opportunity ID | Opportunity that moved stage. FK to Opportunities |
from_stage | STRING | From Stage | Stage the opportunity left. |
to_stage | STRING | To Stage | Stage the opportunity entered. Contiguous chain: to_stage of row n = from_stage of row n+1; the last to_stage equals Opportunities.stage. |
transitioned_at | TIMESTAMP | Transitioned At | When the stage change happened. Within [Opportunities.created_at, close_date]. |
days_in_from_stage | INTEGER | Days In From Stage | Days spent in the previous stage — the velocity bottleneck driver. Sums (per opportunity) to the sales cycle for closed deals. |
Relationships
| Related data mart | On | Cardinality | Meaning |
|---|---|---|---|
| Opportunities | opportunity_id = opportunity_id | N:1 | The deal that moved stage. |
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
| Column | Type | Alias | Description |
|---|---|---|---|
touchpoint_id | STRING | Touchpoint ID | PK. Unique identifier for each marketing touch. |
lead_id | STRING | Lead ID | Lead that this touch belongs to. FK to Lead |
campaign_id | STRING | Campaign ID | Campaign associated with this touch. FK to Campaign |
occurred_at | TIMESTAMP | Occurred At | When the touch happened. |
channel | STRING | Channel | Channel where the touch occurred. Equals the campaign's channel; same controlled vocabulary as Campaign.channel. |
touch_type | STRING | Touch Type | Kind 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_credit | FLOAT | Touch Credit | W-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_touch | BOOLEAN | Is First Touch | True on the lead's earliest touch (exactly one per lead). W-shaped anchor. |
is_lead_create | BOOLEAN | Is Lead Create | True on the touch that created the lead (exactly one per lead; coincides with Lead.created_at). W-shaped anchor. |
is_opp_create | BOOLEAN | Is Opp Create | True 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 mart | On | Cardinality | Meaning |
|---|---|---|---|
| Campaign | campaign_id = campaign_id | N:1 | The campaign that produced this touch. |
| Lead | lead_id = lead_id | N:1 | The lead who was touched. |
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
| Column | Type | Alias | Description |
|---|---|---|---|
session_id | STRING | Session ID | PK. Unique identifier for the web session. |
lead_id | STRING | Lead ID | Known lead after identity stitching; null for never-identified visitors (the overwhelming majority). FK to Lead |
started_at | TIMESTAMP | Started At | When the session began. |
campaign_id | STRING | Campaign ID | Campaign that drove the session; null for organic/direct. FK to Campaign |
source | STRING | Source | Traffic source that referred the session; consistent with the driving campaign's utm_source. |
medium | STRING | Medium | Marketing medium (e.g. organic, cpc, email); consistent with the driving campaign's utm_medium. |
landing_page | STRING | Landing Page | First page viewed in the session. |
form_submits | INTEGER | Form Submits | Number of forms submitted during the session. ≥ 1 when is_conversion is true. |
is_conversion | BOOLEAN | Is Conversion | Whether the session produced a lead or demo request. Session→lead conversion ~1–3% overall, lowest on paid social. |
Relationships
| Related data mart | On | Cardinality | Meaning |
|---|---|---|---|
| Campaign | campaign_id = campaign_id | N:1 | The campaign that drove the visit. |
| Lead | lead_id = lead_id | N:1 | The lead this visit was later tied to. |
- 1
Install the Import Model plugin
One plugin, installed once, in your own OWOX workspace.
- 2
Import this model
Point it at this bundle and it creates every data mart above, joins and all.
- 3
Plug in your data and destinations
Connect your own sources and send the results where your team already works.
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.