DEV Community

Andreas Hatlem
Andreas Hatlem

Posted on

Marketing Attribution Models: A Developer's Guide to Knowing What Actually Works

"I spent $50k on marketing last month. Which half was wasted?"

This is the question every marketing team asks eventually. You're running Google Ads, Meta campaigns, email nurture sequences, SEO content, LinkedIn posts, and a podcast. Revenue is up, but which of those channels is actually driving it? And which ones are burning cash?

The answer depends entirely on your attribution model. Pick the wrong one, and you'll scale your worst channel while cutting your best one. I've seen companies kill profitable SEO programs because last-click attribution gave all the credit to branded search. I've seen others pour money into display ads because first-click made them look like customer acquisition engines.

This guide breaks down every major attribution model, when to use each one, how to implement them technically, and how to navigate the post-iOS14 world where third-party cookies are dying.

What Is Marketing Attribution?

Attribution is the process of assigning credit for a conversion to the marketing touchpoints that influenced it. A "touchpoint" is any interaction: an ad click, an email open, an organic search visit, a social media post view.

The problem is that most conversions involve multiple touchpoints. A typical B2B journey looks like this:

Day 1:  Google search → Blog post (organic)
Day 4:  Retargeting ad → Pricing page (paid)
Day 9:  Email newsletter → Case study (email)
Day 14: Direct visit → Sign up (direct)
Enter fullscreen mode Exit fullscreen mode

Who gets credit for that signup? The blog that introduced them? The ad that brought them back? The email that nudged them? The answer changes everything about how you allocate budget.

The Six Attribution Models

1. First-Touch Attribution

All credit goes to the first interaction.

Touchpoints:    Blog → Ad → Email → Signup
Credit:         100%   0%    0%      -

  Blog (organic)     Ad (paid)     Email        Signup
    [████████]        [        ]    [        ]    ✓
      100%               0%           0%
Enter fullscreen mode Exit fullscreen mode

Pros:

  • Simple to implement
  • Shows you which channels drive awareness and discovery
  • Great for understanding top-of-funnel performance

Cons:

  • Completely ignores everything that happened after the first visit
  • Overvalues awareness channels, undervalues nurture and conversion channels
  • A blog post from 6 months ago gets full credit for a deal closed by sales

Best for: Early-stage startups trying to figure out where new prospects come from. Content marketing teams justifying SEO investment.

2. Last-Touch Attribution

All credit goes to the final interaction before conversion.

Touchpoints:    Blog → Ad → Email → Signup
Credit:          0%    0%   100%     -

  Blog (organic)     Ad (paid)     Email        Signup
    [        ]        [        ]    [████████]    ✓
       0%                0%           100%
Enter fullscreen mode Exit fullscreen mode

Pros:

  • Also simple to implement — it's the default in Google Analytics
  • Shows you which channels close deals
  • Useful for short sales cycles where the last touch really does matter

Cons:

  • Ignores the entire journey that brought the customer to you
  • Overvalues bottom-of-funnel channels (branded search, retargeting, direct)
  • Makes your SEO and content programs look worthless

Best for: E-commerce with impulse purchases. Short sales cycles under 7 days. When you need a quick-and-dirty answer.

3. Linear Attribution

Equal credit to every touchpoint.

Touchpoints:    Blog → Ad → Email → Signup
Credit:         33%   33%    33%     -

  Blog (organic)     Ad (paid)     Email        Signup
    [███     ]        [███     ]    [███     ]    ✓
      33.3%             33.3%         33.3%
Enter fullscreen mode Exit fullscreen mode

Pros:

  • Acknowledges every touchpoint in the journey
  • No channel gets zero credit
  • Simple to understand and explain

Cons:

  • Treats all touchpoints as equally important — they're not
  • A user accidentally clicking an ad once gets the same credit as a 10-email nurture sequence
  • Doesn't reflect reality for most buying journeys

Best for: When you genuinely don't know which touchpoints matter more. As a starting point before moving to more sophisticated models.

4. Time-Decay Attribution

More credit to touchpoints closer to conversion.

Touchpoints:    Blog → Ad → Email → Signup
Credit:         10%   30%    60%     -

  Blog (organic)     Ad (paid)     Email        Signup
    [█       ]        [███     ]    [██████  ]    ✓
      10%               30%           60%
Enter fullscreen mode Exit fullscreen mode

The typical implementation uses exponential decay with a configurable half-life:

function timeDecayAttribution(
  touchpoints: Touchpoint[],
  conversionTime: Date,
  halfLifeDays: number = 7
): Map<string, number> {
  const credits = new Map<string, number>();
  let totalWeight = 0;

  for (const tp of touchpoints) {
    const daysBefore =
      (conversionTime.getTime() - tp.timestamp.getTime())
      / (1000 * 60 * 60 * 24);
    const weight = Math.pow(0.5, daysBefore / halfLifeDays);
    credits.set(tp.channel, (credits.get(tp.channel) || 0) + weight);
    totalWeight += weight;
  }

  // Normalize to percentages
  for (const [channel, weight] of credits) {
    credits.set(channel, weight / totalWeight);
  }

  return credits;
}
Enter fullscreen mode Exit fullscreen mode

Pros:

  • Reflects the intuition that recent touchpoints matter more
  • Configurable half-life lets you tune it to your sales cycle
  • Good balance of simplicity and accuracy

Cons:

  • Undervalues the initial discovery touchpoint
  • The half-life is somewhat arbitrary — 7 days vs. 14 days gives very different results
  • Still a heuristic, not data-driven

Best for: B2B with 2-4 week sales cycles. Lead nurture funnels. When bottom-of-funnel optimization is your priority.

5. Position-Based (U-Shaped) Attribution

40% to first touch, 40% to last touch, 20% split among the middle.

Touchpoints:    Blog → Ad → Email → Webinar → Signup
Credit:         40%    7%    7%      6%        40%
                                          (last touch)

  Blog (organic)     Ad        Email      Webinar     Signup
    [████████]      [█      ]  [█      ]  [█      ]   [████████]
      40%             6.7%       6.7%       6.7%         40%
Enter fullscreen mode Exit fullscreen mode

Wait — that diagram shows the signup getting 40%, but the signup is the conversion event, not a touchpoint. Let me clarify: the "last touch" is the last marketing interaction before conversion. So it's actually:

Touchpoints:    Blog → Ad → Email → Webinar → [Conversion]
Credit:         40%   6.7%  6.7%     40%

  Blog (first)     Ad (middle)   Email (middle)   Webinar (last)
    [████████]      [█       ]     [█       ]       [████████]
      40%             6.7%           6.7%              40%
Enter fullscreen mode Exit fullscreen mode

Pros:

  • Recognizes that discovery and closing are usually the most important moments
  • Good compromise between first-touch and last-touch
  • Works well for B2B where there's a clear introduction and decision moment

Cons:

  • The 40/20/40 split is arbitrary
  • Middle touchpoints might be more important than 6.7% in nurture-heavy funnels
  • Doesn't adapt to your actual data

Best for: B2B SaaS with defined funnel stages. When you care about both acquisition and conversion. Mature marketing teams with multiple active channels.

6. Data-Driven (Algorithmic) Attribution

Uses machine learning to analyze all conversion paths and assign credit based on actual impact. Different users who converted through different paths are compared to users who didn't convert.

Touchpoints:    Blog → Ad → Email → Webinar → [Conversion]
Credit:         15%   45%   10%      30%
(example — varies based on actual data)

  Blog (organic)     Ad (paid)     Email        Webinar
    [██       ]       [█████████]   [█        ]   [██████  ]
      15%               45%           10%           30%
Enter fullscreen mode Exit fullscreen mode

This model works by comparing conversion paths. If users who see an ad after reading a blog post convert at 3x the rate of users who only read the blog post, the ad gets more credit. It uses techniques like Shapley values or Markov chains.

Here's a simplified Markov chain approach:

// Markov chain attribution calculates the "removal effect"
// For each channel: what happens to conversions if we remove it?

interface TransitionMatrix {
  [from: string]: { [to: string]: number };
}

function markovAttribution(
  paths: string[][],     // e.g., [["organic", "paid", "email", "conversion"]]
  conversionNode: string = "conversion"
): Map<string, number> {
  // 1. Build transition probability matrix
  const matrix: TransitionMatrix = {};

  for (const path of paths) {
    for (let i = 0; i < path.length - 1; i++) {
      if (!matrix[path[i]]) matrix[path[i]] = {};
      matrix[path[i]][path[i + 1]] =
        (matrix[path[i]][path[i + 1]] || 0) + 1;
    }
  }

  // Normalize to probabilities
  for (const from of Object.keys(matrix)) {
    const total = Object.values(matrix[from])
      .reduce((a, b) => a + b, 0);
    for (const to of Object.keys(matrix[from])) {
      matrix[from][to] /= total;
    }
  }

  // 2. Calculate baseline conversion rate
  const baselineRate = calculateConversionRate(matrix, conversionNode);

  // 3. For each channel, remove it and recalculate
  const removalEffects = new Map<string, number>();
  const channels = Object.keys(matrix)
    .filter(k => k !== "start" && k !== conversionNode);

  for (const channel of channels) {
    const reduced = removeChannel(matrix, channel);
    const newRate = calculateConversionRate(reduced, conversionNode);
    removalEffects.set(channel, baselineRate - newRate);
  }

  // 4. Normalize removal effects to get attribution
  const totalEffect = [...removalEffects.values()]
    .reduce((a, b) => a + b, 0);

  const attribution = new Map<string, number>();
  for (const [channel, effect] of removalEffects) {
    attribution.set(channel, effect / totalEffect);
  }

  return attribution;
}
Enter fullscreen mode Exit fullscreen mode

Pros:

  • Based on actual data, not arbitrary rules
  • Adapts as your marketing mix changes
  • Can reveal non-obvious insights (e.g., a channel that doesn't convert directly but assists heavily)

Cons:

  • Requires significant data volume (thousands of conversions)
  • Black box — hard to explain to stakeholders why a channel's credit changed
  • Sensitive to data quality issues
  • Requires engineering investment to build and maintain

Best for: Companies with 500+ conversions per month. Mature marketing organizations. When you've outgrown rule-based models and need precision.

Choosing the Right Model

Here's the decision framework:

                    What's your monthly conversion volume?
                               |
                    ┌──────────┴──────────┐
                 < 200                  > 200
                    |                      |
              Rule-based              How long is your
              models only             sales cycle?
                    |                      |
            ┌───────┴───────┐      ┌───────┴───────┐
         < 7 days       > 7 days   < 30 days    > 30 days
            |               |         |              |
        Last-touch     Position-   Time-decay    Data-driven
        or Linear      based       or Position-  (if > 500
                                   based         conversions)
Enter fullscreen mode Exit fullscreen mode

But honestly, the model matters less than whether you're tracking touchpoints correctly in the first place. A mediocre model with complete data beats a perfect model with 40% data loss.

The Technical Foundation: Getting Your Data Right

UTM Parameters

Every link you share needs UTM tags. No exceptions.

https://yoursite.com/pricing
  ?utm_source=google
  &utm_medium=cpc
  &utm_campaign=brand_q1_2026
  &utm_content=pricing_cta
  &utm_term=attribution+software
Enter fullscreen mode Exit fullscreen mode

Store these server-side on first visit:

// middleware.ts or server-side handler
export function captureUTMs(req: Request) {
  const url = new URL(req.url);
  const utmParams = {
    source: url.searchParams.get("utm_source"),
    medium: url.searchParams.get("utm_medium"),
    campaign: url.searchParams.get("utm_campaign"),
    content: url.searchParams.get("utm_content"),
    term: url.searchParams.get("utm_term"),
    landingPage: url.pathname,
    referrer: req.headers.get("referer") || null,
    timestamp: new Date().toISOString(),
  };

  // Store in your database tied to a session/visitor ID
  // Do NOT rely solely on client-side cookies for this
  return utmParams;
}
Enter fullscreen mode Exit fullscreen mode

Server-Side Tracking

Client-side tracking is dying. Ad blockers kill JavaScript pixels. Safari's ITP limits cookies to 7 days (or 24 hours for classified domains). Firefox has Enhanced Tracking Protection. Chrome is phasing out third-party cookies.

The fix is server-side tracking:

// Instead of firing a pixel in the browser:
// <script>fbq('track', 'Purchase', {value: 99})</script>

// Send the event from your server:
async function trackConversion(event: ConversionEvent) {
  // Meta Conversions API
  await fetch(
    `https://graph.facebook.com/v19.0/${PIXEL_ID}/events`,
    {
      method: "POST",
      body: JSON.stringify({
        data: [{
          event_name: "Purchase",
          event_time: Math.floor(Date.now() / 1000),
          user_data: {
            em: hashSHA256(event.email), // hashed PII
            client_ip_address: event.ip,
            client_user_agent: event.userAgent,
            fbc: event.fbClickId, // from _fbc cookie
            fbp: event.fbBrowserId, // from _fbp cookie
          },
          custom_data: {
            value: event.amount,
            currency: "USD",
          },
        }],
        access_token: META_ACCESS_TOKEN,
      }),
    }
  );

  // Google Ads Conversion API (via Measurement Protocol)
  await fetch(
    `https://www.google-analytics.com/mp/collect?` +
    `measurement_id=${GA_ID}&api_secret=${GA_SECRET}`,
    {
      method: "POST",
      body: JSON.stringify({
        client_id: event.gaClientId,
        events: [{
          name: "purchase",
          params: {
            value: event.amount,
            currency: "USD",
            transaction_id: event.orderId,
          },
        }],
      }),
    }
  );
}
Enter fullscreen mode Exit fullscreen mode

Server-side events can't be blocked by ad blockers, aren't affected by cookie restrictions, and give you full control over the data.

First-Party Cookie Strategy

With third-party cookies going away, your first-party cookies are critical:

// Set a first-party visitor ID cookie from YOUR domain
function setVisitorId(res: Response) {
  const visitorId = crypto.randomUUID();

  res.headers.set(
    "Set-Cookie",
    `_vid=${visitorId}; ` +
    `Path=/; ` +
    `HttpOnly; ` +
    `Secure; ` +
    `SameSite=Lax; ` +
    `Max-Age=${60 * 60 * 24 * 365}` // 1 year
  );

  return visitorId;
}
Enter fullscreen mode Exit fullscreen mode

Key points:

  • HttpOnly prevents JavaScript access (more secure, survives ITP longer)
  • Set from your server, not from JavaScript (Safari treats JS cookies differently)
  • First-party only — no cross-domain tracking
  • 1-year expiry — but Safari ITP may cap this to 7 days for known trackers

The Post-iOS 14 Reality

Apple's App Tracking Transparency (ATT) changed everything in 2021. Here's what it broke:

  1. Meta/Facebook: Lost ~30% of conversion data overnight. ROAS reporting became unreliable.
  2. Cross-app tracking: Users must opt in. Only ~25% do.
  3. SKAdNetwork: Apple's privacy-preserving attribution gives you aggregated, delayed data with limited granularity.
  4. View-through attribution: Largely gone on iOS.

What this means for you:

Modeled conversions are the norm now. Meta, Google, and TikTok all use statistical modeling to fill gaps in their reporting. Their numbers are directionally correct but no longer precise.

Server-side is mandatory, not optional. The Conversions API (Meta), Enhanced Conversions (Google), and Events API (TikTok) are how you feed deterministic data back to the platforms. Companies that implemented server-side tracking recovered most of their lost signal.

Multi-touch attribution requires first-party data. You can no longer rely on third-party cookies or ad platform pixels to stitch together the customer journey. You need to build this yourself:

User visits (UTM captured) → Cookie set → Sessions tracked →
  → Conversion event → Stitch journey from YOUR database →
    → Apply attribution model
Enter fullscreen mode Exit fullscreen mode

This is exactly the problem tools like GetChannel are built to solve — unifying your touchpoint data into a single view and applying attribution models across channels without relying on third-party cookies.

Building vs. Buying Attribution

Let me be honest about the build-vs-buy tradeoff:

Building In-House

You'll need:

  • Event collection pipeline — capturing every touchpoint with UTMs, referrer, and user identity
  • Identity resolution — stitching anonymous sessions to known users across devices
  • Data warehouse — storing and querying millions of touchpoint records
  • Attribution engine — implementing the models from this article
  • Reporting UI — dashboards your marketing team can actually use
  • Ongoing maintenance — ad platform API changes, new channels, data quality monitoring

Realistic timeline: 3-6 months for an MVP with one engineer.

Using a Platform

Platforms like GetChannel handle the pipeline, identity resolution, and multi-model attribution out of the box. You add a tracking snippet, connect your ad platforms, and get attribution data across all your channels. It supports first-touch, last-touch, linear, time-decay, position-based, and data-driven models — so you can compare how different models tell different stories about the same data.

For most teams under 50 people, buying makes sense. Your engineers should be building product, not maintaining a data pipeline.

Common Attribution Mistakes

1. Attributing to the last non-direct click
Google Analytics defaults to this. It strips out "direct" visits and gives credit to whatever came before. The problem: a user who bookmarked your site and visited directly 20 times before buying gets zero credit on "direct." You lose all signal about brand loyalty and repeat engagement.

2. Ignoring assisted conversions
A channel that never converts directly but appears in 60% of conversion paths is incredibly valuable. Linear and data-driven models catch this. Last-touch doesn't.

3. Not segmenting by customer type
Attribution should differ for new vs. returning customers, enterprise vs. SMB, high-value vs. low-value. A single blended model hides important differences.

4. Trusting ad platform self-reporting
Meta will tell you Meta drove the conversion. Google will tell you Google drove it. If you add up their numbers, you'll have 3x your actual revenue. Use an independent attribution system.

5. Changing models and comparing to historical data
If you switch from last-touch to time-decay, your historical numbers will shift. Don't compare Q1 (last-touch) to Q2 (time-decay) and conclude that a channel's performance changed.

Practical Next Steps

  1. Audit your UTM coverage. Open a spreadsheet. List every link you share externally — ads, emails, social posts, partner sites. If any lack UTMs, fix that first. Attribution without complete UTM data is guessing.

  2. Implement server-side event capture. At minimum, capture UTM parameters, referrer, and landing page on your server when users arrive. Store it tied to a session ID.

  3. Start with position-based. It's the best balance of simplicity and accuracy for most businesses. You can always graduate to data-driven later.

  4. Compare models side by side. Run the same data through 2-3 models. If they all agree a channel is underperforming, cut it. If they disagree, investigate before making budget decisions.

  5. Look at attribution windows. A 7-day window vs. a 30-day window can completely change your results. Match the window to your sales cycle.

If you want to skip the infrastructure work and get multi-model attribution running this week, GetChannel handles the full pipeline — from touchpoint capture to cross-channel reporting. It's built for teams that need answers, not another data engineering project.

Have questions about attribution models or tracking implementation? Drop them in the comments — happy to go deeper on any of these topics.

Top comments (0)