
Marketing Attribution Dashboard
- 62 installs
- 41 repo stars
- Updated March 13, 2026
- finsilabs/awesome-ecommerce-skills
Build multi-touch attribution dashboards that track revenue by channel, campaign, and creative with blended ROAS and budget-allocation recommendations.
About
Covers multi-touch attribution modeling and dashboards that break revenue down by channel, campaign, and creative with blended ROAS. A marketer or developer uses it to see which marketing actually drives revenue and where to reallocate budget.
- Multi-touch attribution across channels, campaigns, and creatives
- Blended ROAS analysis and budget-allocation recommendations
Marketing Attribution Dashboard by the numbers
- 62 all-time installs (skills.sh)
- Ranked #894 of 2,064 Data Science & ML skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/finsilabs/awesome-ecommerce-skills --skill marketing-attribution-dashboardAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 62 |
|---|---|
| repo stars | ★ 41 |
| Last updated | March 13, 2026 |
| Repository | finsilabs/awesome-ecommerce-skills ↗ |
What it does
Build multi-touch attribution dashboards that track revenue by channel, campaign, and creative with blended ROAS and budget-allocation recommendations.
Files
Marketing Attribution Dashboard
Overview
Every ecommerce business faces attribution chaos: Meta says it drove 300 purchases, Google says 280, and your order management system shows 400 total. The overlap is real, the methodologies differ, and single-touch last-click models misrepresent the true value of upper-funnel channels. A proper attribution dashboard aggregates all marketing data in one place and applies a consistent model so you can make defensible budget decisions. For most merchants, a third-party attribution tool — not custom code — is the fastest path to accurate channel ROI.
When to Use This Skill
- When ad spend decisions rely on platform-reported ROAS rather than actual order data
- When you suspect paid social is stealing attribution from organic search or email
- When preparing a quarterly marketing budget review and need a defensible data source
- When launching a new channel and need to measure its true incremental contribution
- When reporting marketing performance to investors or a board
Core Instructions
Step 1: Choose the right attribution tool
| Platform | Best For | Shopify | WooCommerce | BigCommerce | Price |
|---|---|---|---|---|---|
| Triple Whale | Shopify-native, real-time pixel + CAPI | App Store | Limited | Limited | $129+/mo |
| Northbeam | Advanced multi-touch, TV/podcast spend | App Store | Via pixel | Via pixel | $500+/mo |
| Rockerbox | Mid-market, all channels including offline | App Store | Via pixel | Via pixel | $500+/mo |
| GA4 (free) | All platforms, good enough for most | Via tag | Via tag | Via tag | Free |
| Wicked Reports | WooCommerce-native, integrates with Klaviyo | Limited | Plugin | Limited | $299+/mo |
Start with GA4 for most stores — it is free, handles multi-channel attribution with configurable models, and integrates natively with Google Ads. Upgrade to Triple Whale or Northbeam when you need real-time pixel data that survives iOS 14 restrictions.
Step 2: Set up attribution reporting
---
Shopify
With GA4 (recommended starting point): 1. Go to Shopify Admin → Online Store → Preferences → Google Analytics 2. Enter your GA4 Measurement ID (G-XXXXXXXXXX) 3. Enable Enhanced Ecommerce under GA4 property settings 4. In GA4 → Reports → Acquisition → Traffic Acquisition: set secondary dimension to Session campaign to break down by campaign 5. In GA4 → Reports → Acquisition → Traffic Acquisition: set the comparison period to 30 days and filter by Session medium to isolate paid, organic, email, and social
With Triple Whale (Shopify-native, recommended for paid social): 1. Install Triple Whale from the Shopify App Store 2. Go to Triple Whale → Pixel Setup and add the Triple Whale pixel to your theme — this is a first-party pixel not blocked by iOS 14 3. Connect your ad accounts: Triple Whale → Integrations → connect Meta, Google Ads, and TikTok 4. Triple Whale's Summary Page shows a unified ROAS view across all ad platforms using your actual Shopify order data as the source of truth 5. Use Triple Whale → Attribution → Model Comparison to compare last-click, first-click, and linear attribution in a single view
---
WooCommerce
With GA4: 1. Install MonsterInsights (free tier) or Google Site Kit from the WordPress plugin directory 2. Go to MonsterInsights → Settings → Analytics and connect your GA4 property 3. Enable enhanced ecommerce tracking — MonsterInsights handles the data layer events automatically 4. For UTM attribution reports: go to GA4 → Reports → Acquisition → Traffic Acquisition and filter by source/medium
With Wicked Reports (WooCommerce-specific): 1. Install Wicked Reports from the WordPress plugin directory 2. Connect your WooCommerce store and email platform (Klaviyo, Mailchimp) 3. Wicked Reports assigns multi-touch attribution to each order by tracking the full customer journey from first ad click to purchase 4. Go to Wicked Reports → ROI to see revenue per ad campaign using your actual order data
---
BigCommerce
1. Go to BigCommerce Admin → Settings → Analytics → Google Analytics 2. Enter your GA4 Measurement ID and enable Enhanced Ecommerce 3. For advanced attribution: install Triple Whale or Northbeam from the BigCommerce App Marketplace — both support BigCommerce via tracking pixel 4. In BigCommerce Analytics, go to Analytics → Marketing to see channel-level attribution based on BigCommerce's built-in last-click model
---
Custom / Headless
For headless stores, capture first-touch attribution server-side before building any reporting layer. Third-party tools like Triple Whale, Northbeam, or Rockerbox all provide a JavaScript pixel + server-side API for custom stacks.
If you need to build custom multi-touch attribution:
interface TouchpointEvent {
sessionId: string;
customerId?: string;
anonymousId: string;
channel: string; // 'paid-social' | 'paid-search' | 'organic' | 'email' | 'sms' | 'direct'
source: string; // 'meta' | 'google' | 'tiktok' | 'klaviyo'
medium: string;
campaign?: string;
orderId?: string;
orderValue?: number;
timestamp: Date;
}
function classifyChannel(utm: UtmParams, referrer?: string): string {
if (utm.medium === 'cpc' || utm.medium === 'paid') return 'paid-search';
if (utm.medium === 'paid-social' || utm.source?.match(/meta|facebook|instagram|tiktok/i)) return 'paid-social';
if (utm.medium === 'email') return 'email';
if (utm.medium === 'sms') return 'sms';
if (referrer?.match(/google|bing|yahoo/i)) return 'organic-search';
if (referrer && !referrer.includes(process.env.STORE_DOMAIN!)) return 'referral';
return 'direct';
}
// Attribution model: 40% first touch, 40% last touch, 20% distributed across middle
function positionBasedWeights(touchpoints: TouchpointEvent[]): number[] {
const n = touchpoints.length;
if (n === 1) return [1];
if (n === 2) return [0.5, 0.5];
const middleShare = 0.2 / (n - 2);
return touchpoints.map((_, i) => {
if (i === 0) return 0.4;
if (i === n - 1) return 0.4;
return middleShare;
});
}Use a dedicated attribution API service like Rockerbox or Northbeam rather than building the full attribution engine from scratch — the complexity of cross-device stitching and model computation is not worth custom-building for most stores.
Step 3: Configure UTM tracking consistently
UTM parameters are the foundation of any attribution system. Without consistent UTMs, even GA4 cannot attribute sessions correctly.
Standard UTM structure:
| Channel | utm_source | utm_medium | utm_campaign |
|---|---|---|---|
| Meta Ads | facebook or instagram | paid-social | spring-2026-prospecting |
| Google Ads | google | cpc | brand-search |
| TikTok Ads | tiktok | paid-social | ugc-spring-2026 |
| Klaviyo email | klaviyo | email | welcome-series |
| SMS | postscript | sms | abandoned-cart |
Use Google's Campaign URL Builder at ga-dev-tools.google.com/campaign-url-builder to generate UTM links consistently.
In Klaviyo: Go to Klaviyo → Account → Settings → UTM Tracking and enable auto-UTM tagging — Klaviyo appends UTM parameters to all email links automatically.
In Meta Ads: Go to Ads Manager → Campaign → URL Parameters and add utm_source={{site_source_name}}&utm_medium=paid-social&utm_campaign={{campaign.name}}&utm_content={{ad.name}} to your ad URL parameters at the campaign level.
Step 4: Build a blended ROAS dashboard in GA4
1. Go to GA4 → Explore → Free Form and create a new exploration 2. Set Dimensions: Session source/medium, Session campaign 3. Set Metrics: Sessions, Ecommerce purchases, Purchase revenue, Transactions 4. Add a filter: Session medium contains paid to isolate paid channels 5. Use GA4 → Advertising → Attribution to compare last-click vs. data-driven attribution models side by side 6. For manual ROAS: export GA4 revenue by channel → enter ad spend from each platform → calculate Revenue / Spend in a spreadsheet
Automate with Google Looker Studio (free): 1. Connect Looker Studio to GA4, Google Ads, and your Shopify/WooCommerce data source 2. Build a blended channel dashboard using Looker Studio's Blend Data feature 3. Join GA4 session data with Google Ads spend by campaign name
Step 5: Measure attribution health
| Metric | Target | Where to Find |
|---|---|---|
| Channel coverage (% of orders with UTM attribution) | > 70% | GA4 Acquisition report |
| Email channel share of attributed revenue | Varies by business | GA4 filter by utm_medium=email |
| Paid social EMQ score (for Meta CAPI) | 7+ / 10 | Meta Events Manager |
| Cross-channel ROAS discrepancy | < 30% variance vs. platform-reported | Compare GA4 vs. Ads Manager |
Best Practices
- GA4 is ground truth, not platform dashboards — every ad platform over-attributes to itself; your order database and GA4 are the closest thing to ground truth
- Use position-based attribution for budget decisions — it rewards both discovery (first-touch) and closing (last-touch) channels; linear attribution underweights email and organic
- Set a 30-day lookback minimum — customers research for weeks before buying; 7-day windows miss upper-funnel contributions from paid social
- Consistent UTMs are more important than the tool — even the best attribution tool fails with inconsistent or missing UTM parameters; audit UTM coverage before buying a tool
- Compare models, not just the default — looking at the same period under last-click vs. linear vs. position-based reveals which channels are being systematically over- or under-credited
Common Pitfalls
| Problem | Solution |
|---|---|
| Direct traffic massively over-attributed | Set a 30-min session timeout in GA4; check for missing UTM parameters on all ad campaigns |
| Email channel under-attributed | Enable auto-UTM in Klaviyo; test all links to verify UTMs survive ESP link tracking |
| Platform ROAS looks great but profitability is flat | Platforms count view-through attribution; compare attribution windows — use 7-day click only |
| Dashboard loads slowly | Use pre-aggregated GA4 summary tables; avoid building attribution from raw event exports |
Related Skills
- @meta-ads-integration
- @google-ads-ecommerce
- @tiktok-ads-integration
- @affiliate-program
- @influencer-marketplace-integration
{
"context": "Tests whether the agent implements the attribution model engine with the correct set of models, the specified time-decay half-life, the position-based weight distribution, and ensures weights always sum to 1.0. Also checks that position-based is the default model and edge cases (n=1, n=2) are handled correctly.",
"type": "weighted_checklist",
"checklist": [
{
"name": "All five models present",
"max_score": 10,
"description": "Implementation supports all five model types: last-touch, first-touch, linear, time-decay, and position-based (code contains all five identifiers/cases)"
},
{
"name": "Time-decay half-life value",
"max_score": 10,
"description": "Time-decay model uses a half-life of exactly 7 days (code contains 7 * 24 * 60 * 60 * 1000 or equivalent 604800000 milliseconds)"
},
{
"name": "Time-decay formula",
"max_score": 8,
"description": "Time-decay weights are computed using Math.pow(0.5, ageMs / halfLifeMs) or an equivalent exponential decay formula with base 0.5"
},
{
"name": "Position-based 40/40/20 split",
"max_score": 10,
"description": "Position-based model assigns 0.4 to the first touchpoint, 0.4 to the last touchpoint, and splits the remaining 0.2 equally among middle touchpoints"
},
{
"name": "Position-based n=1 edge case",
"max_score": 6,
"description": "Position-based model returns [1] when there is only a single touchpoint"
},
{
"name": "Position-based n=2 edge case",
"max_score": 6,
"description": "Position-based model returns [0.5, 0.5] when there are exactly two touchpoints"
},
{
"name": "Default model is position-based",
"max_score": 10,
"description": "The default attribution model value, wherever a default is set (function parameter default, useState default, or exported constant), is 'position-based' — NOT 'linear' or 'last-touch'"
},
{
"name": "Weights sum to 1.0",
"max_score": 10,
"description": "demo-output.txt contains evidence (console output or assertion) that weights sum to 1.0 (within floating-point tolerance) for all model/order combinations tested"
},
{
"name": "AttributedConversion structure",
"max_score": 10,
"description": "attributeConversion output includes creditFraction and creditAmount per touchpoint (both fields present in returned credits array)"
},
{
"name": "Zero-touchpoint guard",
"max_score": 6,
"description": "attributeConversion handles the case of zero touchpoints (returns an empty credits array rather than throwing)"
},
{
"name": "Demo output file exists",
"max_score": 8,
"description": "A file named demo-output.txt is present and contains weight/credit output for multiple model combinations"
},
{
"name": "Linear model correctness",
"max_score": 6,
"description": "Linear model assigns equal weight 1/n to every touchpoint (code contains 1 / n or equivalent)"
}
]
}
Attribution Model Engine
Problem/Feature Description
A DTC ecommerce company is launching a marketing analytics platform and needs to move away from relying on last-click attribution. The analytics team has collected multi-channel touchpoint data for thousands of orders but currently has no way to compare how different attribution philosophies would allocate revenue credit across channels like paid search, paid social, and email.
The head of growth wants to evaluate several attribution approaches side by side to understand which channels are being over- or under-valued under their current last-click model. Specifically, they need an engine that can take an order and its associated touchpoint sequence and output how much revenue credit each touchpoint should receive, depending on which model is selected. The engine must handle edge cases (single touchpoint, two touchpoints) and must be defensible to the CFO — meaning the math must be provably correct.
Output Specification
Implement the attribution model engine as a TypeScript module. The module should export:
1. A computeWeights function that, given a list of touchpoints and a model name, returns an array of fractional weights (one per touchpoint). 2. An attributeConversion function that takes an order (with id and total), a list of touchpoints, and a model name, and returns an AttributedConversion object containing per-touchpoint credit amounts and fractions.
Write the implementation to attribution-engine.ts.
Also write a demo.ts script that:
- Creates 3 sample orders with different touchpoint sequences (1 touchpoint, 2 touchpoints, 4+ touchpoints)
- Runs each order through all supported attribution models
- Prints the resulting weight arrays and credit amounts to the console in a readable format
- Verifies that weights sum to 1.0 (within floating-point tolerance) for each model/order combination and prints a summary of which checks passed
Run the demo with npx ts-node demo.ts and save the console output to demo-output.txt.
{
"context": "Tests whether the agent builds the React attribution dashboard with the correct data-fetching setup, KPI cards, visualizations, model comparison, and correctly documents multi-touch revenue discrepancy handling, model explainers, and channel segmentation approach.",
"type": "weighted_checklist",
"checklist": [
{
"name": "SWR for data fetching",
"max_score": 8,
"description": "AttributionDashboard.tsx uses useSWR (from the 'swr' package) for data fetching rather than useEffect/fetch or another library"
},
{
"name": "SWR refresh interval",
"max_score": 8,
"description": "SWR is configured with refreshInterval of 3600000 (1 hour in milliseconds)"
},
{
"name": "Default model is position-based",
"max_score": 8,
"description": "The default selected attribution model is 'position-based' (useState default or equivalent initialisation)"
},
{
"name": "Default period is 30d",
"max_score": 6,
"description": "The default selected time period is '30d' rather than '7d' or another value"
},
{
"name": "Four KPI cards",
"max_score": 10,
"description": "Dashboard includes all four KPI summary cards: Total Attributed Revenue, Total Ad Spend, Blended ROAS, and New Customer CAC"
},
{
"name": "Customer journey visualization",
"max_score": 8,
"description": "Dashboard includes a Sankey chart or flow/path visualization component for common customer journey sequences"
},
{
"name": "Model comparison section",
"max_score": 8,
"description": "Dashboard includes a section (component or UI element) that shows how different attribution models compare for the same orders"
},
{
"name": "Model explainers in UI",
"max_score": 8,
"description": "dashboard-spec.md includes plain-language explainer text for at least 3 attribution models (describing what each model does for a non-technical audience)"
},
{
"name": "Multi-touch revenue disclosure",
"max_score": 8,
"description": "dashboard-spec.md explicitly documents that per-channel attributed revenue sum can exceed actual total revenue and that both figures are shown in the UI"
},
{
"name": "Channel + source segmentation",
"max_score": 8,
"description": "dashboard-spec.md or AttributionDashboard.tsx indicates that channel metrics are broken down by both channel AND source (not channel alone)"
},
{
"name": "New vs returning revenue split",
"max_score": 6,
"description": "Channel metrics include separate fields or display for new customer revenue and returning customer revenue"
},
{
"name": "Journey path limit",
"max_score": 6,
"description": "dashboard-spec.md states that the journey path query limit is 20 (top 20 paths by revenue)"
},
{
"name": "Nightly precompute strategy",
"max_score": 8,
"description": "dashboard-spec.md mentions that aggregated metrics are precomputed nightly (or on a scheduled basis) into a summary table rather than queried directly from raw events"
}
]
}
Marketing Attribution Dashboard UI
Problem/Feature Description
A growth analytics team at an ecommerce company has finished building the backend attribution engine and now needs a React front-end dashboard that their marketing managers can use daily. The existing dashboard only shows last-click data pulled directly from Google Analytics. Managers are frustrated because the numbers don't reconcile with actual orders, and they have no way to see how changing the attribution model affects channel rankings.
The new dashboard needs to present aggregated marketing performance data fetched from an internal API endpoint (/api/attribution/metrics). It should allow users to switch between attribution models and time periods on the fly, display key performance indicators prominently, visualize the most common multi-channel customer journeys, and let stakeholders see a side-by-side comparison of how different attribution models credit the same orders. Critically, the data team has warned that the sum of per-channel attributed revenue will often exceed the actual total order revenue when using multi-touch models — the UI must make this clear rather than confusing users. The team also needs the dashboard to auto-refresh without requiring a page reload, since the underlying data updates throughout the day.
Output Specification
Build the React dashboard as a TypeScript/TSX component. Write the implementation to AttributionDashboard.tsx.
The component should include: 1. Controls to select the attribution model and the time period. 2. Summary KPI cards section. 3. A channel performance table. 4. A section visualizing common customer journey paths. 5. A section for comparing attribution models.
Also write a dashboard-spec.md file that describes:
- The data fetching strategy used (library, refresh behavior)
- How the dashboard handles the discrepancy between per-channel attributed revenue and actual total revenue
- The default attribution model and default time period and the rationale for each choice
- How each attribution model is explained to non-technical users in the UI (include the actual copy/text for at least 3 model explainers)
- How channel metrics are segmented (what breakdown dimensions are used)
- The journey path query limit and why it was chosen
{
"context": "Tests whether the agent implements server-side touchpoint collection with the correct session/cookie logic, the specified channel classification rules, anonymous ID persistence, and post-login identity stitching.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Server-side middleware",
"max_score": 6,
"description": "trackTouchpoint is implemented as an Express middleware (accepts req, res, next) rather than as client-side JavaScript"
},
{
"name": "Conditional touchpoint creation",
"max_score": 10,
"description": "Touchpoint record is only created when either a UTM source is present OR the session has not been tracked yet (not on every request)"
},
{
"name": "Session cookie maxAge",
"max_score": 8,
"description": "The session-tracking cookie (_session_tracked or equivalent) is set with maxAge of 1800 seconds (30 minutes)"
},
{
"name": "Anonymous ID persistence",
"max_score": 8,
"description": "Anonymous visitor ID cookie is set with a 1-year maxAge (365 days / 31536000 seconds / equivalent) rather than a session-only cookie"
},
{
"name": "paid-search classification",
"max_score": 8,
"description": "Visits with utm_medium of 'cpc' or 'paid' are classified as 'paid-search'"
},
{
"name": "paid-social classification",
"max_score": 8,
"description": "Visits with utm_medium of 'paid-social' OR a source matching meta, facebook, instagram, or tiktok are classified as 'paid-social'"
},
{
"name": "organic-search classification",
"max_score": 8,
"description": "Visits where the HTTP referrer matches google, bing, or yahoo (and no paid UTM medium) are classified as 'organic-search'"
},
{
"name": "direct classification",
"max_score": 6,
"description": "Visits with no referrer or a same-domain referrer are classified as 'direct'"
},
{
"name": "email classification",
"max_score": 6,
"description": "Visits with utm_medium of 'email' are classified as 'email'"
},
{
"name": "Post-login identity stitching",
"max_score": 10,
"description": "Code includes a function or logic that links pre-login anonymous touchpoints to a customer ID after the customer is identified (checkout or login event)"
},
{
"name": "TouchpointEvent fields",
"max_score": 8,
"description": "Touchpoint records include anonymousId, channel, source, medium, campaign, landingPage, eventType, deviceType, and isNewVisitor fields"
},
{
"name": "Classification test output",
"max_score": 8,
"description": "classification-test-output.txt exists and shows test results for at least 8 channel classification scenarios"
},
{
"name": "Direct re-attribution logic",
"max_score": 6,
"description": "Code includes logic or comment indicating that direct sessions immediately following an ad click should be re-attributed to the originating channel rather than direct"
}
]
}
Marketing Touchpoint Collection Middleware
Problem/Feature Description
A mid-size apparel brand runs ads on Meta, Google, and TikTok, sends campaigns through Klaviyo, and also gets significant organic search and direct traffic. They have had a persistent headache: their client-side analytics have been silently losing data due to ad blockers, browser privacy restrictions, and iOS tracking changes. The data engineering team estimates that up to 35% of paid sessions are going untracked.
The solution agreed upon is to move all touchpoint collection to the server side. An Express.js API already handles all page requests and checkout events. The team needs a middleware function that intercepts each request and writes a touchpoint record to the database. The middleware must correctly classify what channel drove each visit, handle both new and returning visitors, and ensure session boundaries are respected. A separate concern is identity resolution: once a customer checks out and is identified, all the pre-login anonymous touchpoints in that session should be linked to their customer record so the attribution model has a complete journey.
Output Specification
Implement the server-side touchpoint tracking middleware as a TypeScript module. Write the code to touchpoint-middleware.ts.
The file should include: 1. A trackTouchpoint Express middleware function that records touchpoints to the database. 2. A classifyChannel function that categorises a visit based on UTM parameters and the HTTP referrer. 3. A stitchCustomerTouchpoints function (or equivalent) that links pre-login anonymous touchpoints to a customer ID after login/checkout.
Also write a channel-classification-tests.ts file that tests the classifyChannel function with at least 8 different input scenarios (covering various UTM combinations and referrer values), prints the input and expected vs. actual output for each test case, and exits with a non-zero code if any test fails. Run the tests with npx ts-node channel-classification-tests.ts and save the output to classification-test-output.txt.
You may stub out the actual database calls (e.g. db.touchpoints.create) and cookie/session utilities as needed — focus on the logic.
{
"name": "finsi/marketing-attribution-dashboard",
"version": "0.1.0",
"summary": "Build multi-touch attribution dashboards tracking revenue by channel, campaign, and creative with blended ROAS analysis and budget allocation recommendations",
"skills": {
"marketing-attribution-dashboard": {
"path": "SKILL.md"
}
}
}