Skip to main content

Attribution

What this is​

This page shows how different touchpoints receive credit for conversions. Use it to compare attribution models, review channel roles, see common conversion paths, and inspect recent converting touches.

Common questions​

Why do the numbers change when I choose another model?​

Each model assigns credit differently to the same conversion journeys. The underlying journeys do not change when you switch models.

Which attribution models can I compare?​

The page provides six models, including First Click, Last Click, Linear, and Time Decay.

What does assisted mean?​

Assisted refers to touchpoints that helped a customer reach conversion without being the first or last touch.

Can I change the time window?​

Yes. Choose a different days window to reload the attribution results.

What is included in the recent touches table?​

It lists recent conversion-carrying touches with their date, customer, order, platform, campaign, touch position, and conversion value.

How to use it​

  1. Select the days window you want to review.
  2. Choose an attribution model to compare how credit is assigned.
  3. Review the summary cards and channel-level breakdown.
  4. Check common conversion paths to understand the journeys customers take.
  5. Use the recent touches table when you need order-level attribution evidence.

Screenshot​

Attribution

For developers — routes, guards, and internals
  • Route: /marketing/attribution
  • Guard: marketing surface (anonymous demo reachable; nav under Reporting). Tenant scope enforced server-side by /api/marketing/attribution.

Captured: 30,019 total attributions, $3,536,072.13 attributed revenue, 30,019 unique customers, 337 campaigns.

Features

6-model attribution comparison (model RadioGroup). First Click, Last Click, Linear, Time Decay, and the other credit models render as selectable cards/radio options; all six are computed by the credit engine (src/lib/marketing/attribution.ts) over ONE shared journey fetch — genuine multi-touch, no single-touch collapse, because the sync writes per-touch rows carrying touch_position + is_conversion + conversion_value (verified in the page header comment and types).

Summary StatCards. Total attributions, attributed revenue, unique customers, and campaign count for the window. Money is dollar decimals.

Channel-level first-touch / last-touch / assisted rollup. Per-platform rollup of touch positions off the real touch_position column (ChannelAttribution interface: first_touch, last_touch, assisted, total_touches).

Common conversion paths. Aggregated touchpoint sequences (e.g. facebook → google → conversion) ranked by frequency, from the same journey data.

Recent converting touches table. The latest conversion-carrying touches with timestamp, customer, order, platform, campaign, touch position, and conversion value (RecentConversion interface).

Days-window selector. Adjusts the journey window; the page re-fetches /api/marketing/attribution?full=1.

Data flow & tenancy

  1. The page fetches /api/marketing/attribution?full=1 on load and whenever the model or days window changes; only the credit assignment differs per model — the journey fetch is shared, so switching models is cheap.
  2. The route runs under the marketing auth + resolveQueryScope() chokepoint like every other marketing API; attribution rows are tenant-filtered in ClickHouse for non-super-admins.
  3. The underlying ledger is written by the marketing sync: per-touch rows with touch_position, is_conversion, and conversion_value (dollar decimals). Because touches are stored individually, "assisted" counts and multi-touch credit are real, not reconstructed from single-touch aggregates.
  4. Manual corrections enter through the attribution_overrides layer (migrations 054/055) and are honored by the engine before credit assignment.

Capture notes

Unique customers equals total attributions (30,019) in the capture. UNVERIFIED: the likely reading is one converting journey per customer in this window (the two figures would diverge with repeat purchasers), but the exact dedup rule was not traced in attribution.ts.

Related docs & migrations

Cross-reference the attribution architecture doc in docs/ and Supabase migrations 054/055 (attribution_overrides), which introduce the manual override layer that feeds this engine. The per-touch ledger this page reads is populated by the marketing sync (src/inngest/ sync jobs).

Key files

KindPath
Route/componentsrc/app/marketing/attribution/page.tsx (client component)
Enginesrc/lib/marketing/attribution.ts, types in src/lib/marketing/types.ts (AttributionModel, AttributionResult)
API/api/marketing/attribution (?full=1)
MigrationsSupabase 054/055 (attribution_overrides)