Spotted: Multi-Touch Attribution Modeling

Attribution is a hot mess.

Even before you add multiple channels.

Even before you add all the people involved in the B2B buying process.

For B2B marketers, basic attribution models are not useful. First or last touch attribution might be simple to understand, and they might quickly fill-in-the-blank in a report you owe, but they do not explain why (after a ton of activity from dozens of people) a company did or didn’t buy your product. With basic attribution, you can’t convincingly take credit.

First and last touch attribution fails to explain even simple attribution problems — like which drink got you drunk.

But there is good news.

Attribution modelling is only a data problem.

Hard to solve, yes.

But there’s a common set of tasks to unify, cleanse, reformat & sync data that are similar to personalization, data cleansing & integration that marketing operations & growth engineers have become used to solving.

In this Spotted post, we’re sharing the methods we’ve seen teams at Pollfish, Drift, Lengow and more use to holistically track and attribute their buying cycles, and write it into a reporting tool so that marketing teams could earn the credit for, credibility in & confidence about their marketing strategies.

This is Spotted.

In our Spotted series, we break down and share the trends, tactics & techniques we see scaling SaaS teams do (not just what they say) on Hull and beyond. Subscribe below for the next article in the series.

First, you need to track your leads across every channel.

You need to have data before you can make sense of data.

This isn’t as straightforward as it sounds. What sounds obvious becomes incredibly challenging as the number channels and touchpoints explode.

(Because not every tool in your stack was built to ingest free trial product data, cross-domain tracking, anonymous website visits to review websites, and so on…)

But this is how people buy!

B2B buyers behave just like B2C buyers. There are just more people involved in each purchase.

If you’re going to engage in omnichannel marketing, you need to be able to track your leads & prospects there, and combine their data into one unified lead profile. This is called universal lead tracking - we’ve written about this previously in Spotted.

You need to track people but attribute to companies

In B2B, you sell to companies, not people. So your attribution model needs to be at the account-level, not at the person level.

A few B2B transactions are in a B2C style, where there is just one buyer and decision maker involved. Think of small business owner buying accounting software, or someone buying and then expensing flights & hotels on their corporate credit card.

For everything else, you need to roll up the activity of the many stakeholders in your buying process to account level. It is the behavior of these many stakeholders that determines whether the company buys from you or not.

But their behavior is still consumer-y. They research and engage with your brand and product across many different places just as they would when buying their own car, vacation, mattress, or today’s subscription fad. They’re looking across your:

  • Website
  • Live chat
  • Email
  • Sales reps
  • Review sites
  • Trying your product
  • Asking friends on social.

As with most data problems, this becomes a mess to organize, understand, and build a useful attribution model without structured thinking. Here are the six tactical takeaways we’ve Spotted to guide how you think about multi-touch attribution modelling.

A quick aside: Some of these examples include tools on Hull’s customer data platform, including Hull Processor - a real-time editing environment for your customer data. Whether you use Hull or not, the examples are illustrative and showcase the principles needed to solve the data integration challenges of attribution modelling.

Tactical Takeaway #1: Have a stable, centralized “company ID”

Even if you can join the dots around people in your buying journey and say this CFO or this marketing manager did exactly all these things, you still need to build the picture at account level to understand the whole B2B buying journey.

To this, you need a stable ID (like a database ID) you can associate all known data points about each company (including lead sources and other data you need for your attribution model) as well as every person (anonymous or identified) that are associated with the company. This identifier becomes the key in your “source of truth” database, whether that’s in your CRM, marketing automation tool, or customer data platform.

Different tools in your stack will have different methods for resolving and deduplicating companies within their databases. With layers of different tools and legacy data, this can often be incredibly messy.

To bring this all together under one ID, you may need to write some custom logic to merge company profiles and connect the right people to companies consistently. For instance, if your data from other tools is messy, we see teams using company-level enrichment data like Clearbit to control and backfill your person-company level mapping.

const { clearbit, domain } = user;

// Define which domain to use to identify the account
const accountDomain = domain || clearbit.employment_domain;

// Link the User to the account identified by accountDomain
hull.account({ domain: accountDomain });

Voila! Now you have a company-level entity to attribute all the actions in your buying cycle

Tactical Takeaway #2: Capture actions by anonymous website visitors & prospects and write to the account level

Most tools make it relatively easy to associate known, identified people with accounts. For instance, having someone’s email and extracting their email domain. This person:company mapping can be synced across many different tools.

But modern buying behavior means you don’t often have that data — what do you with everyone else (the 95%?) who don’t fill in a lead form, signup for your product, or enter their email through chat?

To attribute all the activity, you need a method to associate people’s actions with the companies they work before they “signup”. Actions like:

  • Website sessions
  • Chat conversations
  • Review site visits
  • Social media actions

Each of these has (permanent or temporary) identifiers that you can associate with people. For instance, website sessions can be tracked with a sessionId. This may be the link between seeing if an account is highly engaged (viewing pricing, documentation, case studies etc.) or not. This should both trigger sales outreach, but also your attribution model.

There are two common methods for stitching this data together:

  1. Passing hidden fields on form conversion
  2. Reverse IP lookup

Hidden fields

Hidden fields are a way to append additional data in a form submission that isn’t visible in the form interface. Since form fields usually involve a unique personal identifier (like email or domain), you can associate the previous id's.

You could say ed@hull.io looked at the pricing page, and twelve blog posts before submitting a demo request. This should all appear in a unified lead profile, so your lead record does not start with only Demo requested and miss all the valuable web session data beforehand (and therefore mis-attribute the conversion and revenue too).

But not everyone converts right away, and not every valuable action is an email conversion. You need a method to associate the rest.

Reverse IP lookup

Unlike B2C, companies typically have a wider footprint that can be tracked. In the same way, someone who walks into the Hull office each morning probably works for Hull, that same person’s web behavior whilst they’re at the office is probably related to work at Hull.

Since IP addresses are commonly captured with web analytics tools and server logs, you can associate companies web activity (on and off your website) by using reverse IP lookup — “show me the company behind this IP address”.

The best reverse IP lookup tools pair reverse IP lookup with data enrichment (beyond just a company name), a “clean” & stable identifier (as discussed already), and APIs to integrate that data neatly into all your tools. This is why we see so many teams using Clearbit Reveal data for their reverse IP lookup services.

Voila! Now you can track which companies are engaging with your brand & product, beyond just your identified leads.

Tactical Takeaway #3: Stamp UTM parameters from anonymous visitors at the account level

Now you can track anonymous activity (the vast majority of people engaged with your company), you need to start assembling that complete picture of engagement at the account level.

One of the most common anonymous activities is elements of this taking UTM parameters from your web sessions and stamping this at the account level, so you can understand which campaigns, sources, content, and so on have driven engagement amongst an account.

We spotted team’s using Hull Processor to stamp the first session UTM parameters at account level for new accounts.

// Stamp first session UTM parameters at account level
if (_.get(account, "id", null) !== null &&
    _.get(account, "domain", null) !== null &&
    _.get(account, "first_session_initial_utm_source", null) === null &&
    _.get(user, "first_session_initial_utm_source", _.get(user, "traits.first_session_initial_utm_source", null)) !== null) {
  const acctTraits = {
    first_session_initial_utm_source: _.get(user, "first_session_initial_utm_source", _.get(user, "traits.first_session_initial_utm_source", null)),
    first_session_initial_utm_campaign: _.get(user, "first_session_initial_utm_campaign", _.get(user, "traits.first_session_initial_utm_campaign", null)),
    first_session_initial_utm_term: _.get(user, "first_session_initial_utm_term", _.get(user, "traits.first_session_initial_utm_term", null)),
    first_session_initial_utm_medium: _.get(user, "first_session_initial_utm_medium", _.get(user, "traits.first_session_initial_utm_medium", null)),
    first_session_initial_utm_content: _.get(user, "first_session_initial_utm_content", _.get(user, "traits.first_session_initial_utm_content", null))
  };

  const acctIdent = _.pick(account, ["id", "domain", "external_id"]);
  hull.account(acctIdent).traits(acctTraits);
}

For B2B attribution this significant - with reverse IP lookup you can track and attribute your paid campaigns at visitor level, not just last-touch leads that given their email. This makes it much easier to understand where you’re getting returns from your performance marketing and to justify increases in investment.

Performance marketing is regularly the top lead source in hypergrowth SaaS startups we see. Since it’s one of the few channels that can continue to scale (as organic channels get maxed out), they often attract big budgets, become a separate line item in the budget, and attract more finance & executive interest.

By stamping UTM attributes at account-level for visitors, it magnifies insights into one of the biggest acquisition channels in a hypergrowth SaaS business. This enables B2B performance marketers to focus their spend and campaigns, and optimize their ROAS. Together with account-based retargeting ads, they’re one of the biggest growth levers we’ve seen teams implement.

Voila! You can now see which traffic sources and paid campaigns are driving your account-level engagement at the website visitor level.

Tactical Takeaway #4: Progressively profile prospects from each account

Though you sell to accounts, you must still track individuals. It’s important to understand who each of these leads and prospects may be. You need to bring your attribution back to the person-level.

Who are the users & champions? The budget holders? The tire-kickers?

For identified leads, it’s easier to track and associate their web behavior and to understand who they are within an organization using data enrichment. But for leads & prospects who you haven’t yet identified (i.e. captured their email), it isn’t possible to enrich them.

How can you profile website visitors personas?

If you think of your website like one, elaborate form (buttons, links, choices…), how should you design your content and navigation so that you can progressively profile prospects and identify different personas within your engaged accounts?

A simple way to do this is with interest-based segments to group similar, tracked behaviors like their website folder views (/blog/ /docs/ /pricing/ /signup/ etc.) and UTM parameters.

Here’s a simple example from one of our customer’s where they’re looking at any page view, page title, or form submission related to a topic of interest.

For simplicity, portability, and to build attribution models, we can then stamp an attribute on the person level to sync to individual tools too. These can be used to synthesize down and compute conversation starters for sales reps too.

if (isInSegment("Interests - Amazon")) {
  traits({ interests: 'amazon' });

With data enrichment and interest-based profiling (on your website, inside your product, in conversations - everywhere!), you can segment and understand the personas engaging with your brand & product (both identified & anonymous) so you can track, attribute, and react to each companies progress through the buyers’ journey.

  • Who is active in the product?
  • Who is engaged with our marketing content & nurture flows?
  • Who is looking at our pricing?
  • Who is looking at our documentation?
  • Who is showing up to sales meetings?
  • Who is paying for it?

… and who isn’t?

Voila! You can now begin to identify champions and budget holders within your engaged accounts.

This is Spotted.

In our Spotted series, we break down and share the trends, tactics & techniques we see scaling SaaS teams do (not just what they say) on Hull and beyond. Subscribe below for the next article in the series.

Tactical Takeaway #5: Build an account-based attribution model statement

Now you have two parts of the puzzle - the engagement of the account overall, and the different types of people within that account. You need to bring these two parts together.

It’s not enough to think about individual leads in an organization when each (can) contribute to the buying journey. Optimizing within functional silos does not optimize for account-level engagement. Marketing converts whoever enjoys opt-ins & nurture flows, sales reps book in whoever likes meetings, product optimizes for the most active users…

None of this is joined-up, account-level thinking. This is not how B2B buying happens, so this cannot be an accurate B2B attribution model.

Liam Boogar from Madkudu puts it best in The Fallacy of Job Titles.

It is all too easy to lose track of the fact that selling B2B software means that a company is going to buy your software, not a person. There are users, decision-makers, stakeholders and other advisors in the buying process, but at the end of the day a company is going to make a decision about whether to pay another company for their solutions.

Liam Boogar-AzoulayMadkudu

Your multi-touch attribution models for B2B need to show how this chaotic buying journey all comes together. Unify everything around a stable account-level ID (including anonymous activity), progressively profile the people in the buying process, and then build an attribution model to match this all together.

Don’t get lost in tooling & techniques at this point. Your attribution model must start with some sort of statement - a description for how you want to attribute your buying journey. This is unique to your company, product & buying cycle.

Furthermore, as your teams adjust the different offers, methods of capturing leads & signing up, your attribution model will change. It needs to be flexible enough to be adaptable (not cast in stone), whilst still producing useful retroactive data.

For the team at Drift, their product serves many teams across an organization (sales, marketing, support…) and many different levels of seniority (from Chief Revenue Officer to sales rep). They have dozens of unique offers and opportunities to signup.

Instead of getting lost in the fallacy of job titles, they build this statement for their multi-touch attribution model.

For the original source fields, we'd like the highest ranking source & details on the oldest date that any of these events occurred. If two events of the same rank occur, we want the earliest in the day.

For the latest source fields, we'd like the highest ranking source & details on the most recent date that any of these events occurred. If two events of the same rank occur, we want the last in the day.

Everything has the same logic on the account level, but we of course want the oldest source across all users on that account for the original source and the latest across all for the latest.

Attribution model statementDrift

In simple terms, this is a first-and-last touch multi-channel model with some ordered preferences applied to certain lead sources (like signups being more important than chat conversations).

With this statement created, you’re then able to write an attribution model that truly reflects how companies buy from you.

Voila! You’re now able to describe how you can model your customer’s B2B buying journey

Tactical Takeaway #6: Write your custom attribution model

Now you have your attribution model statement, you need to be able to bring it to life.

Earlier, we discussed how multi-touch attribution is just another data integration problem. You need to:

  1. Unify data tracked from across your buyers journey from different tools
  2. Build identity resolution strategies to tie the right data to people, companies, and people-to-companies
  3. Map & sync data across your tools in real-time
  4. Transform streams of real-time person-level events into account-level attributes
  5. Apply the custom logic of your attribution model statement
  6. Be able to seamlessly update and extend your attribution model as your buying process updates

A customer data platform is designed to solve the first three criteria. Here we’re going to share a sample of how to solve the last three last criteria using the example from above.

Transform streams of real-time person-level events into account-level attributes

We can start by transforming the stream of different person-level events into account-level attributes. In this example, attribution is built entirely around the idea of Lead Source (when someone enters their email or joins a campaign flow).

Remember, this is multi-touch and multi-channel attribution, and in order of priority (signups are more important than other lead sources). For this, we want to update based on events from any source. In this example we’ll consider these six examples in order of :

Event Description
Signed Up They signed up to use your product
Email Captured Marketing captured an email via an opt-in offer
Email Captured Marketing captured an email via live chat conversation
Meeting Booked Sales captured an email through booking a meeting
User Created Sales prospected for an email within a target account
Visited Review Page Marketing tracked a visitor to a review website

Here’s a code snippet to show how that works.

// Stamp lead source signup data
function createTraitsFromEvent(eventData: any, prefix: string = ""): any {
  const traits = {};

  if (eventData.name === "Signed Up") {
    const route = _.get(eventData, "properties.route") ? _.get(eventData, "properties.route").split("?")[0] : "ORGANIC";
    if (route.indexOf("app.domain.com/onboarding") !== -1) {
      _.set(traits, `${prefix}lead_source`, "Signup");
      _.set(traits, `${prefix}lead_source_detail`, route);
    }
    else if (eventData.name === "Email Captured") {
    const pageUrl = (_.get(eventData, "properties.route", "") || _.get(eventData, "context.path", "")).split("?")[0];
    // Email captured by marketing forms pages. e.g. webinars
    if (pageUrl.indexOf("domain.com/webinars") !== -1) {
      _.set(traits, `${prefix}lead_source`, "Webinar");
      _.set(traits, `${prefix}lead_source_detail`, pageUrl);
    }
    // Email captured from chat conversations
    else {
      _.set(traits, `${prefix}lead_source`, "CQL");
      _.set(traits, `${prefix}lead_source_detail`, pageUrl);
    }
  }
  // Sales cadence resulted in a meeting booked
  else if (eventData.name === "Meeting Booked" && eventData.connector === "Calendly") {
      _.set(traits, `${prefix}lead_source`, "Outbound");
      _.set(traits, `${prefix}lead_source_detail`, "SDR Cadence");
    }
  // Prospected from companies visiting website
  else if (eventData.name === "User created" && eventData.connector === "Clearbit") {
    _.set(traits, `${prefix}lead_source`, "Revealed Companies");
    _.set(traits, `${prefix}lead_source_detail`, "Anonymous Visitor");
  }
  // Review website site visitors
  else if (eventData.name === "Visited Review Page") {
    _.set(traits, `${prefix}lead_source`, "Revealed Companies");
    _.set(traits, `${prefix}lead_source_detail`, "G2Crowd");
  } else {
    return traits;
  }

  // Always set the timestamp
  _.set(traits, `${prefix}lead_source_timestamp`, eventData.created_at);

  return traits;
}

You can extend this for all the different types of signups actions, email captures, and other sources of leads (where you get a person with an email taking an action forward in the buying cycle) like meetings booked from SDR outreach, a live chat conversation, or a lead from a review site like G2Crowd.

Apply custom logic of the attribution model statement

In Drift’s example, they want to set the first and last touch across different channels and prioritize actions on the day.

For the first touch to be set, there must be no previous matching event. To do this, you need to lookup if there is an existing lead_source. If not, write the oldest event (i.e. the first of the day) into the user (person) level, then onto the associated account (company) level too.

// Step 1 - Check if the user has attribution/lead_source set,
// If not process the first matching event as the initial attribution
if (_.get(eventResult.user, "traits_attribution/lead_source", "n/a") === "n/a") {

  // Get the oldest event and write the attributes
  let firstEvent = _.first(sortedEvents);
  let eventTraits = createTraitsFromEvent(firstEvent);
  let eventIndex = 0;

  // If the first event is not a key event, obtain the first matching one
  while (_.keys(eventTraits).length === 0 && eventIndex < sortedEvents.length) {
    eventIndex += 1;
    firstEvent = _.nth(sortedEvents, eventIndex);
    eventTraits = createTraitsFromEvent(firstEvent);
  }

  traitsObj = _.merge(traitsObj, eventTraits);

  // Check if the user is linked with an account and
  // Check if this account has attribution/lead_source set
  // If not assign the same attribution/lead_source as for the user
  if (eventResult.account.id && _.get(eventResult.account, "attribution/lead_source", "n/a") === "n/a") {
    accountTraitsObj = _.merge(accountTraitsObj, eventTraits);
  }
}

For the last touch attribution, you can stamp the latest events as a last_lead_source attribute. You can write this to update each time there’s a new “latest” event.

Seamlessly update and extend your attribution model as your buying process updates

The attribution model should be extensible. As you add new campaigns and offers, it should be simple to append additional lead source options to the right part within the logic, manage the addition of new products, or to manage migrations and updates (such as maintain attribution after migrating a blog from blog.domain.com to domain.com/blog).

With every event from every channel now stamped as Lead Source attribute at person-level, but also company-level attribute, you can sync this across your tools (like company name, employee size, or any other account-level attribute). Since the lead source data is associated to the account it is straightforward to create a report in Salesforce, or export into spreadsheet.

Voila! You now have a real-time multi-touch attribution model that matches your company’s customer journey.

Tactical Takeaway #7: Turn attribution model & analysis into sales triggers

At Hull, we believe data shouldn’t just be for pulling insights. Data should trigger action.

You can use the same data flows which power your multi-touch attribution model to empower your sales and marketing team. Now you can identify (and attribute) the moments across your buyer’s journey, you should be able to react.

  1. Use universal lead tracking to aggregate all the signals and progress through
  2. Cue your sales team in with real-time conversation starters to the right leads.
  3. Align your sales and marketing teams with shared visibility over the entire buying process.

Voila! You’re now able to turn data into both insight and action

Best-fit criteria for multi-touch attribution modelling

The multi-touch attribution modelling tactics we’ve spotted are best for certain teams. Even if attribution doesn’t trigger immediate action (like sales outreach), the insights should drive actions within teams to optimize and focus efforts.

Established lead gen funnel

Once you’ve established product-market fit, you’ve built out at least one channel, and you’re scaling your sales team, it can make sense to look at adding (and therefore tracking & attributing) additional channels.

Have multiple channels or offers

If your sales teams are able to work qualified leads from more than one place (beyond just demo requests), particularly if you have a lot of anonymous website visitors, then it makes sense to add smarter attribution models to focus their attention

Have long sales cycles

If your sales cycles stretch over multiple interactions over weeks, and months, you need to understand how these interactions and break down over time. It becomes too broad for even a good account executive to get a complete handle on alone.

Teams are already optimizing

The insights from multi-touch attribution should inform what teams should focus on. Sales, marketing, product, and so on should have the basics of their operation already in place, and some level of attention be on improving and optimizing for.


Implementing your own multi-touch attribution model?

This Spotted post outlined the principles we've seen teams using in production. If you're interested in learning more about how to bring attribution modelling like this to your teams, or you have more questions, chat to us on the right here.

Did you learn something new?

We love connecting the dots at Hull. If you want to learn how to connect ideas, tools & data in creative ways, then subscribe for more articles like this.

Ed fry
Ed Fry

'Ed of Growth at Hull, working on all things content, acquisition & conversion. Conference speaker, flight hacker, prev. employee #1 at inbound.org (acq. HubSpot).

If you've questions or ideas, I'd love to geek out together on Twitter or LinkedIn. 👇