Pure last-click attribution credits 100% of every conversion to whichever email the contact happened to click immediately before purchasing, ignoring the nurture sequence, re-engagement email, and product-update announcement that actually moved them down the funnel. Finance then starves the campaigns doing the real work, marketing kills the sequences that compound over weeks, and the attribution table cannot answer basic retrospective questions like "which first-touch emails drive the highest LTV cohorts." Full touchpoint storage is the prerequisite for every other model.
High because last-click-only attribution systematically misdirects marketing budget away from the sequences that actually drive conversions.
Record every email interaction into an attribution_touchpoints table keyed on (contact_id, campaign_id, event_type, occurred_at), then on conversion run an attribution query over a 30-day window that distributes credit across all touchpoints using a linear, time-decay, or position-based model. Store the credit distribution in a conversion_credits join table so reports can switch models without re-running ETL.
const creditPerTouchpoint = 1.0 / touchpoints.length
ID: campaign-analytics-attribution.attribution-conversion.multi-touch-attribution
Severity: high
What to look for: Examine how conversions are attributed to campaigns. Look for attribution logic that assigns credit to email campaigns that contributed to a conversion. The anti-pattern is pure last-click attribution — crediting 100% of a conversion to the final email a contact interacted with before converting, ignoring all prior touchpoints. Look for evidence of multi-touch models: linear attribution (equal credit to all touchpoints), time-decay (more credit to recent touchpoints), position-based (40/20/40 to first/middle/last), or data-driven models. Check whether attribution records store all contributing campaign interactions, not just the last one.
Pass criteria: Attribution logic stores multiple campaign touchpoints per conversion. Credit is distributed across contributing campaigns using a defined model (linear, time-decay, position-based, or custom). The last-click is not the only attribution data point stored. Quote the actual model name or credit distribution logic found in the codebase. Count all attribution queries and verify each distributes credit across at least 2 touchpoints.
Fail criteria: Attribution records only the last email interaction before conversion. All credit is assigned to the final campaign. Prior touchpoints are not stored or weighted. Storing multiple touchpoints but always assigning 100% credit to the last one does not count as pass.
Skip (N/A) when: The project does not track conversions or does not attribute conversions to email campaigns.
Detail on fail: Example: "Attribution table has a single campaign_id column per conversion — only one campaign receives credit, all others ignored" or "Attribution logic takes the most recent click event and assigns 100% credit"
Remediation: Store the full touchpoint history and distribute credit:
// Store all touchpoints
async function recordTouchpoint(contactId: string, campaignId: string, eventType: 'click' | 'open') {
await db.attributionTouchpoints.create({
data: {
contact_id: contactId,
campaign_id: campaignId,
event_type: eventType,
occurred_at: new Date()
}
})
}
// On conversion, distribute credit across touchpoints (linear model)
async function attributeConversion(contactId: string, conversionId: string, attributionWindowDays = 30) {
const windowStart = new Date(Date.now() - attributionWindowDays * 86400 * 1000)
const touchpoints = await db.attributionTouchpoints.findMany({
where: {
contact_id: contactId,
occurred_at: { gte: windowStart }
},
orderBy: { occurred_at: 'asc' }
})
const creditPerTouchpoint = touchpoints.length > 0 ? 1.0 / touchpoints.length : 0
await db.conversionCredits.createMany({
data: touchpoints.map(tp => ({
conversion_id: conversionId,
campaign_id: tp.campaign_id,
touchpoint_id: tp.id,
credit: creditPerTouchpoint,
model: 'linear'
}))
})
}
For a deeper analysis of engagement scoring across the contact journey, the Campaign Orchestration & Sequencing Audit covers this in detail.