• analytics
  • D2C E-Commerce & Retail

Multi-touch attribution across twelve ad channels

A Markov-chain attribution model reading server-side events from every ad platform a D2C brand ran, so budget could follow marginal contribution instead of last click.

  • AI & Machine Learning
  • Data Engineering
  • Full-Stack Development

At a glance

Sector
D2C E-Commerce & Retail
Type
analytics
Services
AI & Machine Learning · Data Engineering · Full-Stack Development

The problem

What it had to solve

A growing D2C skincare brand was buying across a dozen paid channels, and every channel graded its own homework. Each platform reported conversions on last click, so the same sale was claimed several times over and the sum of the dashboards bore no relation to the bank account. Reallocating budget meant a weekly manual stitch of CSV exports, and the decision at the end of it was still a judgement call.

Platform-native tracking has been degraded by ATT, cookie deprecation and consent rules, which pushed the honest measurement problem back onto the advertiser. What the client needed was a first-party model that survives signal loss and answers a narrower question than the dashboards do: not which channel gets the credit, but what happens to revenue if the next pound moves.

What we built

How it works

Three layers. Ingestion pulls raw events from each ad platform's API alongside server-side tracking from the brand's Shopify store into a ClickHouse warehouse. On top of that, a Markov-chain attribution model assigns conversion credit by a touchpoint's contribution to the sequence rather than its position in it. A React dashboard renders the result as cohort performance and reallocation suggestions.

The platform was built as a decision layer over the team's existing tools rather than a replacement for them. Marketers keep buying in Meta Ads Manager and Google Ads; what changes is that each morning they see which campaigns are over-funded and which creatives are losing efficiency. Ripping out working tools is how analytics projects die in adoption, and the measurement problem never required owning the execution.

Calls we would still defend

  • Server-side, first-party trackingevents are recorded on our side of the transaction rather than in the browser, which is what keeps the dataset intact through ATT and third-party cookie blocking. It is also the only version of the data that is ours to model.

  • Markov chains over Shapley valuesShapley is the more principled allocation, and it is slower and harder to explain. Markov chains answer the removal question directly — what happens to conversions if this channel disappears — which is both faster on a large event table and legible to the marketer who has to act on it.

  • ClickHouse over Snowflakethe workload is heavy aggregation at low concurrency, which is what a column store is for. The cost difference at this volume was large enough to change what the client could afford to retrain.

Built with

  • Python

    Powers the ML attribution engine, ETL orchestration, and platform API connectors — ideal for data-heavy analytical workloads.

  • TensorFlow

    Runs the attribution model and the budget optimisation, with nightly retraining on a trailing window.

  • ClickHouse

    Column-oriented, which suits an event table read by heavy aggregate queries and written to constantly. Materially cheaper than a general-purpose warehouse at this shape of workload.

  • Apache Airflow

    Orchestrates the platform API pulls and the Shopify feed, with retries and SLA monitoring — ad platform APIs fail often enough that the retry logic is not optional.

  • React + Recharts

    Interactive marketer-facing dashboard with drill-down cohort analysis and ML recommendation panels.

  • PostgreSQL

    Stores user configurations, historical model outputs, and dashboard state — complements ClickHouse for operational data.

Where it landed

What the build changed

Markov chain

Attribution model

credit assigned across the whole path, not the last click

Server-side

Event capture

first-party, recorded on our side of the transaction to survive browser signal loss

Nightly

Model retraining

so allocation reflects the latest conversion paths each morning

FAQ

The questions this build raises

What was actually built, the constraints it had to meet, and how it holds up in use.

  • It spreads conversion credit across the touchpoints a customer actually passed through, instead of handing all of it to the last click. The Markov-chain model works by removal: it simulates the path with a given channel taken out and measures what happens to conversions. That number is the channel's contribution, and it is usually a long way from what the channel's own dashboard reports.

  • Yes — Meta, Google Ads, TikTok, Snapchat, Pinterest, LinkedIn, Shopify, Klaviyo, HubSpot, Segment and GA4 through their official APIs, with token refresh and rate-limit handling. A proprietary CRM or an internal warehouse needs a connector written for it, which is scoping work rather than configuration.

  • By hold-out experiment. You turn a channel off in a region or a cohort, watch what happens to conversions, and compare that against what the model predicted would happen. That is the only test that settles an attribution argument, because it measures the counterfactual directly rather than modelling it. Any attribution model that is not periodically checked this way is a hypothesis.

  • Nightly, on a trailing ninety-day window, with hyperparameter tuning weekly. The cadence is a trade-off: responsive enough to pick up new creative and seasonal shifts, stable enough that the recommendations are not rewritten by one noisy day.

  • A different allocation, not automatically a better return. What the model reliably changes is where the credit sits — usually away from channels that close sales other channels started, like branded search. Whether moving budget accordingly earns more depends on your own numbers, which is what the hold-out tests above are for. Anyone quoting you a multiple before seeing your data is quoting you an average of somebody else's.

Keep looking

Want to start a project?