ScaledByDesign/Insights
ServicesPricingAboutContact
Book a Call
Scaled By Design

Fractional CTO + execution partner for revenue-critical systems.

Company

  • About
  • Services
  • Contact

Resources

  • Insights
  • Pricing
  • FAQ

Legal

  • Privacy Policy
  • Terms of Service

© 2026 ScaledByDesign. All rights reserved.

contact@scaledbydesign.com

On This Page

The Last-Click LieAttribution Models ExplainedImplementing Multi-Touch AttributionStep 1: Capture Every TouchpointStep 2: Build the Customer JourneyStep 3: Apply Attribution ModelsThe Attribution DashboardPrivacy-Compliant Attribution
  1. Insights
  2. Split Testing & Tracking
  3. Attribution Modeling Beyond Last-Click — What DTC Brands Actually Need

Attribution Modeling Beyond Last-Click — What DTC Brands Actually Need

June 1, 2026·ScaledByDesign·
attributionanalyticsmarketingecommercedata

The Last-Click Lie

A DTC brand spent $500K/month on marketing. According to last-click attribution: Google Brand Search drove 60% of conversions. Facebook drove 5%. They cut Facebook spend by 50%. Two weeks later, Google Brand Search conversions dropped 35%.

Why? Facebook drove awareness. Customers saw Facebook ads, remembered the brand, then Googled the brand name and bought. Last-click gave all credit to Google. When they cut Facebook, fewer people searched for the brand.

Last-click attribution is the default in every analytics platform because it's simple. It's also dangerously wrong for any business with a multi-touch customer journey.

Attribution Models Explained

Last-Click:
  Touchpoints: Facebook Ad → Blog Post → Google Search → Purchase
  Credit:      0%             0%          100%           ←
  Problem:     Ignores everything that led to the search

First-Click:
  Touchpoints: Facebook Ad → Blog Post → Google Search → Purchase
  Credit:      100%          0%          0%             ←
  Problem:     Ignores everything after initial awareness

Linear:
  Touchpoints: Facebook Ad → Blog Post → Google Search → Purchase
  Credit:      33%           33%         33%            ←
  Better:      At least acknowledges every touchpoint
  Problem:     Treats all touchpoints as equally important

Time-Decay:
  Touchpoints: Facebook Ad → Blog Post → Google Search → Purchase
  Credit:      15%           25%         60%            ←
  Better:      Gives more credit to recent touchpoints
  Problem:     Still under-credits top-of-funnel

Position-Based (U-Shaped):
  Touchpoints: Facebook Ad → Blog Post → Google Search → Purchase
  Credit:      40%           20%         40%            ←
  Best default: Credits both discovery and conversion touchpoints

Implementing Multi-Touch Attribution

Step 1: Capture Every Touchpoint

// Track all marketing touchpoints in a customer journey
interface Touchpoint {
  timestamp: Date;
  channel: string;       // "facebook", "google", "email", "direct"
  campaign?: string;     // UTM campaign
  medium: string;        // "cpc", "organic", "social", "email"
  source: string;        // "google", "facebook", "klaviyo"
  landingPage: string;
  sessionId: string;
  userId?: string;       // If logged in
  anonymousId: string;   // Cookie-based ID
}
 
// Capture on every page load
function captureTouchpoint() {
  const params = new URLSearchParams(window.location.search);
  const referrer = document.referrer;
  
  const touchpoint: Touchpoint = {
    timestamp: new Date(),
    channel: determineChannel(params, referrer),
    campaign: params.get("utm_campaign") || undefined,
    medium: params.get("utm_medium") || inferMedium(referrer),
    source: params.get("utm_source") || inferSource(referrer),
    landingPage: window.location.pathname,
    sessionId: getSessionId(),
    anonymousId: getAnonymousId(),
  };
 
  // Store in first-party storage (not just cookies — those get blocked)
  appendToJourney(touchpoint);
}

Step 2: Build the Customer Journey

// Reconstruct the full journey at conversion time
async function getCustomerJourney(customerId: string): Promise<Touchpoint[]> {
  // Merge anonymous and authenticated touchpoints
  const anonymousId = getAnonymousIdForCustomer(customerId);
  
  const touchpoints = await db.touchpoints.findMany({
    where: {
      OR: [
        { userId: customerId },
        { anonymousId: anonymousId },
      ],
    },
    orderBy: { timestamp: "asc" },
  });
 
  // Deduplicate and clean
  return deduplicateTouchpoints(touchpoints);
}

Step 3: Apply Attribution Models

function attributeConversion(
  journey: Touchpoint[],
  conversionValue: number,
  model: "linear" | "time_decay" | "position_based" = "position_based"
): AttributionResult[] {
  if (journey.length === 0) return [];
  if (journey.length === 1) {
    return [{ touchpoint: journey[0], credit: conversionValue }];
  }
 
  switch (model) {
    case "linear":
      const equalCredit = conversionValue / journey.length;
      return journey.map(tp => ({ touchpoint: tp, credit: equalCredit }));
 
    case "time_decay": {
      const halfLife = 7 * 24 * 60 * 60 * 1000; // 7 days
      const conversionTime = journey[journey.length - 1].timestamp.getTime();
      const weights = journey.map(tp => {
        const age = conversionTime - tp.timestamp.getTime();
        return Math.pow(0.5, age / halfLife);
      });
      const totalWeight = weights.reduce((a, b) => a + b, 0);
      return journey.map((tp, i) => ({
        touchpoint: tp,
        credit: (weights[i] / totalWeight) * conversionValue,
      }));
    }
 
    case "position_based": {
      // 40% first, 40% last, 20% distributed among middle
      const credits = journey.map((_, i) => {
        if (i === 0) return 0.4;
        if (i === journey.length - 1) return 0.4;
        return 0.2 / (journey.length - 2);
      });
      return journey.map((tp, i) => ({
        touchpoint: tp,
        credit: credits[i] * conversionValue,
      }));
    }
  }
}

The Attribution Dashboard

Channel Performance (Position-Based Attribution):

| Channel        | Last-Click Rev | Multi-Touch Rev | Difference |
|----------------|---------------|-----------------|------------|
| Google Brand   | $300K (60%)   | $150K (30%)     | -50%       |
| Facebook Ads   | $25K (5%)     | $125K (25%)     | +400%      |
| Email          | $100K (20%)   | $80K (16%)      | -20%       |
| Google Non-Brand| $50K (10%)   | $75K (15%)      | +50%       |
| Direct         | $25K (5%)     | $70K (14%)      | +180%      |

Insight: Facebook is 5x more valuable than last-click suggests.
Cutting Facebook spend would reduce Google Brand conversions
because Facebook drives the awareness that creates brand searches.

Privacy-Compliant Attribution

With third-party cookies dying, attribution needs to adapt:

First-party data strategies:
  → Server-side tracking (events sent from your server, not browser)
  → First-party cookies (your domain, longer lifespan)
  → Authenticated user matching (email-based cross-device)
  → Media Mix Modeling (statistical, no user-level tracking needed)

Implementation:
  → Move tracking server-side (Meta CAPI, Google Enhanced Conversions)
  → Encourage account creation earlier in the funnel
  → Use post-purchase surveys: "How did you hear about us?"
  → Supplement with MMM for channel-level budget allocation

Multi-touch attribution isn't just a reporting change — it's a budget allocation change. When you see the real contribution of each channel, you spend differently. And when you spend differently based on better data, you grow faster.

Previous
Incident Management That Actually Works — From Alert to Post-Mortem
Insights
Attribution Modeling Beyond Last-Click — What DTC Brands Actually NeedA/B Testing: Server-Side vs. Client-Side — The Technical Trade-offsThe GA4 Data Layer Implementation That E-Commerce Brands Actually NeedYour A/B Test Isn't Statistically Significant — Here's What to Do About ItServer-Side Tracking in a Cookieless World — The Implementation GuideYour Analytics Are Double-Counting Revenue — And Nobody NoticedA/B Testing Is Lying to You — Statistical Significance Isn't EnoughServer-Side Split Testing: Why Client-Side Tools Are Costing You RevenueThe Tracking Stack That Survives iOS, Ad Blockers, and Cookie DeathHow to Run Pricing Experiments Without Destroying TrustYour Conversion Rate Is a Vanity Metric — Here's What to Track InsteadBuilding a Feature Flag System That Doesn't Become Technical DebtThe Data Layer Architecture That Makes Every Test Trustworthy

Ready to Ship?

Let's talk about your engineering challenges and how we can help.

Book a Call