# B2B attribution: grain, CRM source fields and self-reported discovery

In B2B the unit that buys (an account, through an opportunity) is not the unit that clicks (a person). Most B2B attribution errors are grain errors, not model errors.

## Contents

- Choose the grain
- What the CRM source fields actually record
- CRM checks before any source or influence report
- Crediting opportunities without multiplying value
- Account grain
- Self-reported attribution in B2B
- Sending CRM outcomes back to ad platforms
- Worked example

## Choose the grain

Pick one grain per view and name it in the view's title.

- Contact grain: "which campaigns create new contacts or MQLs". Unit is one person, deduplicated by the CRM contact. Good for lead generation operations. Bad for revenue: one deal has several contacts.
- Opportunity grain: "which campaigns precede new pipeline or won revenue". Unit is one opportunity, amount counted once. The default for pipeline questions.
- Account grain: "which programmes reach and move buying groups". Unit is one account, with membership dated. Needed for ABM and long cycles with many stakeholders.

Date each outcome from the event, not from the record's current state:

- MQL and SQL outcomes take their date from a dated stage-entry property or a stage history snapshot. Never filter on the current lifecycle stage: it rewrites history whenever a record moves on or back, so last month's MQL count changes every time you rerun it. Read the dated property's internal name in the portal or org.
- Pipeline views use opportunity amount from a dated snapshot, with the snapshot date in the view title.
- Weighted pipeline (amount times win probability) is expected value. Label it so, and never show it as revenue or use it in ROI or ROAS.

Funnel rates must keep one grain per ratio. Twelve opportunities divided by 90 submissions mixes opportunity and submission grains; it may be a useful productivity ratio if named as such, but it is not a conversion rate.

## What the CRM source fields actually record

HubSpot (knowledge base checked 2026-10-17 and 2026-10-18):

- Original Traffic Source is the first known web source. If a new contact is matched to earlier anonymous visits, it becomes the source of the earliest visit. It can only use visits the tracking code recorded and could match to the contact; visits on another device, before tracking consent, or before the data history began are invisible to it.
- Latest Traffic Source changes only when a new session is created. Imports never change it.
- HubSpot does not document what traffic source value an imported or integration-created contact with no web session receives. Check a sample of such records in the portal, and use Record Source, not traffic source, for how the record was created. "Offline sources" is a listed value; HubSpot recommends Record Source instead for anyone who used it to track how contacts were created.
- Companies and deals mirror these properties from associated contacts. Before reading a deal's source as "how the deal started", check which contacts are associated and when; the deal can carry the history of whichever contact HubSpot used, not the first buyer. HubSpot does not document which contact wins.
- Record Source is how the record was created (import, integration, app). Do not use traffic source to answer that.
- Manual edits to a traffic source clear its drill-downs. A cleanup workflow that "fixes" sources destroys the campaign detail.

Salesforce (help pages read 2026-10-20):

- Primary Campaign Source is the default influence model: 100% to the campaign in the opportunity's Primary Campaign Source field.
- First touch, last touch and even distribution models are for Account Engagement users, turned on with the Additional Campaign Influence Models setting.
- Auto-associated influence needs a campaign member who holds a contact role on an open opportunity. No new influence records are created after the opportunity closes.
- Lead Source is a picklist with admin-defined values, filled by forms, integrations or reps.
- Enabled models, the time frame and field filters are org settings. Read them in Setup with an admin.

Practitioner rules for any CRM:

- Never overwrite original source with a later touch, and never copy original source into later touches. Keep original, latest and per-submission context as separate fields.
- Store click IDs and UTM values from the same touch together. A record whose UTM fields are from the first touch while a click ID was overwritten by the latest click produces uploads and reports that disagree with themselves.
- Keep "unknown" and "direct" as legitimate values. Do not require a UTM for a form to be accepted.

## CRM checks before any source or influence report

Run these read-only and print the results in the brief before any channel share. Read field internal names in the portal or org first; names below are labels. Record results as counts and interaction types per pseudonymous contact (contact 1, contact 2), never names, emails or company names.

HubSpot:

- Zero-contact deals: count closed-won deals in scope with no associated contact, and their share of amount. Revenue attribution credits nothing to them.
- Multi-contact deals: pick one known deal with several associated contacts and compare its attribution report interactions with each contact's timeline. Record whether interactions from every contact appear.
- Inbound after offline creation: count contacts whose Original Traffic Source is Offline sources (or another non-web value) and who later have a high-intent form submission (demo request, contact sales). HubSpot does not say whether a later visit changes Original Traffic Source, so detect it in the data. Report the pipeline on those contacts' deals as a separate line, "inbound after offline creation", so marketing-sourced pipeline is not undercounted as offline.

Salesforce:

- Zero-contact-role opportunities: count opportunities in scope with no contact roles and their share of amount. Auto-associated influence cannot reach them. If the share is above 10% of amount, lead the brief with it.
- Lead Source: report how it is populated (which forms set it, whether reps edit it, the share left blank). Treat it as self-declared or rep-entered. Never use it as the first web touch.
- Primary Campaign Source: report its fill rate on opportunities in scope and how it gets set (conversion from a lead, a rep, an automation). An empty field gives the default model nothing to credit.
- Name the Salesforce source fields used in the inventory's `crm` block, with how each is populated.

## Crediting opportunities without multiplying value

Build the opportunity table at one row per opportunity first, then attach credit.

- Originated: the opportunity was created from this submission or campaign response. One campaign per opportunity, amount counted once.
- Associated: an account in the campaign's audience had an opportunity open during a defined window. Overlapping by design; never add across campaigns.
- Influenced: touches by associated contacts within a lookback before an anchor date, distributed with weights that sum to 1 per opportunity, plus an unallocated bucket where the evidence does not support a link.

Choose the anchor by the question and name it in the view title:

- Pipeline created ("which campaigns precede new pipeline"): anchor lag and lookback on opportunity create date. Touches after creation are not eligible.
- Won revenue ("which campaigns precede closed-won revenue"): anchor on close date. Touches between creation and close become eligible, so this view answers a different question.
- Compute the lag distribution on the same anchor as the lookback. A lag measured to close date and a lookback counted back from create date do not fit together.
- When the p90 lag is longer than an ad platform's maximum window (LinkedIn 90 days, or 180 or 365 for some Conversions API types; GA4 key events and Google Ads clicks 90 days), the platform's own numbers are truncated by design. Answer pipeline questions from the CRM or warehouse view.

HubSpot revenue attribution counts each interaction separately and attributes nothing to deals with no associated contact. HubSpot does not say whether all associated contacts' interactions are used; check one known multi-contact deal before relying on it, and count zero-contact deals and their share of revenue first.

## Account grain

- Account membership changes. Record each contact's account with effective dates, and attribute a touch to the account the contact belonged to at the touch date.
- Separate known membership (the CRM association) from inferred matches (email domain, reverse IP). Report them as separate coverage numbers.
- An existing late-stage deal does not become newly sourced pipeline because a second stakeholder downloads a guide.

## Self-reported attribution in B2B

- Ask once, on the highest-intent form (demo request, contact sales) or in the first sales call, as an optional open-text field. Store the raw answer on the submission record, not on the contact, so repeat submissions keep their own answers.
- Raw answers often hold names, employers or email addresses. Report only coded counts; never quote raw answers in the brief or the chat.
- Code answers with a versioned codebook (peer or colleague, podcast, community, event, search, AI assistant, social post, review site, existing customer, other). Keep the raw text.
- Report at the same grain as the pipeline view (by opportunity). Show response rate.
- Compare with original source and the first observed touch. A large "direct" or "organic" share in software attribution beside a large "podcast" or "peer" share in self-reported answers marks demand creation that click data cannot see.

## Sending CRM outcomes back to ad platforms

Platform attribution improves only if the platform sees the outcomes that matter. LinkedIn's Conversions API accepts lead, qualified lead, marketing qualified lead and sales qualified lead conversion types (the last two from API version 202608), with 180 or 365-day windows for some types. Design and QA of these uploads belong to the offline-conversion-import and linkedin-and-openai-ads-conversions skills. If neither is installed: send each CRM stage as its own conversion type, keep the original event time and the click ID or hashed identifier captured with the touch, and never assign the eventual contract value to the first form submission. LinkedIn rejects events whose `conversionHappenedAt` is more than 90 days in the past (schema page, read 2026-10-20), even on rules with 180 or 365-day windows. Send stage events within days of the stage date, and do not plan backfills of older stages.

## Worked example

Fictional. Acme Analytics sells annual contracts. Days are counted from the start of the quarter.

CRM check first: of 40 opportunities created in the quarter, 3 have no contact roles, 6% of amount. That is below the 10% threshold, so it is a line in the brief, not the headline.

One opportunity, $48,000, created on day 30 after a demo, closed won on day 75, two associated contacts:

- Contact 1: organic search visit (day 2), webinar registration from an email campaign (day 12), demo request from a paid search click (day 28).
- Contact 2: LinkedIn ad click (day 10), direct visit (day 18), organic social click (day 41).

Contact grain, joined naively: each contact's path receives the $48,000, so the report shows $96,000 of influenced pipeline from one $48,000 deal.

Influenced pipeline created, opportunity grain, 90-day lookback before create date (day 30), linear per interaction, direct excluded: organic search, email, paid search and LinkedIn are eligible, $12,000 each. The day 41 organic social click came after creation, so it is not eligible. Total $48,000.

Influenced won revenue, 90-day lookback before close date (day 75): the day 41 organic social click becomes eligible, so five touches get $9,600 each. Same deal, different question; the anchor goes in each title.

Originated view: the opportunity was created from Contact 1's demo request, so paid search originated $48,000; LinkedIn originated $0 but appears in both influenced views. Each is a correct answer to a different question; label them.
